| Index: test/js-perf-test/PropertyQueries/property-queries.js
|
| diff --git a/test/js-perf-test/PropertyQueries/property-queries.js b/test/js-perf-test/PropertyQueries/property-queries.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8c5498bd6569a55b8bec84c7beca8173ba03fe43
|
| --- /dev/null
|
| +++ b/test/js-perf-test/PropertyQueries/property-queries.js
|
| @@ -0,0 +1,260 @@
|
| +// Copyright 2016 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 o = {};
|
| + for (var i = 0; i < count; i++) {
|
| + var key = keyGen(i + keyOffset);
|
| + o[key] = "value";
|
| + }
|
| + return o;
|
| +}
|
| +
|
| +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 #depth prototypes each having #keys properties
|
| +// generated by given keyGen.
|
| +function ObjectWithProtoKeys(depth, keys, cacheable,
|
| + keyGen = ObjectWithProperties) {
|
| + var o = keyGen(keys);
|
| + var current = o;
|
| + var keyOffset = 0;
|
| + for (var i = 0; i < depth; i++) {
|
| + keyOffset += keys;
|
| + current.__proto__ = keyGen(keys, keyOffset);
|
| + current = current.__proto__;
|
| + }
|
| + if (cacheable === false) {
|
| + // Add an empty proxy at the prototype chain to make caching properties
|
| + // impossible.
|
| + current.__proto__ = new Proxy({}, {});
|
| + }
|
| + 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;
|
| + delete obj.foo;
|
| + obj[1e9] = 0;
|
| + return obj;
|
| +}
|
| +
|
| +function Internalize(s) {
|
| + return Object.keys({[s]:0})[0];
|
| +}
|
| +
|
| +function Deinternalize(s) {
|
| + return [...s].join("");
|
| +}
|
| +
|
| +// ============================================================================
|
| +
|
| +const QUERY_INTERNALIZED_PROP = "query-internalized-prop";
|
| +const QUERY_DEINTERNALIZED_PROP = "query-deinternalized-prop";
|
| +const QUERY_NON_EXISTING_INTERNALIZED_PROP =
|
| + "query-non-existing-internalized-prop";
|
| +const QUERY_NON_EXISTING_DEINTERNALIZED_PROP =
|
| + "query-non-existing-deinternalized-prop";
|
| +const QUERY_ELEMENT = "query-element";
|
| +const QUERY_ELEMENT_AS_STRING = "query-element-as-string";
|
| +const QUERY_NON_EXISTING_ELEMENT = "query-non-existing-element";
|
| +
|
| +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_ELEMENT_AS_STRING,
|
| + 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_ELEMENT_AS_STRING:
|
| + return elements.map(String);
|
| +
|
| + 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 = [];
|
| +
|
| +[true, false].forEach((cachable) => {
|
| + [OBJ_MODE_FAST, OBJ_MODE_SLOW].forEach((obj_mode) => {
|
| + var proto_mode = cachable ? "" : "-with-slow-proto";
|
| + var name = `${obj_mode}-obj${proto_mode}`;
|
| + var objects = [];
|
| + [10, 50, 100, 200, 500, 1000].forEach((prop_count) => {
|
| + // Create object with prop_count properties and prop_count elements.
|
| + obj = ObjectWithProtoKeys(5, prop_count * 2, cachable,
|
| + ObjectWithMixedKeys);
|
| + if (obj_mode == OBJ_MODE_SLOW) {
|
| + obj = MakeDictionaryMode(obj);
|
| + }
|
| + objects.push(obj);
|
| + });
|
| + TestData.push({name, objects});
|
| + });
|
| +});
|
| +
|
| +
|
| +// ============================================================================
|
| +
|
| +function CreateTestFunction(template, object, keys) {
|
| + // Force a new function for each test-object to avoid side-effects due to ICs.
|
| + var text = "// random comment " + Math.random() + "\n" +
|
| + template(object, keys);
|
| + var func = new Function("object", "keys", text);
|
| + return () => func(object, keys);
|
| +}
|
| +
|
| +var TestFunctions = [
|
| + {
|
| + name: "in",
|
| + // Query all keys.
|
| + keys: (object) => Object.keys(object),
|
| + template: (object, keys) => {
|
| + var lines = [
|
| + `var result = true;`,
|
| + `for (var i = 0; i < keys.length; i++) {`,
|
| + ` var key = keys[i];`,
|
| + ` result = (key in object) && result;`,
|
| + `}`,
|
| + `return result;`,
|
| + ];
|
| + return lines.join("\n");
|
| + },
|
| + },
|
| + {
|
| + name: "Object.hasOwnProperty",
|
| + // Query only own keys.
|
| + keys: (object) => Object.getOwnPropertyNames(object),
|
| + template: (object, keys) => {
|
| + var lines = [
|
| + `var result = true;`,
|
| + `for (var i = 0; i < keys.length; i++) {`,
|
| + ` var key = keys[i];`,
|
| + ` result = object.hasOwnProperty(key) && result;`,
|
| + `}`,
|
| + `return result;`,
|
| + ];
|
| + return lines.join("\n");
|
| + },
|
| + },
|
| +];
|
| +
|
| +
|
| +// ============================================================================
|
| +// 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_function_desc of TestFunctions) {
|
| + var test_function_name = test_function_desc.name;
|
| +
|
| + for (var query_kind of TestQueries) {
|
| + for (var test_data of TestData) {
|
| + var benchmarks = [];
|
| + var name = test_function_name + "--" + query_kind + "--" + test_data.name;
|
| +
|
| + for (var object of test_data.objects) {
|
| + var keys = test_function_desc.keys(object);
|
| + keys = MakeKeyQueries(keys, query_kind);
|
| +
|
| + var test_function = CreateTestFunction(test_function_desc.template,
|
| + object, keys);
|
| +
|
| + var benchmark = new Benchmark(name, false, true, 400, test_function);
|
| + benchmarks.push(benchmark);
|
| + }
|
| + Benchmarks.push(new BenchmarkSuite(name, [100], benchmarks));
|
| + }
|
| + }
|
| +}
|
| +
|
| +// ============================================================================
|
|
|