The performance is most of the times very important. In this post we will explore we can do performance testing in Xcode by using the measure block.
We can use the following algorithm (https://github.com/arnot-project/swift-bip39/blob/main/Sources/arnot/BIP39.swift)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
func bip39(withENT ent: EntropyLength) -> [UInt16] { let entropy = cryptoProvider.generateRandomBytes(withSize: ent.size) let sha = cryptoProvider.sha256(of: entropy) let checkSum = sha[0] >> (8 - ((entropy.count * 8) / 32)) return entropyPlusChecksumGrouped( in: entropy, checkSum: checkSum ) } private func entropyPlusChecksumGrouped(in input: [UInt8], checkSum: UInt8) -> [UInt16] { let mask: UInt16 = 0b0000011111111111 let input16 = input.map { UInt16($0) } let count = (input.count * 8 + (input.count * 8 / 32)) / 11 var output: [UInt16] = Array<UInt16>(repeating: 0, count: count) for (index, value) in input16.enumerated() { let firstWordIndex = (index * 8) / 11 let rightOffset = (index * 8) % 11 output[firstWordIndex] = output[firstWordIndex] | ((value << 3) >> rightOffset) let lastWordIndex = (index * 8 + 8) / 11 let leftOffset = 11 - (index * 8 + 8) % 11 output[lastWordIndex] = output[lastWordIndex] | (value << leftOffset) & mask } output[count - 1] = output[count - 1] | UInt16(checkSum) return Array(output) } |
Lets’ write a performance test for this algorithm:
1 2 3 4 5 6 |
func testPerformance_FistAlgortihm() { self.measure() { let sut = makeSUT(entropy: Array<UInt8>(repeating: 129, count: 256 / 8), sha256: [128]) _ = sut.bip39(withENT: .bits256) } } |
Now let’s write a second algorithm by using strings instead of bit manipulation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
func bip39_2(withENT ent: EntropyLength) -> [UInt16] { let entropy = cryptoProvider.generateRandomBytes(withSize: ent.size) let sha = cryptoProvider.sha256(of: entropy) let checkSum = sha[0] >> (8 - ((entropy.count * 8) / 32)) return entropyPlusChecksumGrouped2( in: entropy, checkSum: checkSum ) } private func entropyPlusChecksumGrouped2(in input: [UInt8], checkSum: UInt8) -> [UInt16] { let entropyBits = String(input.flatMap { ("00000000" + String($0, radix:2)).suffix(8) }) let checksumBits = String(checkSum, radix:2) let bits = entropyBits + checksumBits var output: [UInt16] = [] for i in 0..<(bits.count / 11) { let wi = UInt16(bits[bits.index(bits.startIndex, offsetBy: i * 11)..<bits.index(bits.startIndex, offsetBy: (i + 1) * 11)], radix: 2)! output.append(wi) } return output } |
and the performance test:
1 2 3 4 5 6 |
func testPerformance_SecondAlgortihm() { self.measure() { let sut = makeSUT(entropy: Array<UInt8>(repeating: 129, count: 256 / 8), sha256: [128]) _ = sut.bip39_2(withENT: .bits256) } } |
And now lets run our tests:
It looks that both have same time, but that is because the input array was small. But if we open them we can compare them better.
And we can set the baseline so we can compare their performance next time we run the tests.
It is important to commit to the repo the files that the Xcode creates for saving this information.