Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

wip: crypto: use cppgc to manage Hash #51017

Draft
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

joyeecheung
Copy link
Member

@joyeecheung joyeecheung commented Dec 2, 2023

While working on #40786 I happened to notice that the current BaseObject management comes with a significant overhead from global handle creation and incidentally migrating these objects to Oilpan gives us faster creation of objects (in my local testing, creating a cppgc-managed object is about 2.5x faster than creating a weak BaseObject).

This is only a WIP and not yet ready for review. It's mostly open as a reference for nodejs/performance#136 and for running benchmark CI. The primary blocker is that I'll need to figure out how to support embedder object book-keeping in the heap snapshot with cppgc (right now it doesn't work and embedder objects become missing in the heap snapshots) - EDIT: this is currently being worked on in https://chromium-review.googlesource.com/c/v8/v8/+/5630497

Refs: nodejs/performance#136
Refs: #40786

On macOS + M2 where OpenSSL 3 performs much better

                               confidence improvement accuracy (*)   (**)  (***)
crypto/create-hash.js n=100000        ***     16.29 %       ±1.83% ±2.44% ±3.18%
                                                                              confidence improvement accuracy (*)   (**)  (***)
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=10 sync='createHash'         ***      9.52 %       ±1.41% ±1.88% ±2.46%
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=100 sync='createHash'        ***      9.96 %       ±1.71% ±2.27% ±2.97%
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=20 sync='createHash'         ***      9.85 %       ±1.08% ±1.44% ±1.87%
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=50 sync='createHash'         ***      9.80 %       ±1.91% ±2.55% ±3.35%

On Ubuntu + Intel(R) Xeon(R) Platinum 8280 CPU @ 2.70GHz, where OpenSSL 3 does not perform as well and becomes the bottleneck in digest computation:

                               confidence improvement accuracy (*)   (**)  (***)
crypto/create-hash.js n=100000        ***     17.17 %       ±1.53% ±2.03% ±2.65%
                                                                              confidence improvement accuracy (*)   (**)  (***)
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=10 sync='createHash'                  0.43 %       ±0.91% ±1.21% ±1.57%
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=100 sync='createHash'                 0.55 %       ±0.98% ±1.31% ±1.70%
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=20 sync='createHash'                  0.18 %       ±1.07% ±1.42% ±1.86%
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=50 sync='createHash'                  1.19 %       ±1.71% ±2.28% ±2.98%

@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/crypto
  • @nodejs/gyp

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Dec 2, 2023

namespace node {

#define ASSIGN_OR_RETURN_UNWRAP_CPPGC(ptr, obj, ...) \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind adding a small documentation/comment around what cppgc is, some important links etc to this document?

Copy link
Member

@tniessen tniessen Dec 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some documentation should probably go into src/README.md.

Environment* env_; \
v8::TracedReference<v8::Object> traced_reference_;

class CppgcMixin {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment for the purpose of this class?

@@ -103,7 +109,8 @@ void Hash::New(const FunctionCallbackInfo<Value>& args) {
xof_md_len = Just<unsigned int>(args[1].As<Uint32>()->Value());
}

Hash* hash = new Hash(env, args.This());
Hash* hash = cppgc::MakeGarbageCollected<Hash>(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind adding a comment to here why we don't call new Hash but prefer the recommended change to here.

ASSIGN_OR_RETURN_UNWRAP(&ctx, args.Holder());
} else {
ctx = CppgcMixin::Unwrap<T>(args.Holder());
if (ctx == nullptr) return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In what scenario can ctx be nullptr?

@joyeecheung
Copy link
Member Author

joyeecheung commented Dec 2, 2023

@anonrig

This is only a WIP and not yet ready for review.

I am mostly opening it to run the benchmark CI.

@lemire
Copy link
Member

lemire commented Dec 2, 2023

@joyeecheung I ran local benchmarks earlier today (using a commit you had added) and it did solve the performance issues for me. However, I did not investigate further and I wasn’t very careful.

However I got interested in your commit because it is attacking a performance bottleneck.

@joyeecheung
Copy link
Member Author

Results from the CI https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1476/console

00:02:11                                                                                 confidence improvement accuracy (*)   (**)  (***)
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-1' data=10 sync='createHash'            **      7.92 %       ±5.59% ±7.44% ±9.68%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-1' data=10 sync='subtle'                        0.76 %       ±2.14% ±2.85% ±3.70%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-1' data=100 sync='createHash'          ***      9.56 %       ±3.62% ±4.82% ±6.29%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-1' data=100 sync='subtle'                       1.73 %       ±2.66% ±3.54% ±4.61%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-1' data=20 sync='createHash'           ***      8.95 %       ±4.04% ±5.38% ±7.02%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-1' data=20 sync='subtle'                        2.32 %       ±2.92% ±3.89% ±5.07%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-1' data=50 sync='createHash'             *      5.19 %       ±4.34% ±5.80% ±7.59%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-1' data=50 sync='subtle'                        2.05 %       ±2.71% ±3.61% ±4.70%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-256' data=10 sync='createHash'          **      7.96 %       ±4.75% ±6.32% ±8.24%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-256' data=10 sync='subtle'              **      3.90 %       ±2.37% ±3.15% ±4.11%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-256' data=100 sync='createHash'        ***      7.01 %       ±3.74% ±4.98% ±6.49%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-256' data=100 sync='subtle'                     0.79 %       ±2.86% ±3.81% ±4.96%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-256' data=20 sync='createHash'           *      4.94 %       ±3.82% ±5.11% ±6.68%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-256' data=20 sync='subtle'                     -0.97 %       ±2.59% ±3.46% ±4.51%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-256' data=50 sync='createHash'         ***      7.86 %       ±4.28% ±5.70% ±7.43%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-256' data=50 sync='subtle'                      1.59 %       ±2.62% ±3.49% ±4.54%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-384' data=10 sync='createHash'         ***      8.68 %       ±4.17% ±5.55% ±7.23%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-384' data=10 sync='subtle'                      2.92 %       ±3.18% ±4.23% ±5.50%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-384' data=100 sync='createHash'          *      3.08 %       ±3.05% ±4.08% ±5.37%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-384' data=100 sync='subtle'              *      3.04 %       ±2.55% ±3.40% ±4.42%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-384' data=20 sync='createHash'         ***      5.73 %       ±3.24% ±4.31% ±5.62%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-384' data=20 sync='subtle'               *      2.86 %       ±2.55% ±3.40% ±4.43%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-384' data=50 sync='createHash'         ***      6.76 %       ±3.47% ±4.62% ±6.01%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-384' data=50 sync='subtle'               *      3.42 %       ±2.74% ±3.65% ±4.75%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-512' data=10 sync='createHash'           *      5.22 %       ±4.30% ±5.72% ±7.46%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-512' data=10 sync='subtle'                      0.00 %       ±2.91% ±3.87% ±5.04%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-512' data=100 sync='createHash'         **      5.90 %       ±3.49% ±4.65% ±6.05%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-512' data=100 sync='subtle'                     0.78 %       ±2.71% ±3.61% ±4.70%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-512' data=20 sync='createHash'                  3.56 %       ±3.88% ±5.18% ±6.76%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-512' data=20 sync='subtle'                      1.67 %       ±3.01% ±4.01% ±5.21%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-512' data=50 sync='createHash'                  1.94 %       ±3.19% ±4.26% ±5.58%
00:02:11 crypto/webcrypto-digest.js n=100000 method='SHA-512' data=50 sync='subtle'                      2.49 %       ±2.87% ±3.82% ±4.98%

And https://ci.nodejs.org/view/Node.js%20benchmark/job/benchmark-node-micro-benchmarks/1475/console - looks like n is too small for the CI machine and needs to be increased. It seems interesting how in the CI machine the digest benchmarks get a more stable speedup (though we also know that this largely depends on the CPU and how big of a bottleneck OpenSSL is on that CPU).

23:20:03                                confidence improvement accuracy (*)    (**)   (***)
23:20:03 crypto/create-hash.js n=100000                 4.18 %       ±7.75% ±10.31% ±13.42%

@tniessen tniessen added the wip Issues and PRs that are still a work in progress. label Dec 3, 2023
@joyeecheung
Copy link
Member Author

Updated a bit to use cppgc::GarbageCollectedMixin and cppgc::NameProvider. Startup snapshot integration is still unsolved but I don't think it's needed for Hash. Though it would be nice to be able to customize the memory tracking so we can track OpenSSL memory in the heap snapshot. Still looking into it.

Somehow numbers are even better now on macOS + M2 Max

                               confidence improvement accuracy (*)   (**)  (***)
crypto/create-hash.js n=100000        ***     27.16 %       ±1.12% ±1.49% ±1.94%
                                                                                confidence improvement accuracy (*)   (**)  (***)
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=10 sync='createHash'           ***      9.44 %       ±1.53% ±2.04% ±2.66%
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=100 sync='createHash'          ***      9.19 %       ±1.49% ±1.99% ±2.60%
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=20 sync='createHash'           ***     11.12 %       ±3.46% ±4.65% ±6.13%
crypto/webcrypto-digest.js n=100000 method='SHA-1' data=50 sync='createHash'           ***      8.01 %       ±1.71% ±2.27% ±2.97%
crypto/webcrypto-digest.js n=100000 method='SHA-256' data=10 sync='createHash'         ***      9.66 %       ±2.89% ±3.86% ±5.05%
crypto/webcrypto-digest.js n=100000 method='SHA-256' data=100 sync='createHash'        ***      9.49 %       ±1.26% ±1.68% ±2.19%
crypto/webcrypto-digest.js n=100000 method='SHA-256' data=20 sync='createHash'         ***     11.40 %       ±3.15% ±4.23% ±5.59%
crypto/webcrypto-digest.js n=100000 method='SHA-256' data=50 sync='createHash'         ***      9.98 %       ±1.42% ±1.89% ±2.47%
crypto/webcrypto-digest.js n=100000 method='SHA-384' data=10 sync='createHash'         ***      8.42 %       ±1.44% ±1.91% ±2.49%
crypto/webcrypto-digest.js n=100000 method='SHA-384' data=100 sync='createHash'        ***      8.88 %       ±1.43% ±1.91% ±2.49%
crypto/webcrypto-digest.js n=100000 method='SHA-384' data=20 sync='createHash'         ***      8.12 %       ±0.94% ±1.26% ±1.63%
crypto/webcrypto-digest.js n=100000 method='SHA-384' data=50 sync='createHash'         ***      8.81 %       ±1.38% ±1.83% ±2.39%
crypto/webcrypto-digest.js n=100000 method='SHA-512' data=10 sync='createHash'         ***      7.36 %       ±2.96% ±3.96% ±5.20%
crypto/webcrypto-digest.js n=100000 method='SHA-512' data=100 sync='createHash'        ***      7.14 %       ±1.52% ±2.03% ±2.66%
crypto/webcrypto-digest.js n=100000 method='SHA-512' data=20 sync='createHash'         ***      7.70 %       ±3.24% ±4.35% ±5.76%
crypto/webcrypto-digest.js n=100000 method='SHA-512' data=50 sync='createHash'         ***      5.71 %       ±3.20% ±4.29% ±5.63%

@joyeecheung
Copy link
Member Author

From discussions in https://docs.google.com/document/d/1ny2Qz_EsUnXGKJRGxoA-FXIE2xpLgaMAN6jD7eAkqFQ/edit looks like we need to upstream an API to V8 to track external memory in the heap snapshot. Looking into an API - it probably wouldn't be too different from what we already have in MemoryRetainer...

@joyeecheung
Copy link
Member Author

Started working on the external memory tracking API: https://chromium-review.googlesource.com/c/v8/v8/+/5630497 it may need to take care of the GC tuning part too, from the discussions in the doc

Original commit message:

    [cppgc] add cppgc::Visitor::TraceExternal()

    This allows tracking externally-managed memory owned by cppgc-managed
    objects in the heap snapshots.

    To accompany this API, a class cppgc::External is added to abstract
    over externally-managed structures.

    Design doc: https://docs.google.com/document/d/1-kHbj9SNL3wMXZz_GZiDw6kHUJ1IvicFzEmqC_um0Wg/edit#heading=h.n1atlriavj6v

    Change-Id: I7a27eaa45e29d3c14936997d115a112a6e54458c

Refs: v8/v8@fa7509f
@joyeecheung joyeecheung added the blocked PRs that are blocked by other issues or PRs. label Aug 18, 2024
@joyeecheung
Copy link
Member Author

joyeecheung commented Aug 18, 2024

Rebased after v8 12.8 landed on main, and used updated version of https://chromium-review.googlesource.com/c/v8/v8/+/5630497 as well as the helpers in #52295 - the other PR will be ready after I finish the docs, whereas this PR depends on the mentioned V8 CL that needs to be hashed out (especially the GC scheduling integration part) since unlike ContextifyScript, crypto::Hash does have external memory, so this won't be unblocked until the V8 CL lands.

With the WIP V8 CL the heap snapshot after migration looks like this:

Screen Shot 2024-08-18 at 20 47 15

(Currently cppgc-managed objects cannot specify edge names in the heap snapshot, that is being worked on in https://docs.google.com/document/d/1PQQHhT0MLlStoiqNmji2-GcX62xtAsXPrihXi403ib4/edit#heading=h.n1atlriavj6v)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
blocked PRs that are blocked by other issues or PRs. c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. wip Issues and PRs that are still a work in progress.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants