Quickest way to concatenate strings in Swift 2

I ran the following code in the simulator and on an iPhone6S Plus. The results in both cases showed the string1 + " " + string2 addition faster for the strings I used. I didn't try with different types of strings, optimizations, etc. but you can run the code and check for your particular strings etc. Try running this code online in the IBM Swift Sandbox. The timer struct is from here: Measure elapsed time in Swift

To run the code create a single view application in Xcode and add the following code to the ViewController:

import UIKit
import CoreFoundation

class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let a = "abscdefghi jkl¢€@sads dljlæejktæljæ leijroptjiæa Dog! iojeg r æioej rgæoija"
        let b = a
        timeStringAdding(a, string2: b, times: 1_000_000, repetitions: 5)
    }
            
    struct RunTimer: CustomStringConvertible {
        var begin: CFAbsoluteTime
        var end:CFAbsoluteTime
        
        init() {
            begin = CFAbsoluteTimeGetCurrent()
            end = 0
        }

        mutating func start() {
            begin = CFAbsoluteTimeGetCurrent()
            end = 0
        }

        @discardableResult
        mutating func stop() -> Double {
            if (end == 0) { end = CFAbsoluteTimeGetCurrent() }
            return Double(end - begin)
        }

        var duration: CFAbsoluteTime {
            get {
                if (end == 0) { return CFAbsoluteTimeGetCurrent() - begin }
                else { return end - begin }
            }
        }

        var description: String {
            let time = duration
            if (time > 100) {return " \(time/60) min"}
            else if (time < 1e-6) {return " \(time*1e9) ns"}
            else if (time < 1e-3) {return " \(time*1e6) µs"}
            else if (time < 1) {return " \(time*1000) ms"}
            else {return " \(time) s"}
        }
    }
    
    func timeStringAdding(string1:String, string2:String, times:Int, repetitions:Int) {
        var newString = ""
        var i = 0
        var j = 0
        var timer = RunTimer()
        
        while j < repetitions {
            i = 0
            timer.start()
            while i < times {
                newString = string1 + " " + string2
                i += 1
            }
            print("+ add \(timer)")
            
            i = 0
            timer.start()
            while i < times {
                newString = "\(string1) \(string2)"
                i += 1
            }
            print("\\(  add \(timer)")
            j += 1
        }
    }
}

On an iPhone 6S Plus, it gave:

+   add  727.977991104126 ms
\(  add  1.1197350025177 s

+   add  693.499982357025 ms
\(  add  1.11982899904251 s

+   add  690.113961696625 ms
\(  add  1.12086200714111 s

+   add  707.363963127136 ms
\(  add  1.13451600074768 s

+   add  734.095990657806 ms
\(  add  1.19673496484756 s

And on the simulator (iMac Retina):

+   add  406.143009662628 ms
\(  add  594.823002815247 ms

+   add  366.503953933716 ms
\(  add  595.698952674866 ms

+   add  370.530009269714 ms
\(  add  596.457958221436 ms

+   add  369.667053222656 ms
\(  add  594.724953174591 ms

+   add  369.095981121063 ms
\(  add  595.37798166275 ms

Most of the time is allocation and freeing memory for the string structs and for those really curious run the code in the Instruments panel with Time Profiler usage and see how the time is allocated for alloc and free etc, in relation to the machine code which is shown there also.


This question piqued my curiosity, so I put this into a new project. These are quick and dirty benchmarks and should be taken with the usual grains of salt, but the results were intriguing.

var string1 = "This"
var string2 = "that"
var newString: String

let startTime = NSDate()
for _ in 1...100000000 {
  newString = string1 + " " + string2
}
print("Diff: \(startTime.timeIntervalSinceNow * -1)")

In 6 runs on the simulator running on my MacBook Pro (mid-2014 i7, 2.5GHz), the output to the debug console averaged 1.36 seconds. Deployed as debug code to my iPhone 6S, the average of all the outputs in 6 runs was 1.33 seconds.

Using the same code but changing the line where the strings are concatenated to this...

newString = "\(string1) \(string2)"

...gave me rather different results. In 6 runs on the simulator, the average time reported on the debug console was 50.86 seconds. In 6 runs on the iPhone 6S, the average run time was 88.82 seconds. That's almost 2 orders of magnitude's difference.

These results suggest that if you have to concatenate a large number of strings, you should use the + operator rather than string interpolation.

Tags:

Swift

Swift2