Open baarde opened 3 weeks ago
@Lukasa Thanks for your feedback. I've applied your suggestions. I agree: it is better.
I've rewritten the code to use the specialized MD5_*
/SHA*_*
APIs instead of the generic EVP_MD_*
APIs.
I've also made sure that context and digest buffers get zeroized after use (EVP_DigestFinal
and EVP_MD_CTX_free
already did that for context buffers).
One noticeable "hack" is the use of the @exclusivity
attribute to get a +15% performance improvement. This attribute was proposed in SE-0176 and implemented in Swift 5.7, but is currently undocumented.
@swift-server-bot add to allowlist
This PR reduces the number of heap allocations needed to compute a hash digest from 7 to 1.
Checklist
Motivation:
Currently, computing a hash digest requires 7 heap allocations:
DigestContext
object is created during init.EVP_MD_CTX
struct is allocated byEVP_MD_CTX_new
.md_data
is allocated when initializing the context usingEVP_DigestInit
.DigestContext
object is created during finalizeEVP_MD_CTX
struct is allocated byEVP_MD_CTX_new
.md_data
is allocated when copying the context usingEVP_MD_CTX_copy
.EVP_DigestFinalize
.This means that for small messages the
HashFunction.hash
method spends most of its time allocating and deallocating memory.Generally, users will only compute a few hash digests and aren't really concerned by the performance. But sometimes, it may be necessary to compute a lot of hash digests as fast as possible. Possible use cases include:
Modifications:
MD5_*
/SHA*_*
APIs are used instead of the genericEVP_MD_*
APIs.MD5_CTX
/SHA*_CTX
digest context is stored inline in theDigestContext
object.finalize
, the temporary digest context copy is allocated on the stack as a temporary variable.Result:
On its own, this change makes hashing about 2x faster and computing HMAC authentication codes about 1.5x faster.
On my machine (MacBook Pro 14-inch 2021) with suggested changes (
MD5: 19.1 → 10.5 seconds (9.0 seconds) SHA1: 15.8 → 7.5 seconds (6.2 seconds) SHA256: 16.1 → 7.7 seconds (6.3 seconds) SHA384: 20.4 → 10.6 seconds (9.2 seconds) SHA512: 19.8 → 10.6 seconds (9.1 seconds) HMAC-MD5: 50.4 → 31.0 seconds (28.6 seconds) HMAC-SHA1: 42.9 → 25.1 seconds (22.9 seconds) HMAC-SHA256: 43.5 → 25.7 seconds (23.1 seconds) HMAC-SHA384: 52.0 → 31.4 seconds (29.2 seconds) HMAC-SHA512: 50.5 → 32.0 seconds (29.3 seconds)@exclusivity(unchecked)
between parentheses)Test code
```swift import Crypto import Foundation func testDigest