3

In an application I've written I have a process that parses a large amount of data from Core-Data and displays it to a graph. While doing this processing I also end up writing the data out to a CSV File. I created a separate class called CSVLine which assists with the creation of the CSV file.
For my test case of 140k recorded my Objective-C code takes aprox 12 seconds to run. After "migrating" the class over to swift It now takes somewhere between 280-360 seconds to run. Obviously I've done something terrible.

Using Instruments I was able to identify the "slow" method and I was wondering if I've done something clear in SWIFT to cause the issue.

Objc

- (void)newLine {
//    NSLog(@"Appending %@", self.csvString);

    [outData appendData:[self csvData] ];
    [self clear];
}

- (void)clear {

    // Erase every single value

    for (NSUInteger i = 0; i < [values count]; i ++) {
        values[i] = @"";
    }
}

Swift

func newLine() {
    outData.appendData(csvData())
    clear()
}


// Clear out the Array
func clear() {
    for (var i = 0; i < values.count; i++) {
        values[i] = ""
    }
}

I'm working with various types of data that are all being written to a single CSV file so there are many blank lines. To accommodate for this I designed this class so that it has an array of keys and an array of values. The keys store the "column" names for the CSV file and values will store either a blank or a value for the index of the key for that data element.

Example:

Keys = [speed,heading,lat,lon]

values might be [200,300,"",""]

or ["","","38.553","25.2256"]

Once I'm done with a line i will write a comma joined list of the values into an internal data structure and clear out the line (erase all the items in the values array). This seems to be where the slowdown is with the swift class. Is there something blatantly "slow" i'm doing when i zero out my array?

Full Swift Class

@objc class CSVLineSwift : NSObject {
    
    // Define Arrays
    var keys: [String] = [String]()
    var values: [String] = [String]()
    
    var outData : NSMutableData = NSMutableData()
    
    override init() {
        
    }
    
    // Singelton Operator - Thread Safe :: http://code.martinrue.com/posts/the-singleton-pattern-in-swift
    class var instance : CSVLineSwift {
    
        // Computed Property
        struct Static {
            static var instance : CSVLineSwift?
            static var token: dispatch_once_t = 0
            }
    
        dispatch_once(&Static.token) {
            Static.instance = CSVLineSwift();
        }
        
        return Static.instance!
    }
    
    // Erase existing Data
    func newFile() {
        outData = NSMutableData();
        outData.appendData(headerData())
    }
    
    func csvString() -> String {
        return ",".join(values)
    }
    
    func csvData() -> NSData {
        let string = csvString()
        let data = string.dataUsingEncoding(NSUTF8StringEncoding)
        return data!
    }
    
    
    func addField(field : String) {
        keys.append(field)
        values.append("")
    }
    
    func setValueForKey(value:String, key:String) {

        if let index = find(keys, key) {
            values[index] = value
        } else {
            print( "ERROR -- There was no key: \(key) in the header Array")
        }
    }
    
    func headerString() -> String {
        return ",".join(keys)
    }
    
    func headerData() -> NSData {
       return headerString().dataUsingEncoding(NSUTF8StringEncoding)!
    }
    
 
    func newLine() {
        outData.appendData(csvData())
        clear()
    }
    
    
    // Clear out the Array
    func clear() {
        for (var i = 0; i < values.count; i++) {
            values[i] = ""
        }
    }
    
    func writeToFile(fileName : String) {
        outData.writeToFile(fileName, atomically: true)
    }
   
}
Community
  • 1
  • 1
Jeef
  • 26,861
  • 21
  • 78
  • 156
  • 4
    Are you sure you're testing with a release build in both cases? With Swift, the difference in performance between debug and release builds can be vast. – rob mayoff Sep 12 '14 at 18:49
  • I think i just discovered that ... cause now its running fine - its the optimization isn't it... – Jeef Sep 12 '14 at 19:07

2 Answers2

5

Be sure that Swift's optimization level in Build Settings is not -Onone. In my experience, it is orders of magnitude slower than -O. (-O is also the default for 'release', so alternatively you could simply build for release, as already suggested.) As for 'zeroing out' the array, it might be faster (although I do not know) to simply re-initialize the array with a repeated value:

values = [String](count: values.count, repeatedValue: "")

Or if you know you will be appending the new values as you go along, and are not bound to using the indices, you could call:

values.removeAll(keepCapacity: true)

And add the new values with values.append() rather than at indices.

Ideasthete
  • 1,553
  • 13
  • 22
2

It appears my issues were due to a debug build. In debug build there is no optimization. Once you do a run build the optimization kicks in and things run MUCH MUCH faster....

Similar to the answer posted here: Is Swift really slow at dealing with numbers?

Community
  • 1
  • 1
Jeef
  • 26,861
  • 21
  • 78
  • 156
  • Zeroing an array with 16,737,732 bytes, in my test case, went from 2.37 seconds (no optimization) to 0.0165 seconds (optimized). THAT'S 143 TIMES FASTER! Btw, took C's `memset` only 0.00827seconds on the same machine -- that' another 2x speedup. – wcochran Oct 04 '17 at 19:31