From 05a9413527c2001851f544a36c15c07241a61c40 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Mon, 29 Jul 2024 12:19:52 +0100 Subject: [PATCH] node-api: add property keys benchmark PR-URL: https://github.com/nodejs/node/pull/54012 Reviewed-By: Yagiz Nizipli Reviewed-By: Michael Dawson --- benchmark/napi/property_keys/.gitignore | 1 + benchmark/napi/property_keys/binding.cc | 111 +++++++++++++++++++++++ benchmark/napi/property_keys/binding.gyp | 9 ++ benchmark/napi/property_keys/index.js | 15 +++ tools/build_addons.py | 12 ++- 5 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 benchmark/napi/property_keys/.gitignore create mode 100644 benchmark/napi/property_keys/binding.cc create mode 100644 benchmark/napi/property_keys/binding.gyp create mode 100644 benchmark/napi/property_keys/index.js diff --git a/benchmark/napi/property_keys/.gitignore b/benchmark/napi/property_keys/.gitignore new file mode 100644 index 00000000000000..567609b1234a9b --- /dev/null +++ b/benchmark/napi/property_keys/.gitignore @@ -0,0 +1 @@ +build/ diff --git a/benchmark/napi/property_keys/binding.cc b/benchmark/napi/property_keys/binding.cc new file mode 100644 index 00000000000000..258193f4b356a7 --- /dev/null +++ b/benchmark/napi/property_keys/binding.cc @@ -0,0 +1,111 @@ +#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 napi_value Runner(napi_env env, + napi_callback_info info, + napi_value property_key) { + napi_value argv[2], undefined, js_array_length, start, end; + size_t argc = 2; + napi_valuetype val_type = napi_undefined; + bool is_array = false; + uint32_t array_length = 0; + napi_value* native_array; + + // 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_is_array(env, argv[1], &is_array)); + ABORT_IF_FALSE(is_array); + NODE_API_CALL(napi_get_array_length(env, argv[1], &array_length)); + 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_undefined(env, &undefined)); + NODE_API_CALL(napi_create_uint32(env, array_length, &js_array_length)); + + // Copy objects into a native array. + native_array = + static_cast(malloc(array_length * sizeof(napi_value))); + for (uint32_t idx = 0; idx < array_length; idx++) { + NODE_API_CALL(napi_get_element(env, argv[1], idx, &native_array[idx])); + } + + // Start the benchmark. + napi_call_function(env, argv[0], start, 0, nullptr, nullptr); + + for (uint32_t idx = 0; idx < array_length; idx++) { + NODE_API_CALL( + napi_set_property(env, native_array[idx], property_key, undefined)); + } + + // Conclude the benchmark. + NODE_API_CALL( + napi_call_function(env, argv[0], end, 1, &js_array_length, nullptr)); + + free(native_array); + + return undefined; +} + +static napi_value RunPropertyKey(napi_env env, napi_callback_info info) { + napi_value property_key; + NODE_API_CALL(node_api_create_property_key_utf16( + env, u"prop", NAPI_AUTO_LENGTH, &property_key)); + return Runner(env, info, property_key); +} + +static napi_value RunNormalString(napi_env env, napi_callback_info info) { + napi_value property_key; + NODE_API_CALL( + napi_create_string_utf16(env, u"prop", NAPI_AUTO_LENGTH, &property_key)); + return Runner(env, info, property_key); +} + +NAPI_MODULE_INIT() { + napi_property_descriptor props[] = { + {"RunPropertyKey", + nullptr, + RunPropertyKey, + nullptr, + nullptr, + nullptr, + static_cast(napi_writable | napi_configurable | + napi_enumerable), + nullptr}, + {"RunNormalString", + nullptr, + RunNormalString, + 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/property_keys/binding.gyp b/benchmark/napi/property_keys/binding.gyp new file mode 100644 index 00000000000000..0006991c5d1b7c --- /dev/null +++ b/benchmark/napi/property_keys/binding.gyp @@ -0,0 +1,9 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.cc' ], + 'defines': [ 'NAPI_EXPERIMENTAL' ], + } + ] +} diff --git a/benchmark/napi/property_keys/index.js b/benchmark/napi/property_keys/index.js new file mode 100644 index 00000000000000..258c8aabdd116e --- /dev/null +++ b/benchmark/napi/property_keys/index.js @@ -0,0 +1,15 @@ +'use strict'; + +const common = require('../../common.js'); + +const binding = require(`./build/${common.buildType}/binding`); + +const bench = common.createBenchmark(main, { + n: [5e6], + implem: ['RunPropertyKey', 'RunNormalString'], +}); + +function main({ n, implem }) { + const objs = Array(n).fill(null).map((item) => new Object()); + binding[implem](bench, objs); +} diff --git a/tools/build_addons.py b/tools/build_addons.py index db948ba7b908e8..b8e36078d236cb 100755 --- a/tools/build_addons.py +++ b/tools/build_addons.py @@ -66,6 +66,7 @@ def node_gyp_rebuild(test_dir): print(stdout.decode()) if stderr: print(stderr.decode()) + return return_code except Exception as e: print(f'Unexpected error when building addon in {test_dir}. Error: {e}') @@ -86,7 +87,8 @@ def node_gyp_rebuild(test_dir): test_dirs.append(full_path) with ThreadPoolExecutor() as executor: - executor.map(node_gyp_rebuild, test_dirs) + codes = executor.map(node_gyp_rebuild, test_dirs) + return 0 if all(code == 0 for code in codes) else 1 def get_default_out_dir(args): default_out_dir = os.path.join('out', args.config) @@ -131,17 +133,19 @@ def main(): if not args.out_dir: args.out_dir = get_default_out_dir(args) + exit_code = 1 if args.headers_dir: - rebuild_addons(args) + exit_code = rebuild_addons(args) else: # When --headers-dir is not specified, generate headers into a temp dir and # build with the new headers. try: args.headers_dir = tempfile.mkdtemp() generate_headers(args.headers_dir, unknown_args) - rebuild_addons(args) + exit_code = rebuild_addons(args) finally: shutil.rmtree(args.headers_dir) + return exit_code if __name__ == '__main__': - main() + sys.exit(main())