From b8e8f99d86e67a740ba9a45dc4401ce3d504af28 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Wed, 24 Jul 2024 00:37:48 +0100 Subject: [PATCH 1/2] node-api: add property keys benchmark --- benchmark/napi/property_keys/.gitignore | 1 + benchmark/napi/property_keys/binding.c | 108 +++++++++++++++++++++++ benchmark/napi/property_keys/binding.gyp | 9 ++ benchmark/napi/property_keys/index.js | 15 ++++ 4 files changed, 133 insertions(+) create mode 100644 benchmark/napi/property_keys/.gitignore create mode 100644 benchmark/napi/property_keys/binding.c 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.c b/benchmark/napi/property_keys/binding.c new file mode 100644 index 00000000000000..be468893b99259 --- /dev/null +++ b/benchmark/napi/property_keys/binding.c @@ -0,0 +1,108 @@ +#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, NULL, NULL)); + 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 = malloc(array_length * sizeof(*native_array)); + 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, NULL, NULL); + + 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, NULL)); + + 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, "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, "prop", NAPI_AUTO_LENGTH, &property_key)); + return Runner(env, info, property_key); +} + +NAPI_MODULE_INIT() { + napi_property_descriptor props[] = { + {"RunPropertyKey", + NULL, + RunPropertyKey, + NULL, + NULL, + NULL, + napi_writable | napi_configurable | napi_enumerable, + NULL}, + {"RunNormalString", + NULL, + RunNormalString, + NULL, + NULL, + NULL, + napi_writable | napi_configurable | napi_enumerable, + NULL}, + }; + + 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..4c5e72ebd1cca9 --- /dev/null +++ b/benchmark/napi/property_keys/binding.gyp @@ -0,0 +1,9 @@ +{ + 'targets': [ + { + 'target_name': 'binding', + 'sources': [ 'binding.c' ], + '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); +} From 09bfb10f747430cbd96fec353cbcb99c59a470c7 Mon Sep 17 00:00:00 2001 From: Chengzhong Wu Date: Sun, 28 Jul 2024 00:27:46 +0100 Subject: [PATCH 2/2] fixup! node-api: add property keys benchmark --- .../property_keys/{binding.c => binding.cc} | 39 ++++++++++--------- benchmark/napi/property_keys/binding.gyp | 2 +- tools/build_addons.py | 12 ++++-- 3 files changed, 30 insertions(+), 23 deletions(-) rename benchmark/napi/property_keys/{binding.c => binding.cc} (79%) diff --git a/benchmark/napi/property_keys/binding.c b/benchmark/napi/property_keys/binding.cc similarity index 79% rename from benchmark/napi/property_keys/binding.c rename to benchmark/napi/property_keys/binding.cc index be468893b99259..258193f4b356a7 100644 --- a/benchmark/napi/property_keys/binding.c +++ b/benchmark/napi/property_keys/binding.cc @@ -28,7 +28,7 @@ static napi_value Runner(napi_env env, napi_value* native_array; // Validate params and retrieve start and end function. - NODE_API_CALL(napi_get_cb_info(env, info, &argc, argv, NULL, NULL)); + 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); @@ -46,13 +46,14 @@ static napi_value Runner(napi_env env, NODE_API_CALL(napi_create_uint32(env, array_length, &js_array_length)); // Copy objects into a native array. - native_array = malloc(array_length * sizeof(*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, NULL, NULL); + napi_call_function(env, argv[0], start, 0, nullptr, nullptr); for (uint32_t idx = 0; idx < array_length; idx++) { NODE_API_CALL( @@ -61,7 +62,7 @@ static napi_value Runner(napi_env env, // Conclude the benchmark. NODE_API_CALL( - napi_call_function(env, argv[0], end, 1, &js_array_length, NULL)); + napi_call_function(env, argv[0], end, 1, &js_array_length, nullptr)); free(native_array); @@ -71,35 +72,37 @@ static napi_value Runner(napi_env env, 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, "prop", NAPI_AUTO_LENGTH, &property_key)); + 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, "prop", NAPI_AUTO_LENGTH, &property_key)); + 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", - NULL, + nullptr, RunPropertyKey, - NULL, - NULL, - NULL, - napi_writable | napi_configurable | napi_enumerable, - NULL}, + nullptr, + nullptr, + nullptr, + static_cast(napi_writable | napi_configurable | + napi_enumerable), + nullptr}, {"RunNormalString", - NULL, + nullptr, RunNormalString, - NULL, - NULL, - NULL, - napi_writable | napi_configurable | napi_enumerable, - NULL}, + nullptr, + nullptr, + nullptr, + static_cast(napi_writable | napi_configurable | + napi_enumerable), + nullptr}, }; NODE_API_CALL(napi_define_properties( diff --git a/benchmark/napi/property_keys/binding.gyp b/benchmark/napi/property_keys/binding.gyp index 4c5e72ebd1cca9..0006991c5d1b7c 100644 --- a/benchmark/napi/property_keys/binding.gyp +++ b/benchmark/napi/property_keys/binding.gyp @@ -2,7 +2,7 @@ 'targets': [ { 'target_name': 'binding', - 'sources': [ 'binding.c' ], + 'sources': [ 'binding.cc' ], 'defines': [ 'NAPI_EXPERIMENTAL' ], } ] 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())