mirror of
https://github.com/nodejs/node.git
synced 2025-05-15 08:02:06 +00:00

PR-URL: https://github.com/nodejs/node/pull/44741 Fixes: https://github.com/nodejs/node/issues/44650 Fixes: https://github.com/nodejs/node/issues/37472 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Jiawen Geng <technicalcute@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
281 lines
7.5 KiB
JavaScript
281 lines
7.5 KiB
JavaScript
// Copyright 2022 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
function ObjectWithKeys(count, keyOffset = 0, keyGen) {
|
|
var body = "";
|
|
for (var i = 0; i < count; i++) {
|
|
var key = keyGen(i + keyOffset);
|
|
if (typeof key === "string") {
|
|
body += `this.${key} = 0\n`;
|
|
} else {
|
|
body += `this[${key}] = 0\n`;
|
|
}
|
|
}
|
|
var f = new Function(body);
|
|
return new f();
|
|
}
|
|
|
|
function ObjectWithProperties(count, keyOffset) {
|
|
return ObjectWithKeys(count, keyOffset, (key) => "key" + key );
|
|
}
|
|
|
|
function ObjectWithElements(count, keyOffset) {
|
|
return ObjectWithKeys(count, keyOffset, (key) => key );
|
|
}
|
|
|
|
function ObjectWithMixedKeys(count, keyOffset) {
|
|
return ObjectWithKeys(count, keyOffset, (key) => {
|
|
if (key % 2 == 0) return (key / 2);
|
|
return "key" + ((key - 1) / 2);
|
|
});
|
|
}
|
|
|
|
// Create an object with 0 prototypes each having #keys properties
|
|
// generated by given keyGen.
|
|
function ObjectWithOwnKeys(depth, keys,
|
|
keyGen = ObjectWithProperties) {
|
|
var o = keyGen(keys);
|
|
var current = o;
|
|
var keyOffset = 0;
|
|
for (var i = 0; i < 1; i++) {
|
|
keyOffset += keys;
|
|
current.__proto__ = keyGen(keys, keyOffset);
|
|
}
|
|
return o;
|
|
}
|
|
|
|
|
|
function HoleyIntArray(size) {
|
|
var array = new Array(size);
|
|
for (var i = 0; i < size; i += 3) {
|
|
array[i] = i;
|
|
}
|
|
return array
|
|
}
|
|
|
|
function IntArray(size) {
|
|
var array = new Array(size);
|
|
for (var i = 0; i < size; i++) {
|
|
array[i] = i;
|
|
}
|
|
return array;
|
|
}
|
|
|
|
// Switch object's properties and elements to dictionary mode.
|
|
function MakeDictionaryMode(obj) {
|
|
obj.foo = 0;
|
|
obj.bar = 0;
|
|
// Delete the second-to-last property first to force normalization.
|
|
delete obj.foo;
|
|
delete obj.bar;
|
|
obj[1e9] = 0;
|
|
return obj;
|
|
}
|
|
|
|
function Internalize(s) {
|
|
return Object.keys({[s]:0})[0];
|
|
}
|
|
|
|
function Deinternalize(s) {
|
|
return [...s].join("");
|
|
}
|
|
|
|
// ============================================================================
|
|
|
|
const QUERY_INTERNALIZED_PROP = "INTERN-prop";
|
|
const QUERY_DEINTERNALIZED_PROP = "DEINTERN-prop";
|
|
const QUERY_NON_EXISTING_INTERNALIZED_PROP = "NE-INTERN-prop";
|
|
const QUERY_NON_EXISTING_DEINTERNALIZED_PROP = "NE-DEINTERN-prop";
|
|
const QUERY_ELEMENT = "el";
|
|
const QUERY_NON_EXISTING_ELEMENT = "NE-el";
|
|
|
|
const OBJ_MODE_FAST = "fast";
|
|
const OBJ_MODE_SLOW = "slow";
|
|
|
|
var TestQueries = [
|
|
QUERY_INTERNALIZED_PROP,
|
|
QUERY_DEINTERNALIZED_PROP,
|
|
QUERY_NON_EXISTING_INTERNALIZED_PROP,
|
|
QUERY_NON_EXISTING_DEINTERNALIZED_PROP,
|
|
QUERY_ELEMENT,
|
|
QUERY_NON_EXISTING_ELEMENT,
|
|
];
|
|
|
|
const QUERIES_PER_OBJECT_NUMBER = 10;
|
|
|
|
// Leave only every "count"th keys.
|
|
function FilterKeys(keys, count) {
|
|
var len = keys.length;
|
|
if (len < count) throw new Error("Keys array is too short: " + len);
|
|
var step = len / count;
|
|
if (step == 0) throw new Error("Bad count specified: " + count);
|
|
return keys.filter((element, index) => index % step == 0);
|
|
}
|
|
|
|
|
|
function MakeKeyQueries(keys, query_kind) {
|
|
var properties = keys.filter((element) => isNaN(Number(element)));
|
|
var elements = keys.filter((element) => !isNaN(Number(element)));
|
|
|
|
properties = FilterKeys(properties, QUERIES_PER_OBJECT_NUMBER);
|
|
elements = FilterKeys(elements, QUERIES_PER_OBJECT_NUMBER);
|
|
|
|
switch (query_kind) {
|
|
case QUERY_INTERNALIZED_PROP:
|
|
return properties;
|
|
|
|
case QUERY_DEINTERNALIZED_PROP:
|
|
return properties.map(Deinternalize);
|
|
|
|
case QUERY_NON_EXISTING_INTERNALIZED_PROP:
|
|
case QUERY_NON_EXISTING_DEINTERNALIZED_PROP:
|
|
var non_existing = [];
|
|
for (var i = 0; i < QUERIES_PER_OBJECT_NUMBER; i++) {
|
|
non_existing.push("non-existing" + i);
|
|
}
|
|
if (query_kind == QUERY_NON_EXISTING_INTERNALIZED_PROP) {
|
|
return non_existing.map(Internalize);
|
|
} else {
|
|
return non_existing.map(Deinternalize);
|
|
}
|
|
|
|
case QUERY_ELEMENT:
|
|
return elements.map(Number);
|
|
|
|
case QUERY_NON_EXISTING_ELEMENT:
|
|
var non_existing = [];
|
|
for (var i = 0; i < QUERIES_PER_OBJECT_NUMBER; i++) {
|
|
non_existing.push(1200 + 100*i);
|
|
}
|
|
return non_existing;
|
|
|
|
default:
|
|
throw new Error("Bad query_kind: " + query_kind);
|
|
}
|
|
}
|
|
|
|
|
|
var TestData = [];
|
|
[OBJ_MODE_FAST, OBJ_MODE_SLOW].forEach((obj_mode) => {
|
|
var name = `${obj_mode}-obj`;
|
|
var objects = [];
|
|
[10, 50, 100, 200, 500].forEach((prop_count) => {
|
|
// Create object with prop_count properties and prop_count elements.
|
|
obj = ObjectWithOwnKeys(5, prop_count * 2, ObjectWithMixedKeys);
|
|
if (obj_mode == OBJ_MODE_SLOW) {
|
|
obj = MakeDictionaryMode(obj);
|
|
}
|
|
objects.push(obj);
|
|
});
|
|
TestData.push({name, objects});
|
|
});
|
|
|
|
|
|
// ============================================================================
|
|
|
|
function CreateTestFunction(testMethod, object, keys) {
|
|
// Force a new function for each test-object to avoid side-effects due to ICs.
|
|
var text = `
|
|
// random comment ${Math.random()}
|
|
let results = [];
|
|
for (var key in keys) {
|
|
results.push(${testMethod}(object, key));
|
|
}
|
|
return results;`;
|
|
|
|
var func = new Function("object", "keys", text);
|
|
return () => func(object, keys);
|
|
}
|
|
|
|
function CombineTestFunctions(tests) {
|
|
return () => {
|
|
for (var i = 0; i < tests.length; i++ ) {
|
|
let results = tests[i]();
|
|
results.push(1);
|
|
globalThis.results = results;
|
|
}
|
|
};
|
|
}
|
|
|
|
var TestMethods = {
|
|
"ObjectGetOwnPropertyDescriptor": "Object.getOwnPropertyDescriptor",
|
|
"ReflectGetOwnPropertyDescriptor": "Reflect.getOwnPropertyDescriptor",
|
|
};
|
|
|
|
var TestTargets = {
|
|
"object": {
|
|
target(object) {
|
|
return object;
|
|
}
|
|
},
|
|
|
|
"proxy-no-trap": {
|
|
target(object) {
|
|
return new Proxy(object, {});
|
|
},
|
|
},
|
|
|
|
"proxy-reflect": {
|
|
target(object) {
|
|
return new Proxy(object, {
|
|
getOwnPropertyDescriptor(target, propertyKey) {
|
|
return Reflect.getOwnPropertyDescriptor(target, propertyKey);
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
"proxy-proxy-no-trap": {
|
|
target(object) {
|
|
let proxy = this["proxy-no-trap"].target(object);
|
|
return new Proxy(proxy, {});
|
|
}
|
|
},
|
|
|
|
"proxy-proxy-reflect": {
|
|
target(object) {
|
|
let proxy = this["proxy-no-trap"].target(object);
|
|
return new Proxy(object, {
|
|
getOwnPropertyDescriptor(target, propertyKey) {
|
|
return Reflect.getOwnPropertyDescriptor(target, propertyKey);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
|
|
// ============================================================================
|
|
// Create the benchmark suites. We create a suite for each pair of the test
|
|
// functions above and query kind. Each suite contains benchmarks for each
|
|
// object type.
|
|
var Benchmarks = [];
|
|
|
|
for (var [test_method_name, test_method_entry] of Object.entries(TestMethods)) {
|
|
for (var [test_target_name, test_target] of Object.entries(TestTargets)) {
|
|
for (var query_kind of TestQueries) {
|
|
var benchmarks = [];
|
|
var suit_name = `${test_method_name}--${test_target_name}--${query_kind}`;
|
|
for (var test_data of TestData) {
|
|
var name = `${suit_name}--${test_data.name}`;
|
|
|
|
var tests = [];
|
|
for (var object of test_data.objects) {
|
|
var keys = Object.getOwnPropertyNames(object);
|
|
keys = MakeKeyQueries(keys, query_kind);
|
|
|
|
var test = CreateTestFunction(test_method_entry, object, keys);
|
|
tests.push(test);
|
|
}
|
|
var run_function = CombineTestFunctions(tests);
|
|
var benchmark = new Benchmark(name, false, false, 0, run_function);
|
|
benchmarks.push(benchmark);
|
|
}
|
|
Benchmarks.push(new BenchmarkSuite(suit_name, [100], benchmarks));
|
|
}
|
|
}
|
|
}
|
|
|
|
// ============================================================================
|