Created
July 4, 2019 20:51
-
-
Save DaveWoodCom/b9fb8700b2f99ce7399ccb516e812790 to your computer and use it in GitHub Desktop.
Xcode Test to test the performance of using a `for` loop with a `where` clause vs an `if` statement vs a `guard` statement.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// TestProjectTests.swift | |
// Tests | |
// | |
// Based on a question Nick Lockwood asked on Twitter: https://twitter.com/nicklockwood/status/1146508387498303488 . | |
// And then followed up by GeekAndDad https://twitter.com/GeekAndDad/status/1146630469842116609 . | |
// These tests were executed on an iMac 5k, against an iOS project running in the XR Simulator. | |
// My results are included as comments in each test below. I ran the tests 5 times (which itself runs each test 10 times). | |
// The average of each 10 runs are listed below, as well as the placing 1st/2nd/3rd for each of the 3 styles. | |
// | |
// Style 1: for...where | |
// Style 2: for...if | |
// Style 3: for...guard | |
import XCTest | |
@testable import TestProject | |
let numberOfElements: Int = 10000 | |
let numberOfIterations: Int = 1000 | |
class TestProjectTests: XCTestCase { | |
struct SomeObject { | |
var isWall: Bool = false | |
} | |
let someObjects: [SomeObject] = { | |
var someObjects: [SomeObject] = [SomeObject]() | |
for i in 0 ..< numberOfElements { | |
someObjects.append(SomeObject(isWall: i % 4 == 0)) | |
} | |
return someObjects | |
}() | |
override func setUp() { | |
// Put setup code here. This method is called before the invocation of each test method in the class. | |
} | |
override func tearDown() { | |
// Put teardown code here. This method is called after the invocation of each test method in the class. | |
} | |
func testABufferPrime_IgnoreTheseResults() { | |
// This test should always run first and should be ignored | |
// It's here only to ensure the results of being the first test aren't skewed | |
// I've seen cases where the first test takes longer (presumably because it couldn't make use of any hardware caching etc) | |
var count: Int = 0 | |
self.measure { | |
for _ in 0 ..< numberOfIterations { | |
for someObject in someObjects { | |
if someObject.isWall { | |
count += 1 | |
} | |
} | |
} | |
} | |
} | |
func testStyle1() { | |
// 2.098, 2.263, 2.153, 2.105, 2.084 | |
// 2, 3, 2, 3, 3 | |
var count: Int = 0 | |
self.measure { | |
for _ in 0 ..< numberOfIterations { | |
for someObject in someObjects where someObject.isWall { | |
count += 1 | |
} | |
} | |
} | |
print("\(#function) -> count: \(count)") | |
} | |
func testStyle2() { | |
// 2.089, 2.238, 2.205, 2.080, 2.072 | |
// 1, 1, 3, 1, 1 | |
var count: Int = 0 | |
self.measure { | |
for _ in 0 ..< numberOfIterations { | |
for someObject in someObjects { | |
if someObject.isWall { | |
count += 1 | |
} | |
} | |
} | |
} | |
print("\(#function) -> count: \(count)") | |
} | |
func testStyle3() { | |
// 2.107, 2.242, 2.091, 2.092, 2.074 | |
// 3, 2, 1, 2, 2 | |
var count: Int = 0 | |
self.measure { | |
for _ in 0 ..< numberOfIterations { | |
for someObject in someObjects { | |
guard someObject.isWall else { continue } | |
count += 1 | |
} | |
} | |
} | |
print("\(#function) -> count: \(count)") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment