Skip to content

Optimizing hashing performance #136

Description

@fabiospampinato

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:

import crypto from 'node:crypto';

const value = '/Users/fabio/path/to/some/random/file.js';

console.time ( 'crypto' );
for ( let i = 0; i < 100_000; i++ ) {
  crypto.createHash("sha1").update(value).digest("hex");
}
console.timeEnd ( 'crypto' );

I see the following output:

~ ❯ node hashbench.js
crypto: 106.557ms
~ ❯ node hashbench.js
crypto: 108.052ms
~ ❯ node hashbench.js
crypto: 107.642ms
~ ❯ node hashbench.js
crypto: 107.136ms
~ ❯ node hashbench.js
crypto: 107.747ms
~ ❯ bun hashbench.js
[29.98ms] crypto
~ ❯ bun hashbench.js
[33.87ms] crypto
~ ❯ bun hashbench.js
[30.74ms] crypto
~ ❯ bun hashbench.js
[30.78ms] crypto
~ ❯ bun hashbench.js
[30.12ms] crypto

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

Activity

  1. transferred this issue fromnodejs/nodeon Nov 25, 2023
  2. H4ad commented on Nov 25, 2023

    @H4ad
    Member

    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 update function is handled by CryptoDigest.cpp.

    Apparently, bun uses openssl 1.x, Node.js 16.x also uses that same version, after 16.x Node.js started using openssl 3.x

    Instead of EVP_* functions (code), bun uses 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.03241100162267685ms

    So, even calling only update is slower than bun, so maybe using SHA1_UPDATE instead of EVP_DigestUpdate can help.

  3. Uzlopak commented on Nov 25, 2023

    @Uzlopak
    Contributor

    But is is even recommendable to use non EVP Calls?

  4. Uzlopak commented on Nov 26, 2023

    @Uzlopak
    Contributor

    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.

  5. H4ad commented on Nov 26, 2023

    @H4ad
    Member

    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 SHA1 functions but they still support it if you change the arguments to pass context, 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 update and digest, this improved the performance significantly.

  6. tniessen commented on Nov 28, 2023

    @tniessen
    Member

    Apparently, bun uses openssl 1.x, Node.js 16.x also uses that same version, after 16.x Node.js started using openssl 3.x

    There 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), bun uses 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 than crypto.Hash for one-shot hashing. It might still be slower than an equivalent implementation using OpenSSL 1.1.1 and low-level OpenSSL APIs.

  7. ronag commented on Nov 28, 2023

    @ronag
    Member

    Are we using OpenSSL 3.0 or 3.1?

  8. Uzlopak commented on Nov 28, 2023

    @Uzlopak
    Contributor

    @ronag
    A fork of 3.0 with quic patched. No original OpenSSL 3.0

    We are basically cut off from direct OpenSSL updates.

  9. ronag commented on Nov 28, 2023

    @ronag
    Member

    So basically we are stuck with this until we update OpenSSL.

  10. ronag commented on Nov 28, 2023

    @ronag
    Member

    Anyone considered switching to BoringSSL?

  11. Uzlopak commented on Nov 28, 2023

    @Uzlopak
    Contributor

    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.

  12. ronag commented on Nov 28, 2023

    @ronag
    Member

    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?

  13. targos commented on Nov 28, 2023

    @targos
    Member

    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.

  14. tniessen commented on Nov 28, 2023

    @tniessen
    Member

    BoringSSL 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.

  15. GeoffreyBooth commented on Dec 1, 2023

    @GeoffreyBooth
    Member

    @nodejs/performance @nodejs/crypto

  16. 35 remaining items

  17. locked as spam and limited conversation to collaborators on Sep 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      Sponsor
      SponsoredKunjungi sekarang
      Promo