Repository navigation
Optimizing hashing performance #136
Description
Activity
I spent a few minutes trying to investigate this and here are some numbers.
Bun
[39.36ms] crypto
Node.js 16.20.1
crypto: 85.105ms
Node.js 18.18.1
crypto: 105.906ms
Node.js 20.9.0
crypto: 91.476ms
Node.js 21.1.0
crypto: 90.148ms
About bun, the
updatefunction is handled by CryptoDigest.cpp.Apparently,
bunuses openssl 1.x, Node.js 16.x also uses that same version, after 16.x Node.js started using openssl 3.xInstead of EVP_* functions (code),
bunuses SHA1 Functions (code).I updated the benchmark to:
const crypto = require('node:crypto'); const value = '/Users/fabio/path/to/some/random/file.js'; const hash = crypto.createHash("sha1"); const start = performance.now(); hash.update(value) console.log(`End: ${performance.now() - start}`);
Bun: 0.0250780000000006ms
Node.js 21.1.0: 0.03838299959897995ms
Node.js 21.1.0 (with%OptimizeFunctionOnNextCall(hash.update)): 0.03241100162267685msSo, even calling only
updateis slower thanbun, so maybe usingSHA1_UPDATEinstead ofEVP_DigestUpdatecan help.But is is even recommendable to use non EVP Calls?
Btw. It is crazy how slow it is to sha256 hmac a string. It is something i cant optimize in probot and results in about 6k req/s performance of a probot server.
But is is even recommendable to use non EVP Calls?
I don't have any idea, in the documentation page, they deprecated some overloads of
SHA1functions but they still support it if you change the arguments to passcontext, data, length.So, I think is safe to use.
Btw. It is crazy how slow it is to sha256 hmac a string. It is something i cant optimize in probot and results in about 6k req/s performance of a probot server.
Once I cached all the string and then performed only one
updateanddigest, this improved the performance significantly.Apparently,
bunuses openssl 1.x, Node.js 16.x also uses that same version, after 16.x Node.js started using openssl 3.xThere are various known performance issues related to OpenSSL 3. It's possible that they negatively affect performance of hashing through
EVP_*as well, but someone would have to benchmark the C/C++ code itself to be sure.Instead of EVP_* functions (code),
bunuses SHA1 Functions (code).The
EVP_*functions are recommended over low-level functions etc. The latter are gradually being deprecated and using them might break compatibility with OpenSSL providers/engines.
FWIW, I proposed
crypto.hash()at some point (see nodejs/node#42233), which would be faster thancrypto.Hashfor one-shot hashing. It might still be slower than an equivalent implementation using OpenSSL 1.1.1 and low-level OpenSSL APIs.Reacted by Robert Nagy, Aras Abbasi, Fabio Spampinato, Steven and Heyward FannAre we using OpenSSL 3.0 or 3.1?
@ronag
A fork of 3.0 with quic patched. No original OpenSSL 3.0We are basically cut off from direct OpenSSL updates.
So basically we are stuck with this until we update OpenSSL.
Anyone considered switching to BoringSSL?
I guess a PR to switch to BoringSSL would result in a lot of negative feedback. Also BoringSSL does not guarantee stable ABI.
https://boringssl.googlesource.com/boringssl/+/HEAD/PORTING.md#porting-from-openssl-to-boringssl
Note: BoringSSL does not have a stable API or ABI. It must be updated with its consumers. It is not suitable for, say, a system library in a traditional Linux distribution. For instance, Chromium statically links the specific revision of BoringSSL it was built against. Likewise, Android's system-internal copy of BoringSSL is not exposed by the NDK and must not be used by third-party applications.
E.g. @mcollina mentioned that stable ABI is important for him. So I assume for other it is also important.
E.g. @mcollina mentioned that stable ABI is important for him. So I assume for other it is also important.
Is this a theoretical issue or a practical one? Do they actually break the ABI that often? I think quic support is better in BoringSSL so at the end of the day it might be easier to use?
https://boringssl.googlesource.com/boringssl/+/HEAD/README.md
Although BoringSSL is an open source project, it is not intended for general use, as OpenSSL is. We don't recommend that third parties depend upon it. Doing so is likely to be frustrating because there are no guarantees of API or ABI stability.
Reacted by Robert Nagy, Tobias Nießen and StevenBoringSSL also lacks various features that users of Node.js might rely on and that'd we'd have to polyfill somehow, e.g., certain algorithms.
@nodejs/performance @nodejs/crypto
35 remaining items
- locked as spam and limited conversation to collaborators
on Sep 6, 2024
What is the problem this feature will solve?
Making the hash functions significantly faster.
What is the feature you are proposing to solve the problem?
I'm not sure what the best option for this is, but running the following file:
I see the following output:
Basically Node's sha1 function seems at least 3x slower than Bun's.
Hashing is at the core of many important things, so I'd argue it's important to hash as fast as possible since that would speed up a cascade of use cases.
I'm not sure what the best solution is here, but if Bun is 3x faster than Node here presumably there's a lot of room for improvement.
What alternatives have you considered?
No response