Assuming I'm reading your project correctly (and I've only skimmed it, sorry), you're comparing performance on the iOS Simulator? That's not really an environment where performance comparisons are meaningful. In particular, many OS components use default implementations when running on the simulator for a variety of reasons, while they use tuned implementations when running on iOS or macOS. Have you benchmarked against the native libraries at all?
Note that I'm very much not saying that it's not a problem, rather that simulator performance is significantly lower priority than iOS or macOS performance for most teams. In any event, your best course of action is to file a bug report via Feedback Assistant, since CryptoKit is an Apple framework, not part of the Swift project umbrella (meaning that the team responsible doesn't generally look for feedback here).
I changed to macOS SDK and added an example macOS app (no GUI, just initiating background task from AppDelegate) and running it on 2016 Macbook Pro yields ~ same result everytime, CryptoKit being 60% slower than CryptoSwift.
Thanks! I created an ssue using Feedback Assistant, not sure if those are visible to you (you need to sign in), but posting link to it anyway, for reference: Feedback Assistant
I tried adding a CommonCrypto-based implementation:
import CommonCrypto
public func sha256sha256(of data: Data) -> Data {
var buffer = UnsafeMutableRawPointer.allocate(byteCount: Int(CC_SHA256_DIGEST_LENGTH), alignment: 8)
var buffer2 = UnsafeMutablePointer<UInt8>.allocate(capacity: Int(CC_SHA256_DIGEST_LENGTH))
defer {
buffer.deallocate()
buffer2.deallocate()
}
data.withUnsafeBytes { (ptr: UnsafeRawBufferPointer) -> () in
CC_SHA256(ptr.baseAddress!, CC_LONG(data.count), buffer.assumingMemoryBound(to: UInt8.self))
}
CC_SHA256(buffer, CC_LONG(CC_SHA256_DIGEST_LENGTH), buffer2)
return Data(buffer: UnsafeBufferPointer(start: buffer2, count: Int(CC_SHA256_DIGEST_LENGTH)))
}
It ran in 1.094s compared 3.847 for CryptoSwift on my computer (I'm still on 10.14 so I couldn't test the CryptoKit one)
Worse, according to Instruments, only 279ms of that was actually spent in CC_SHA256, the rest was spent allocating and throwing away buffers, so if you spent a bit of time optimizing you should be able to do things much faster.
Based on this, I think that your issues are just as likely to be caused by you making a few extra data copies (for example when you call Data(digest)) or other things. I would recommend profiling your code in Instruments to see how much of this time is due to the library compared to how much of the time is due to your handling of its results.
Also you should probably turn on Whole Module Optimization
I think you’re over-releasing buffer2 (don’t have the docs handy, but I don’t think that initialiser copies the data)
IMO the cleanest way to do this would be to create a Data object with the required size and use its withUnsafeMutableBytes method to get the internal pointer to populate it.
I agree that writing directly into a data would be a better idea, but I don't think the current code double frees. IIRC there's a data initializer with "NoCopy" in the name that doesn't copy, and the rest do. Also double-frees usually blow up pretty quickly (free will trap) so I think I would have noticed.
Thanks a lot Karl for suggested performance optimization, much appriciated. In fact I knew the implementation of the POW is not optimized, I haven't got around to it yet.
But that is irrelevant to this thread I believe? It is independent? Since the fact remains, that the same code using either CryptoKit or CryptoSwift results in a big difference in performance...
Anyway, Apple followed up a couple of days ago, so awaiting response from them.