7ed989817a
- Add tests comparing super property access to normal property access - Shift the work so that the framework takes less time and the thing we're trying to measure takes more time. - Optimize / disable the optimization for the target function, not the whole test framework. - Reduce the amount of boilerplate code in the tests. Bug: v8:9237 Change-Id: Idde133298c9b8ffb3d49945ef9c67f5039634598 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2536635 Reviewed-by: Shu-yu Guo <syg@chromium.org> Commit-Queue: Marja Hölttä <marja@chromium.org> Cr-Commit-Position: refs/heads/master@{#71196}
244 lines
8.5 KiB
JavaScript
244 lines
8.5 KiB
JavaScript
// Copyright 2020 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.
|
|
|
|
load('../base.js');
|
|
|
|
const BENCHMARK_NAME = arguments[0];
|
|
const TEST_TYPE = arguments[1];
|
|
const optimize_param = arguments[2];
|
|
let optimize;
|
|
if (optimize_param == "opt") {
|
|
optimize = true;
|
|
} else if (optimize_param == "noopt"){
|
|
optimize = false;
|
|
} else {
|
|
throw new Error("Test configuration error");
|
|
}
|
|
|
|
const DETERMINISTIC_RUNS = 1;
|
|
const LOCAL_ITERATIONS = 10000;
|
|
new BenchmarkSuite(BENCHMARK_NAME, [1000], [
|
|
new Benchmark(BENCHMARK_NAME, false, false, DETERMINISTIC_RUNS, runBenchmark)
|
|
]);
|
|
|
|
// Classes for monomorphic super property access.
|
|
class A { };
|
|
A.prototype.super_prop_a0 = 10;
|
|
A.prototype.super_prop_a1 = 10;
|
|
A.prototype.super_prop_a2 = 10;
|
|
A.prototype.super_prop_a3 = 10;
|
|
A.prototype.super_prop_a4 = 10;
|
|
A.prototype.super_prop_a5 = 10;
|
|
A.prototype.super_prop_a6 = 10;
|
|
A.prototype.super_prop_a7 = 10;
|
|
A.prototype.super_prop_a8 = 10;
|
|
A.prototype.super_prop_a9 = 10;
|
|
|
|
class B extends A { }
|
|
B.prototype.super_prop_b0 = 10;
|
|
B.prototype.super_prop_b1 = 10;
|
|
B.prototype.super_prop_b2 = 10;
|
|
B.prototype.super_prop_b3 = 10;
|
|
B.prototype.super_prop_b4 = 10;
|
|
B.prototype.super_prop_b5 = 10;
|
|
B.prototype.super_prop_b6 = 10;
|
|
B.prototype.super_prop_b7 = 10;
|
|
B.prototype.super_prop_b8 = 10;
|
|
B.prototype.super_prop_b9 = 10;
|
|
|
|
class C extends B {
|
|
test_super_a(unused) {
|
|
// Do many property accesses to increase the time taken by the thing we want
|
|
// to measure, in comparison to the overhead of calling this function.
|
|
return (super.super_prop_a0 + super.super_prop_a1 + super.super_prop_a2 +
|
|
super.super_prop_a3 + super.super_prop_a4 + super.super_prop_a5 +
|
|
super.super_prop_a6 + super.super_prop_a7 + super.super_prop_a8 +
|
|
super.super_prop_a9);
|
|
}
|
|
test_super_b(unused) {
|
|
return (super.super_prop_b0 + super.super_prop_b1 + super.super_prop_b2 +
|
|
super.super_prop_b3 + super.super_prop_b4 + super.super_prop_b5 +
|
|
super.super_prop_b6 + super.super_prop_b7 + super.super_prop_b8 +
|
|
super.super_prop_b9);
|
|
}
|
|
};
|
|
|
|
// Classes for megamorphic super property access.
|
|
function createClasses(base) {
|
|
class B extends base { };
|
|
B.prototype.super_prop_b0 = 10;
|
|
B.prototype.super_prop_b1 = 10;
|
|
B.prototype.super_prop_b2 = 10;
|
|
B.prototype.super_prop_b3 = 10;
|
|
B.prototype.super_prop_b4 = 10;
|
|
B.prototype.super_prop_b5 = 10;
|
|
B.prototype.super_prop_b6 = 10;
|
|
B.prototype.super_prop_b7 = 10;
|
|
B.prototype.super_prop_b8 = 10;
|
|
B.prototype.super_prop_b9 = 10;
|
|
|
|
class C extends B {
|
|
test_super_a(unused) {
|
|
return (super.super_prop_a0 + super.super_prop_a1 + super.super_prop_a2 +
|
|
super.super_prop_a3 + super.super_prop_a4 + super.super_prop_a5 +
|
|
super.super_prop_a6 + super.super_prop_a7 + super.super_prop_a8 +
|
|
super.super_prop_a9);
|
|
}
|
|
test_super_b(unused) {
|
|
return (super.super_prop_b0 + super.super_prop_b1 + super.super_prop_b2 +
|
|
super.super_prop_b3 + super.super_prop_b4 + super.super_prop_b5 +
|
|
super.super_prop_b6 + super.super_prop_b7 + super.super_prop_b8 +
|
|
super.super_prop_b9);
|
|
}
|
|
}
|
|
return C;
|
|
}
|
|
|
|
function test_property_access(o) {
|
|
return (o.prop0 + o.prop1 + o.prop2 + o.prop3 + o.prop4 + o.prop5 + o.prop6 +
|
|
o.prop7 + o.prop8 + o.prop9);
|
|
}
|
|
|
|
// Set up "objects" and "tested_functions" based on which test we're running.
|
|
let objects;
|
|
let tested_functions;
|
|
|
|
switch (TEST_TYPE) {
|
|
case "super_1":
|
|
// Monomorphic super property lookup, property is a constant, found 1 step
|
|
// above the lookup start object in the prototype chain.
|
|
objects = [new C()];
|
|
tested_functions = [C.prototype.test_super_a];
|
|
break;
|
|
case "normal_1":
|
|
// Monomorphic normal property lookup, property is a constant, found 1 step
|
|
// above the lookup start object in the prototype chain.
|
|
objects = [{__proto__: {"prop0": 10, "prop1": 10, "prop2": 10, "prop3": 10,
|
|
"prop4": 10, "prop5": 10, "prop6": 10, "prop7": 10,
|
|
"prop8": 10, "prop9": 10}}];
|
|
tested_functions = [test_property_access];
|
|
break;
|
|
case "super_2":
|
|
// Monomorphic super property lookup, property is a constant, found in the
|
|
// lookup start object.
|
|
objects = [new C()];
|
|
tested_functions = [C.prototype.test_super_b];
|
|
break;
|
|
case "normal_2":
|
|
// Monomorphic normal property lookup, property is a constant, found in the
|
|
// lookup start object.
|
|
objects = [{"prop0": 10, "prop1": 10, "prop2": 10, "prop3": 10, "prop4": 10,
|
|
"prop5": 10, "prop6": 10, "prop7": 10, "prop8": 10,
|
|
"prop9": 10}];
|
|
tested_functions = [test_property_access];
|
|
break;
|
|
case "super_3":
|
|
// Megamorphic super property lookup, property is a constant, found 1 step
|
|
// above the lookup start object in the prototype chain. The holder is
|
|
// the same in all cases though.
|
|
objects = [];
|
|
tested_functions = [];
|
|
for (let i = 0; i < 5; ++i) {
|
|
const c = createClasses(A);
|
|
objects.push(new c());
|
|
tested_functions.push(c.prototype.test_super_a);
|
|
}
|
|
break;
|
|
case "normal_3":
|
|
// Megamorphic normal property lookup, property is a constant, found 1 step
|
|
// above the lookup start object in the prototype chain. The holder is
|
|
// the same in all cases though.
|
|
const proto = {"prop0": 10, "prop1": 10, "prop2": 10, "prop3": 10,
|
|
"prop4": 10, "prop5": 10, "prop6": 10, "prop7": 10,
|
|
"prop8": 10, "prop9": 10};
|
|
objects = [{__proto__: proto}, {__proto__: proto, "a": 1},
|
|
{__proto__: proto, "a": 1, "b": 1},
|
|
{__proto__: proto, "a": 1, "b": 1, "c": 1},
|
|
{__proto__: proto, "a": 1, "b": 1, "c": 1, "d": 1}];
|
|
tested_functions = [test_property_access, test_property_access,
|
|
test_property_access, test_property_access,
|
|
test_property_access];
|
|
break;
|
|
case "super_4":
|
|
// Megamorphic super property lookup, property is a constant, found in the
|
|
// lookup start object. The holder is always a different object.
|
|
objects = [];
|
|
tested_functions = [];
|
|
for (let i = 0; i < 5; ++i) {
|
|
const c = createClasses(A);
|
|
objects.push(new c());
|
|
tested_functions.push(c.prototype.test_super_b);
|
|
}
|
|
break;
|
|
case "normal_4":
|
|
// Megamorphic normal property lookup, property is a constant, found in the
|
|
// lookup start object. The holder is always a different object.
|
|
objects = [{"prop0": 10, "prop1": 10, "prop2": 10, "prop3": 10, "prop4": 10,
|
|
"prop5": 10, "prop6": 10, "prop7": 10, "prop8": 10,
|
|
"prop9": 10},
|
|
{"a": 0, "prop0": 10, "prop1": 10, "prop2": 10, "prop3": 10,
|
|
"prop4": 10, "prop5": 10, "prop6": 10, "prop7": 10, "prop8": 10,
|
|
"prop9": 10},
|
|
{"a": 0, "b": 0, "prop0": 10, "prop1": 10, "prop2": 10,
|
|
"prop3": 10, "prop4": 10, "prop5": 10, "prop6": 10, "prop7": 10,
|
|
"prop8": 10, "prop9": 10},
|
|
{"a": 0, "b": 0, "c": 0, "prop0": 10, "prop1": 10, "prop2": 10,
|
|
"prop3": 10, "prop4": 10, "prop5": 10, "prop6": 10, "prop7": 10,
|
|
"prop8": 10, "prop9": 10},
|
|
{"a": 0, "b": 0, "c": 0, "d": 0, "prop0": 10, "prop1": 10,
|
|
"prop2": 10, "prop3": 10, "prop4": 10, "prop5": 10, "prop6": 10,
|
|
"prop7": 10, "prop8": 10, "prop9": 10}];
|
|
|
|
tested_functions = [test_property_access, test_property_access,
|
|
test_property_access, test_property_access,
|
|
test_property_access];
|
|
break;
|
|
default:
|
|
throw new Error("Test configuration error");
|
|
}
|
|
|
|
for (f of tested_functions) {
|
|
if (optimize) {
|
|
%PrepareFunctionForOptimization(f);
|
|
} else {
|
|
%NeverOptimizeFunction(f);
|
|
}
|
|
}
|
|
|
|
function runBenchmark() {
|
|
const expected_value = 10 * 10;
|
|
let ix = 0;
|
|
for (let i = 0; i < LOCAL_ITERATIONS; ++i) {
|
|
const object = objects[ix];
|
|
const r = tested_functions[ix].call(object, object);
|
|
if (r != expected_value) {
|
|
throw new Error("Test error");
|
|
}
|
|
if (++ix == objects.length) {
|
|
ix = 0;
|
|
if (optimize) {
|
|
for (f of tested_functions) {
|
|
%OptimizeFunctionOnNextCall(f);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var success = true;
|
|
|
|
function PrintResult(name, result) {
|
|
print(name + '(Score): ' + result);
|
|
}
|
|
|
|
function PrintError(name, error) {
|
|
PrintResult(name, error);
|
|
success = false;
|
|
}
|
|
|
|
BenchmarkSuite.config.doWarmup = false;
|
|
BenchmarkSuite.config.doDeterministic = true;
|
|
|
|
BenchmarkSuite.RunSuites({NotifyResult: PrintResult, NotifyError: PrintError});
|