-2

I wish to remove all intermediate spaces in s String.

Option 1:

 func removingWhitespaces() -> String {
    return replacingOccurrences(of: " ", with: "")
 }

Option 2:

 func removingWhitespaces() -> String {
    return components(separatedBy: .whitespaces).joined()
 }

Which is more performant ?

Abhishek Bedi
  • 5,205
  • 2
  • 36
  • 62

5 Answers5

1

As per my point of view, I think option 1 is faster than the options 2.

Reason:

In option 2, you are chaining the return value of components(separatedBy:) to joined(). So ultimately the return value of the joined()` is used whereas in the option 1 you are directly calling the build-in function of the String.

Mahendra
  • 8,448
  • 3
  • 33
  • 56
1

As per my understanding, I would like to suggest Option 1

Because,

replacingOccurrences(of: " ", with: "")

will perform only single operation.

where,

components(separatedBy: .whitespaces).joined()

will perform two operation and taking more time. First it will separate elements by whitespace and create array then on array it will perform join operation and give you output.

Sagar Chauhan
  • 5,715
  • 2
  • 22
  • 56
1

Figure it out yourself. Basic performance testing is very simple in Xcode. In a XCTestCase class run these 2 tests

func testPerformance1() {
    let string = "I wish to remove all intermediate spaces in a String"
    self.measure {
        for _ in 0..<10000 {
            _ = string.replacingOccurrences(of: " ", with: "")
        }
    }
}

func testPerformance2() {
    let string = "I wish to remove all intermediate spaces in a String"
    self.measure {
        for _ in 0..<10000 {
            _ = string.components(separatedBy: .whitespaces).joined()
        }
    }
}

and read the result in the console. replacingOccurrences is much faster.

There is no big difference between components(separatedBy: " ") and components(separatedBy: .whitespaces)

vadian
  • 274,689
  • 30
  • 353
  • 361
1

Space

When talking about performance, one should take space complexity into account. What's meant by this term is how much memory will be needed to run this piece of code and describes the relationship between the number of elements in the input and the reserved memory. For example, we talk about:

  • O(n) space complexity when the reserved memory grows with the number of elements in the input.
  • O(1) space complexity when the reserved memory doesn't grow when the number of the input elements grows.

Between replacingOccurrences(of: " ", with: "") and components(separatedBy: .whitespaces).joined(), the former wins on space complexity since the latter creates an intermidiary array, and in performance, less is more.

Time

Given this string :

let str = "Lorem ipsum dolor sit amet, tempor nulla integer morbi, amet non amet pede quis enim, ipsum in a in congue etiam, aenean orci wisi, habitant ipsum magna auctor quo odio leo. Urna nunc. Semper mauris felis vivamus dictumst. Tortor volutpat fringilla sed, lorem dui bibendum ligula faucibus massa, dis metus volutpat nec ridiculus, ac vel vitae. At pellentesque, at sed, fringilla erat, justo eu at porttitor vestibulum hac, morbi in etiam sed nam. Elit consectetuer lorem feugiat, ante turpis elit et pellentesque erat nec, vitae a fermentum vivamus ut. Orci donec nulla justo non id quis, ante vestibulum nec, volutpat a egestas pretium aliquam non sed, eget vivamus vestibulum, ornare sed tempus. Suscipit laoreet vivamus congue, tempor amet erat nulla, nostrum justo, wisi cras ac tempor tincidunt eu, hac faucibus convallis. Ac massa aenean nunc est orci, erat facilisis. Aliquam donec. Ut blandit potenti quam quis pellentesque, cursus imperdiet morbi ea ut, non mauris consectetuer mauris risus vehicula in, sed rutrum pellentesque turpis. Eros gravida volutpat justo proin donec penatibus, suspendisse fermentum sed proin fringilla libero malesuada, nulla lectus ligula, aliquam amet, nemo quis est. Quis imperdiet, class leo, lobortis etiam volutpat lacus wisi. Vestibulum vitae, nibh sem molestie natoque. Elementum ornare, rutrum quisque ultrices odio mauris condimentum et, auctor elementum erat ultrices. Ex gravida libero molestie facilisi rutrum, wisi quam penatibus, dignissim elementum elit mi, mauris est elit convallis. Non etiam mauris pretium id, tempus neque magna, tincidunt odio metus habitasse in maecenas nonummy. Suspendisse eget neque, pretium fermentum elementum."

The benchmarking code is given below. Each code block will be run separately, while the others will be commented out. :

do {
    let start = Date()
    let result = str.components(separatedBy: " ").joined()
    let end = Date()
    print(result.count, end.timeIntervalSince(start))
}

do {
    let start = Date()
    let result = str.split(separator: " ").joined()
    let end = Date()
    print(result.count, end.timeIntervalSince(start))
}

do {
    let start = Date()
    let result = str.filter { !$0.isWhitespace }
    let end = Date()
    print(s.count, end.timeIntervalSince(start))
}

do {
    let start = Date()
    var s = str
    s.removeAll { $0.isWhitespace }
    let end = Date()
    print(s.count, end.timeIntervalSince(start))
}

do {
    let start = Date()
    let result = str.components(separatedBy: .whitespaces).joined()
    let end = Date()
    print(result.count, end.timeIntervalSince(start))
}

do {
    let start = Date()
    var result = ""

    for char in str where char != " " {
        result.append(char)
    }

    let end = Date()
    print(result.count, end.timeIntervalSince(start))
}

do {
    let start = Date()
    let result = str.replacingOccurrences(of: " ", with: "")
    let end = Date()
    print(result.count, end.timeIntervalSince(start))
}

do {
    let start = Date()
    var arr = str.utf8CString
    arr.removeAll(where: { $0 != 32 })
    var result = ""
    arr.withUnsafeBufferPointer { ptr in
        result = String(cString: ptr.baseAddress!)
    }
    let end = Date()
    print(result.count, end.timeIntervalSince(start))
}

Compiled with optimization in the terminal using this command:

xcrun swiftc -O ReplaceStr.swift -o replaceStr
  • -O: with optimizations
  • ReplaceStr.swift: the name of the file containing the code. You should cd to the location of this file first.
  • -o: to specify the name of the output compiled file
  • replaceStr is an example name for the output file

And then run with ./replaceStr

After running each block multiple times, here are the best timings:

components(separatedBy: " ").joined()          : 0.77ms
components(separatedBy: .whitespaces).joined() : 0.75ms
str.split(separator: " ").joined()             : 0.54ms
filter { !$0.isWhitespace }                    : 0.52ms
removeAll { $0.isWhitespace }                  : 0.52ms
for char in str where char != " "              : 0.26ms
replacingOccurrences(of: " ", with: "")        : 0.23ms
str.utf8CString                                : 0.18ms

Comparable results were found with a shorter string :

let str = "Lorem ipsum dolor sit amet, tempor nulla integer morbi, amet non amet pede quis enim, ipsum in a in congue etiam, aenean orci wisi, habitant ipsum magna auctor quo odio leo."

Verdict

replacingOccurrences(of: " ", with: "") is better than components(separatedBy: .whitespaces).joined() in time complexity too. This is partially because replacingOccurrences(of:with:) is defined on NSString and not String. In a sense it's like comparing to .

Manipulating the underlying CString beats them all and is the overall best.


For more on benchmarking code, here is a good thread.

ielyamani
  • 17,807
  • 10
  • 55
  • 90
  • 1
    I *assume* that the last variant is fast since Swift 5, because the underlying representation changed to UTF-8 (https://swift.org/blog/utf8-string/). – You can simplify that slightly with `let result = arr.withUnsafeBufferPointer { ptr in String(cString: ptr.baseAddress!) }` – Martin R May 08 '19 at 09:54
  • @MartinR Thank you for the link! I was intending to tag you and ask about a better way of writing that code including pointers and *CStuff* (knowing how good you are with both). Faster code and feedback on the results are always welcome. – ielyamani May 08 '19 at 12:25
0

Using Split with joined is faster the other 2 options

class new: XCTestCase {
    func testOption1() {
        let string = String(repeating: "This is an example of a performance test case.", count: 10000)
        self.measure {//0.0231s
            _ =  string.replacingOccurrences(of: " ", with: "")
        }
    }
    func testOption2() {
        let string = String(repeating: "This is an example of a performance test case.", count: 10000)
        self.measure {//0.194s
            _ =  string.components(separatedBy: " ").joined()
        }
    }
    func testOption3() {
        let string = String(repeating: "This is an example of a performance test case.", count: 10000)
        self.measure {//0.0184s
            _ =  string.split(separator: " ").joined()
        }
    }
}
RajeshKumar R
  • 15,445
  • 2
  • 38
  • 70