The fastest way to concatenate strings in Swift 2

What is the fastest and most efficient way to concatenate multiple lines in Swift 2?

// Solution 1... let newString:String = string1 + " " + string2 // ... Or Solution 2? let newString:String = "\(string1) \(string2)" 

Or is this the only differentiation, how does it look for a programmer?

+6
source share
5 answers

I ran the following code in the simulator and on the iPhone6S Plus. The results in both cases showed the addition of string1 + " " + string2 faster for the strings I used. I have not tried to use different types of lines, optimizations, etc., but you can run the code and check your specific lines, etc. Try running this code online in the IBM Swift Sandbox . Timer structure from here: Measuring elapsed time in fast

To run the code, create one 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 RunningTimer: CustomStringConvertible { var begin:CFAbsoluteTime var end:CFAbsoluteTime init() { begin = CFAbsoluteTimeGetCurrent() end = 0 } mutating func start() { begin = CFAbsoluteTimeGetCurrent() end = 0 } 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 = RunningTimer.init() 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 the iPhone 6S Plus, he 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 it allocates and frees memory for string structures, and for those who are really curious, it runs the code in the Instruments panel using the Time Profiler and sees how time is allocated for alloc and free, etc., with respect to it, there is also a machine code.

+7
source

This question aroused my curiosity, so I included it in a new project. These are quick and dirty benchmarks and should be taken with regular 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 starts on a simulator running on my MacBook Pro (mid-2014 i7, 2.5 GHz), the output to the debug console was an average of 1.36 seconds. Deployed as a debugging code for my iPhone 6S, the average of all outputs in 6 runs was 1.33 seconds.

Using the same code, but changing the line where the lines are combined with this ...

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

... gave me very different results. In 6 simulator runs, the average time reported on the debug console was 50.86 seconds. In 6 runs on the iPhone 6S, the average runtime was 88.82 seconds. This is almost 2 orders of magnitude difference.

These results show that if you need to concatenate a large number of strings, you should use the + operator instead of string interpolation.

+2
source

The fastest way is implementation dependent. You can compare your specific compiler combination, compiler optimization options, library, framework, OS, and processor / processor. But, most likely, performance may vary in different combinations.

The answer may also differ from whether string1 and string2 are mutable or not, but also depends on the compiler optimization level.

+1
source

Swift 3

Another way to concatenate strings in Swift 3 is to use joined :

 let stringArray = ["string1", "string2"] let newString = stringArray.joined(separator: " ") 

Of course, this requires the rows to be in an array. I did not make any time profile, so I can not compare it with other proposed solutions.

+1
source

TL DR . The difference is noticeable only if you are working with large strings (millions of bytes / characters).

All tests were compiled and performed on the iMac 21.5 Late 2012, 2.7 GHz Intel Core i5 .

I made a small landmark. Here is the code.

interpolation.swift compiled with swiftc ./interpolation.swift -o ./interpolation

 import Swift _ = "\(Process.arguments[1]) \(Process.arguments[2])" 

swiftc output with -emit-assembly flag:

  .section __TEXT,__text,regular,pure_instructions .macosx_version_min 10, 9 .globl _main .align 4, 0x90 _main: .cfi_startproc pushq %rbp Ltmp0: .cfi_def_cfa_offset 16 Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbp Ltmp2: .cfi_def_cfa_register %rbp subq $128, %rsp movq _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token5@GOTPCREL (%rip), %rax movq __TZvOs7Process5_argcVs5Int32@GOTPCREL (%rip), %rcx movl %edi, (%rcx) cmpq $-1, (%rax) movq %rsi, -56(%rbp) je LBB0_2 movq _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token5@GOTPCREL (%rip), %rdi movq _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func5@GOTPCREL (%rip), %rax movq %rax, %rsi callq _swift_once LBB0_2: movl $5, %eax movl %eax, %edi movq __TZvOs7Process11_unsafeArgvGSpGSpVs4Int8__@GOTPCREL (%rip), %rcx movq -56(%rbp), %rdx movq %rdx, (%rcx) callq __TTSg5SS___TFs27_allocateUninitializedArrayurFBwTGSax_Bp_ leaq L___unnamed_1(%rip), %rdi xorl %esi, %esi movl $1, %r8d movq %rdx, -64(%rbp) movl %r8d, %edx movq %rax, -72(%rbp) callq __TFSSCfT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS movq %rax, %rdi movq %rdx, %rsi movq %rcx, %rdx callq __TFSSCfT26stringInterpolationSegmentSS_SS movq -64(%rbp), %rsi movq %rax, (%rsi) movq %rdx, 8(%rsi) movq %rcx, 16(%rsi) callq __TFOs7Processau9argumentsGSaSS_ movq (%rax), %rax movq %rax, %rdi movq %rax, -80(%rbp) callq _swift_bridgeObjectRetain leaq -24(%rbp), %rdi movl $1, %r8d movl %r8d, %esi movq -80(%rbp), %rdx movq %rax, -88(%rbp) callq __TTSg5SS___TFSag9subscriptFSix movq -80(%rbp), %rdi callq _swift_bridgeObjectRelease movq -24(%rbp), %rdi movq -16(%rbp), %rsi movq -8(%rbp), %rdx callq __TFSSCfT26stringInterpolationSegmentSS_SS leaq L___unnamed_2(%rip), %rdi movl $1, %r8d movl %r8d, %esi movl $1, %r8d movq -64(%rbp), %r9 movq %rax, 24(%r9) movq %rdx, 32(%r9) movq %rcx, 40(%r9) movl %r8d, %edx callq __TFSSCfT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS movq %rax, %rdi movq %rdx, %rsi movq %rcx, %rdx callq __TFSSCfT26stringInterpolationSegmentSS_SS movq -64(%rbp), %rsi movq %rax, 48(%rsi) movq %rdx, 56(%rsi) movq %rcx, 64(%rsi) callq __TFOs7Processau9argumentsGSaSS_ movq (%rax), %rax movq %rax, %rdi movq %rax, -96(%rbp) callq _swift_bridgeObjectRetain leaq -48(%rbp), %rdi movl $2, %r8d movl %r8d, %esi movq -96(%rbp), %rdx movq %rax, -104(%rbp) callq __TTSg5SS___TFSag9subscriptFSix movq -96(%rbp), %rdi callq _swift_bridgeObjectRelease movq -48(%rbp), %rdi movq -40(%rbp), %rsi movq -32(%rbp), %rdx callq __TFSSCfT26stringInterpolationSegmentSS_SS leaq L___unnamed_1(%rip), %rdi xorl %r8d, %r8d movl %r8d, %esi movl $1, %r8d movq -64(%rbp), %r9 movq %rax, 72(%r9) movq %rdx, 80(%r9) movq %rcx, 88(%r9) movl %r8d, %edx callq __TFSSCfT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS movq %rax, %rdi movq %rdx, %rsi movq %rcx, %rdx callq __TFSSCfT26stringInterpolationSegmentSS_SS movq -64(%rbp), %rsi movq %rax, 96(%rsi) movq %rdx, 104(%rsi) movq %rcx, 112(%rsi) movq -72(%rbp), %rdi callq __TFSSCft19stringInterpolationGSaSS__SS movq %rcx, %rdi movq %rax, -112(%rbp) movq %rdx, -120(%rbp) callq _swift_unknownRelease xorl %eax, %eax addq $128, %rsp popq %rbp retq .cfi_endproc .section __TEXT,__cstring,cstring_literals L___unnamed_1: .space 1 L___unnamed_2: .asciz " " .linker_option "-lswiftCore" .linker_option "-lobjc" .section __DATA,__objc_imageinfo,regular,no_dead_strip L_OBJC_IMAGE_INFO: .long 0 .long 768 .subsections_via_symbols 

addstr.swift ( + statement) compiled with swiftc ./addstr.swift -o ./addstr

 import Swift _ = Process.arguments[1] + " " + Process.arguments[2] 

swiftc output with -emit-assembly flag:

  .section __TEXT,__text,regular,pure_instructions .macosx_version_min 10, 9 .globl _main .align 4, 0x90 _main: .cfi_startproc pushq %rbp Ltmp0: .cfi_def_cfa_offset 16 Ltmp1: .cfi_offset %rbp, -16 movq %rsp, %rbp Ltmp2: .cfi_def_cfa_register %rbp subq $176, %rsp movq _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token5@GOTPCREL (%rip), %rax movq __TZvOs7Process5_argcVs5Int32@GOTPCREL (%rip), %rcx movl %edi, (%rcx) cmpq $-1, (%rax) movq %rsi, -56(%rbp) je LBB0_2 movq _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token5@GOTPCREL (%rip), %rdi movq _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func5@GOTPCREL (%rip), %rax movq %rax, %rsi callq _swift_once LBB0_2: movq __TZvOs7Process11_unsafeArgvGSpGSpVs4Int8__@GOTPCREL (%rip), %rax movq -56(%rbp), %rcx movq %rcx, (%rax) callq __TFOs7Processau9argumentsGSaSS_ movq (%rax), %rax movq %rax, %rdi movq %rax, -64(%rbp) callq _swift_bridgeObjectRetain leaq -24(%rbp), %rdi movl $1, %edx movl %edx, %esi movq -64(%rbp), %rdx movq %rax, -72(%rbp) callq __TTSg5SS___TFSag9subscriptFSix movq -64(%rbp), %rdi callq _swift_bridgeObjectRelease leaq L___unnamed_1(%rip), %rdi movl $1, %r8d movl %r8d, %esi movl $1, %edx movq -24(%rbp), %rax movq -16(%rbp), %rcx movq -8(%rbp), %r9 movq %r9, -80(%rbp) movq %rcx, -88(%rbp) movq %rax, -96(%rbp) callq __TFSSCfT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS movq -96(%rbp), %rdi movq -88(%rbp), %rsi movq -80(%rbp), %r9 movq %rdx, -104(%rbp) movq %r9, %rdx movq %rcx, -112(%rbp) movq %rax, %rcx movq -104(%rbp), %r8 movq -112(%rbp), %r9 callq __TZFsoi1pFTSSSS_SS movq %rax, -120(%rbp) movq %rdx, -128(%rbp) movq %rcx, -136(%rbp) callq __TFOs7Processau9argumentsGSaSS_ movq (%rax), %rax movq %rax, %rdi movq %rax, -144(%rbp) callq _swift_bridgeObjectRetain leaq -48(%rbp), %rdi movl $2, %r10d movl %r10d, %esi movq -144(%rbp), %rdx movq %rax, -152(%rbp) callq __TTSg5SS___TFSag9subscriptFSix movq -144(%rbp), %rdi callq _swift_bridgeObjectRelease movq -48(%rbp), %rcx movq -40(%rbp), %r8 movq -32(%rbp), %r9 movq -120(%rbp), %rdi movq -128(%rbp), %rsi movq -136(%rbp), %rdx callq __TZFsoi1pFTSSSS_SS movq %rcx, %rdi movq %rax, -160(%rbp) movq %rdx, -168(%rbp) callq _swift_unknownRelease xorl %eax, %eax addq $176, %rsp popq %rbp retq .cfi_endproc .section __TEXT,__cstring,cstring_literals L___unnamed_1: .asciz " " .linker_option "-lswiftCore" .linker_option "-lobjc" .section __DATA,__objc_imageinfo,regular,no_dead_strip L_OBJC_IMAGE_INFO: .long 0 .long 768 .subsections_via_symbols 

As you can see, the addstr.swift assembly contains fewer commands than interpolation.swift .

Below are the test results using /usr/bin/time for synchronization (bash -3.2).

 $ ARG1=$(printf '📲%.0s' {1..30000}) # 30000 '📲' characters $ ARG2=$(printf '🖥%.0s' {1..30000}) # 30000 '🖥' characters $ time ./interpolation $ARG1 $ARG2 > > real 0m0.026s > user 0m0.018s > sys 0m0.006s $ time ./addstr $ARG1 $ARG2 > > real 0m0.026s > user 0m0.018s > sys 0m0.006s 

I performed this test many times, but the results were always the same (± 0.001 s).

0
source

All Articles