blob: f763d262d4ebec8a97072070d078b5f24163156a [file] [log] [blame]
Ben Murdochc5610432016-08-08 18:44:38 +01001// Copyright 2016 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5function ObjectWithKeys(count, keyOffset = 0, keyGen) {
6 var body = "";
7 for (var i = 0; i < count; i++) {
8 var key = keyGen(i + keyOffset);
9 if (typeof key === "string") {
10 body += `this.${key} = 0\n`;
11 } else {
12 body += `this[${key}] = 0\n`;
13 }
14 }
15 var f = new Function(body);
16 return new f();
17}
18
19function ObjectWithProperties(count, keyOffset) {
20 return ObjectWithKeys(count, keyOffset, (key) => "key" + key );
21}
22
23function ObjectWithElements(count, keyOffset) {
24 return ObjectWithKeys(count, keyOffset, (key) => key );
25}
26
27function ObjectWithMixedKeys(count, keyOffset) {
28 return ObjectWithKeys(count, keyOffset, (key) => {
29 if (key % 2 == 0) return (key / 2);
30 return "key" + ((key - 1) / 2);
31 });
32}
33
34// Create an object with #depth prototypes each having #keys properties
35// generated by given keyGen.
36function ObjectWithProtoKeys(depth, keys, cacheable,
37 keyGen = ObjectWithProperties) {
38 var o = keyGen(keys);
39 var current = o;
40 var keyOffset = 0;
41 for (var i = 0; i < depth; i++) {
42 keyOffset += keys;
43 current.__proto__ = keyGen(keys, keyOffset);
44 current = current.__proto__;
45 }
46 if (cacheable === false) {
47 // Add an empty proxy at the prototype chain to make caching properties
48 // impossible.
49 current.__proto__ = new Proxy({}, {});
50 }
51 return o;
52}
53
54
55function HoleyIntArray(size) {
56 var array = new Array(size);
57 for (var i = 0; i < size; i += 3) {
58 array[i] = i;
59 }
60 return array
61}
62
63function IntArray(size) {
64 var array = new Array(size);
65 for (var i = 0; i < size; i++) {
66 array[i] = i;
67 }
68 return array;
69}
70
71// Switch object's properties and elements to dictionary mode.
72function MakeDictionaryMode(obj) {
73 obj.foo = 0;
74 delete obj.foo;
75 obj[1e9] = 0;
76 return obj;
77}
78
79function Internalize(s) {
80 return Object.keys({[s]:0})[0];
81}
82
83function Deinternalize(s) {
84 return [...s].join("");
85}
86
87// ============================================================================
88
89const QUERY_INTERNALIZED_PROP = "INTERN-prop";
90const QUERY_DEINTERNALIZED_PROP = "DEINTERN-prop";
91const QUERY_NON_EXISTING_INTERNALIZED_PROP = "NE-INTERN-prop";
92const QUERY_NON_EXISTING_DEINTERNALIZED_PROP = "NE-DEINTERN-prop";
93const QUERY_ELEMENT = "el";
94const QUERY_ELEMENT_AS_STRING = "el-str";
95const QUERY_NON_EXISTING_ELEMENT = "NE-el";
96
97const OBJ_MODE_FAST = "fast";
98const OBJ_MODE_SLOW = "slow";
99
100var TestQueries = [
101 QUERY_INTERNALIZED_PROP,
102 QUERY_DEINTERNALIZED_PROP,
103 QUERY_NON_EXISTING_INTERNALIZED_PROP,
104 QUERY_NON_EXISTING_DEINTERNALIZED_PROP,
105 QUERY_ELEMENT,
106 QUERY_ELEMENT_AS_STRING,
107 QUERY_NON_EXISTING_ELEMENT,
108];
109
110const QUERIES_PER_OBJECT_NUMBER = 10;
111
112// Leave only every "count"th keys.
113function FilterKeys(keys, count) {
114 var len = keys.length;
115 if (len < count) throw new Error("Keys array is too short: " + len);
116 var step = len / count;
117 if (step == 0) throw new Error("Bad count specified: " + count);
118 return keys.filter((element, index) => index % step == 0);
119}
120
121
122function MakeKeyQueries(keys, query_kind) {
123 var properties = keys.filter((element) => isNaN(Number(element)));
124 var elements = keys.filter((element) => !isNaN(Number(element)));
125
126 properties = FilterKeys(properties, QUERIES_PER_OBJECT_NUMBER);
127 elements = FilterKeys(elements, QUERIES_PER_OBJECT_NUMBER);
128
129 switch (query_kind) {
130 case QUERY_INTERNALIZED_PROP:
131 return properties;
132
133 case QUERY_DEINTERNALIZED_PROP:
134 return properties.map(Deinternalize);
135
136 case QUERY_NON_EXISTING_INTERNALIZED_PROP:
137 case QUERY_NON_EXISTING_DEINTERNALIZED_PROP:
138 var non_existing = [];
139 for (var i = 0; i < QUERIES_PER_OBJECT_NUMBER; i++) {
140 non_existing.push("non-existing" + i);
141 }
142 if (query_kind == QUERY_NON_EXISTING_INTERNALIZED_PROP) {
143 return non_existing.map(Internalize);
144 } else {
145 return non_existing.map(Deinternalize);
146 }
147
148 case QUERY_ELEMENT:
149 return elements.map(Number);
150
151 case QUERY_ELEMENT_AS_STRING:
152 return elements.map(String);
153
154 case QUERY_NON_EXISTING_ELEMENT:
155 var non_existing = [];
156 for (var i = 0; i < QUERIES_PER_OBJECT_NUMBER; i++) {
157 non_existing.push(1200 + 100*i);
158 }
159 return non_existing;
160
161 default:
162 throw new Error("Bad query_kind: " + query_kind);
163 }
164}
165
166
167var TestData = [];
168
169[true, false].forEach((cachable) => {
170 [OBJ_MODE_FAST, OBJ_MODE_SLOW].forEach((obj_mode) => {
171 var proto_mode = cachable ? "" : "-with-slow-proto";
172 var name = `${obj_mode}-obj${proto_mode}`;
173 var objects = [];
174 [10, 50, 100, 200, 500].forEach((prop_count) => {
175 // Create object with prop_count properties and prop_count elements.
176 obj = ObjectWithProtoKeys(5, prop_count * 2, cachable,
177 ObjectWithMixedKeys);
178 if (obj_mode == OBJ_MODE_SLOW) {
179 obj = MakeDictionaryMode(obj);
180 }
181 objects.push(obj);
182 });
183 TestData.push({name, objects});
184 });
185});
186
187
188// ============================================================================
189
190function CreateTestFunction(template, object, keys) {
191 // Force a new function for each test-object to avoid side-effects due to ICs.
192 var text = "// random comment " + Math.random() + "\n" +
193 template(object, keys);
194 var func = new Function("object", "keys", text);
195 return () => func(object, keys);
196}
197
198function CombineTestFunctions(tests) {
199 return () => {
200 for (var i = 0; i < tests.length; i++ ) {
201 tests[i]();
202 }
203 };
204}
205
206var TestFunctions = [
207 {
208 name: "in",
209 // Query all keys.
210 keys: (object) => Object.keys(object),
211 template: (object, keys) => {
212 var lines = [
213 `var result = true;`,
214 `for (var i = 0; i < keys.length; i++) {`,
215 ` var key = keys[i];`,
216 ` result = (key in object) && result;`,
217 `}`,
218 `return result;`,
219 ];
220 return lines.join("\n");
221 },
222 },
223 {
224 name: "Object.hasOwnProperty",
225 // Query only own keys.
226 keys: (object) => Object.getOwnPropertyNames(object),
227 template: (object, keys) => {
228 var lines = [
229 `var result = true;`,
230 `for (var i = 0; i < keys.length; i++) {`,
231 ` var key = keys[i];`,
232 ` result = object.hasOwnProperty(key) && result;`,
233 `}`,
234 `return result;`,
235 ];
236 return lines.join("\n");
237 },
238 },
239];
240
241
242// ============================================================================
243// Create the benchmark suites. We create a suite for each pair of the test
244// functions above and query kind. Each suite contains benchmarks for each
245// object type.
246var Benchmarks = [];
247
248for (var test_function_desc of TestFunctions) {
249 var test_function_name = test_function_desc.name;
250
251 for (var query_kind of TestQueries) {
252 var benchmarks = [];
253 var suit_name = test_function_name + "--" + query_kind;
254 for (var test_data of TestData) {
255 var name = suit_name + "--" + test_data.name;
256
257 var tests = [];
258 for (var object of test_data.objects) {
259 var keys = test_function_desc.keys(object);
260 keys = MakeKeyQueries(keys, query_kind);
261
262 var test = CreateTestFunction(test_function_desc.template, object,
263 keys);
264 tests.push(test);
265 }
266 var run_function = CombineTestFunctions(tests);
267 var benchmark = new Benchmark(name, false, false, 0, run_function);
268 benchmarks.push(benchmark);
269 }
270 Benchmarks.push(new BenchmarkSuite(suit_name, [100], benchmarks));
271 }
272}
273
274// ============================================================================