From d64835f97e629842c53944ea8c7633a8355840ca Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Fri, 13 Sep 2024 20:29:22 -0400 Subject: [PATCH] node-api: add external buffer creation benchmark Add a micro benchmark for external buffer creation. PR-URL: https://github.com/nodejs/node/pull/54877 Refs: https://github.com/nodejs/node/issues/53804 Refs: https://github.com/nodejs/node/issues/44111 Reviewed-By: Gabriel Schulhof Reviewed-By: Michael Dawson --- benchmark/napi/buffer/.gitignore | 1 + benchmark/napi/buffer/binding.cc | 85 +++++++++++++++++++++++++++++++ benchmark/napi/buffer/binding.gyp | 18 +++++++ benchmark/napi/buffer/index.js | 14 +++++ 4 files changed, 118 insertions(+) create mode 100644 benchmark/napi/buffer/.gitignore create mode 100644 benchmark/napi/buffer/binding.cc create mode 100644 benchmark/napi/buffer/binding.gyp create mode 100644 benchmark/napi/buffer/index.js diff --git a/benchmark/napi/buffer/.gitignore b/benchmark/napi/buffer/.gitignore new file mode 100644 index 00000000000000..567609b1234a9b --- /dev/null +++ b/benchmark/napi/buffer/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/benchmark/napi/buffer/binding.cc b/benchmark/napi/buffer/binding.cc new file mode 100644 index 00000000000000..d593ecbc3840f2 --- /dev/null +++ b/benchmark/napi/buffer/binding.cc @@ -0,0 +1,85 @@ +#include +#include +#include + +#define NODE_API_CALL(call) \ + do { \ + napi_status status = call; \ + if (status != napi_ok) { \ + fprintf(stderr, #call " failed: %d\n", status); \ + abort(); \ + } \ + } while (0) + +#define ABORT_IF_FALSE(condition) \ + if (!(condition)) { \ + fprintf(stderr, #condition " failed\n"); \ + abort(); \ + } + +static void Finalize(node_api_basic_env env, void* data, void* hint) { + delete[] static_cast(data); +} + +static napi_value CreateExternalBuffer(napi_env env, napi_callback_info info) { + napi_value argv[2], undefined, start, end; + size_t argc = 2; + int32_t n = 0; + napi_valuetype val_type = napi_undefined; + + // Validate params and retrieve start and end function. + NODE_API_CALL(napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr)); + ABORT_IF_FALSE(argc == 2); + NODE_API_CALL(napi_typeof(env, argv[0], &val_type)); + ABORT_IF_FALSE(val_type == napi_object); + NODE_API_CALL(napi_typeof(env, argv[1], &val_type)); + ABORT_IF_FALSE(val_type == napi_number); + NODE_API_CALL(napi_get_named_property(env, argv[0], "start", &start)); + NODE_API_CALL(napi_typeof(env, start, &val_type)); + ABORT_IF_FALSE(val_type == napi_function); + NODE_API_CALL(napi_get_named_property(env, argv[0], "end", &end)); + NODE_API_CALL(napi_typeof(env, end, &val_type)); + ABORT_IF_FALSE(val_type == napi_function); + NODE_API_CALL(napi_get_value_int32(env, argv[1], &n)); + + NODE_API_CALL(napi_get_undefined(env, &undefined)); + + constexpr uint32_t kBufferLen = 32; + + // Start the benchmark. + napi_call_function(env, argv[0], start, 0, nullptr, nullptr); + + for (int32_t idx = 0; idx < n; idx++) { + napi_handle_scope scope; + uint8_t* buffer = new uint8_t[kBufferLen]; + napi_value jsbuffer; + NODE_API_CALL(napi_open_handle_scope(env, &scope)); + NODE_API_CALL(napi_create_external_buffer( + env, kBufferLen, buffer, Finalize, nullptr, &jsbuffer)); + NODE_API_CALL(napi_close_handle_scope(env, scope)); + } + + // Conclude the benchmark. + napi_value end_argv[] = {argv[1]}; + NODE_API_CALL(napi_call_function(env, argv[0], end, 1, end_argv, nullptr)); + + return undefined; +} + +NAPI_MODULE_INIT() { + napi_property_descriptor props[] = { + {"createExternalBuffer", + nullptr, + CreateExternalBuffer, + nullptr, + nullptr, + nullptr, + static_cast(napi_writable | napi_configurable | + napi_enumerable), + nullptr}, + }; + + NODE_API_CALL(napi_define_properties( + env, exports, sizeof(props) / sizeof(*props), props)); + return exports; +} diff --git a/benchmark/napi/buffer/binding.gyp b/benchmark/napi/buffer/binding.gyp new file mode 100644 index 00000000000000..c80eb5357b7f81 --- /dev/null +++ b/benchmark/napi/buffer/binding.gyp @@ -0,0 +1,18 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.cc' ], + 'defines': [ + 'NAPI_EXPERIMENTAL' + ] + }, + { + 'target_name': 'binding_node_api_v8', + 'sources': [ 'binding.cc' ], + 'defines': [ + 'NAPI_VERSION=8' + ] + } + ] +} diff --git a/benchmark/napi/buffer/index.js b/benchmark/napi/buffer/index.js new file mode 100644 index 00000000000000..25429a93fab01b --- /dev/null +++ b/benchmark/napi/buffer/index.js @@ -0,0 +1,14 @@ +'use strict'; + +const common = require('../../common.js'); + +const bench = common.createBenchmark(main, { + n: [5e6], + addon: ['binding', 'binding_node_api_v8'], + implem: ['createExternalBuffer'], +}); + +function main({ n, implem, addon }) { + const binding = require(`./build/${common.buildType}/${addon}`); + binding[implem](bench, n); +}