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