mirror of
https://github.com/dart-lang/sdk
synced 2024-10-03 00:45:16 +00:00
Roll benchmark_harness to version 2.3.0, add new benchmark.
This relands "Add copy of ObjectHash benchmark that uses hardware performance counters"
It lands a roll of benchmark_harness to a new version without
the breaking change in the previous, reverted, roll.
It adds a copy of a benchmark that uses
the new features in benchmark_harness.
Reason for reland: The breaking change is removed from benchmark harness.
Original change's description:
> Revert "Add copy of ObjectHash benchmark that uses hardware performance counters"
>
> This reverts commit faee649175
.
>
> Reason for revert: The new version of benchmark_harness breaks cbuild.
>
> Original change's description:
> > Add copy of ObjectHash benchmark that uses hardware performance counters
> >
> > Bug: b/320440992
> > Change-Id: Ia504a59861b2de32c0b82d6bb38491600a37e9ff
> > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/363703
> > Reviewed-by: Jonas Termansen <sortie@google.com>
>
> Bug: b/320440992
> Change-Id: I19bd814c1641243ab0b75f45bc55cdfe27d4bff3
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/363740
> Auto-Submit: Alexander Thomas <athom@google.com>
> Commit-Queue: Emmanuel Pellereau <emmanuelp@google.com>
> Bot-Commit: Rubber Stamper <rubber-stamper@appspot.gserviceaccount.com>
> Commit-Queue: Ivan Inozemtsev <iinozemtsev@google.com>
> Reviewed-by: Emmanuel Pellereau <emmanuelp@google.com>
> Reviewed-by: Ivan Inozemtsev <iinozemtsev@google.com>
Bug: b/320440992
Change-Id: I101a7ca756a5da5ddf934818325177501c370f23
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/364180
Reviewed-by: Jonas Termansen <sortie@google.com>
Reviewed-by: Alexander Thomas <athom@google.com>
Commit-Queue: William Hesse <whesse@google.com>
This commit is contained in:
parent
bae278ec40
commit
1f80809e0d
2
DEPS
2
DEPS
|
@ -123,7 +123,7 @@ vars = {
|
||||||
"args_rev": "5c83bc9785d6c32ffe6824ba79fadcc51fbcd1c1",
|
"args_rev": "5c83bc9785d6c32ffe6824ba79fadcc51fbcd1c1",
|
||||||
"async_rev": "47968047eb9888f74ca0691640821bd55b47e763",
|
"async_rev": "47968047eb9888f74ca0691640821bd55b47e763",
|
||||||
"bazel_worker_rev": "79d2ad13c83d5e0883136503d86ddf60fe665900",
|
"bazel_worker_rev": "79d2ad13c83d5e0883136503d86ddf60fe665900",
|
||||||
"benchmark_harness_rev": "aa139fdf3a3b829fa8c10719102c66729495afb6",
|
"benchmark_harness_rev": "197702c2c73e58eb2d31c1fd83cc5d8096d3eceb",
|
||||||
"boolean_selector_rev": "24635df68661bb44c1c13fb405562421e24298e5",
|
"boolean_selector_rev": "24635df68661bb44c1c13fb405562421e24298e5",
|
||||||
"browser_launcher_rev": "c4b2c81aa9debcce3651eda1b68a9bc5d5adf400",
|
"browser_launcher_rev": "c4b2c81aa9debcce3651eda1b68a9bc5d5adf400",
|
||||||
"characters_rev": "7633a16a22c626e19ca750223237396315268a06",
|
"characters_rev": "7633a16a22c626e19ca750223237396315268a06",
|
||||||
|
|
256
benchmarks/ObjectHashPerf/dart/ObjectHashPerf.dart
Normal file
256
benchmarks/ObjectHashPerf/dart/ObjectHashPerf.dart
Normal file
|
@ -0,0 +1,256 @@
|
||||||
|
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
|
||||||
|
// for details. All rights reserved. Use of this source code is governed by a
|
||||||
|
// BSD-style license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// ignore_for_file: hash_and_equals
|
||||||
|
|
||||||
|
// Benchmark for `Object.hash` and `Object.hashAll`.
|
||||||
|
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
|
import 'package:benchmark_harness/perf_benchmark_harness.dart';
|
||||||
|
|
||||||
|
int get nextHash => Random().nextInt(0x20000000);
|
||||||
|
|
||||||
|
// An object with a fast hashCode.
|
||||||
|
class Leaf {
|
||||||
|
@override
|
||||||
|
final int hashCode = nextHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract class Node5 {
|
||||||
|
final item1 = Leaf();
|
||||||
|
final item2 = Leaf();
|
||||||
|
final item3 = Leaf();
|
||||||
|
final item4 = Random().nextBool();
|
||||||
|
final item5 = nextHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Node5Hash extends Node5 {
|
||||||
|
// This is the main subject of the benchmark - a typical use of `Object.hash`.
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(item1, item2, item3, item4, item5);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Node5Manual extends Node5 {
|
||||||
|
// This is a similar quality hashCode but with statically resolvable
|
||||||
|
// `hashCode` calls and a 0 seed (instead of loading a unique random seed from
|
||||||
|
// global late-final variable).
|
||||||
|
@override
|
||||||
|
int get hashCode => _SystemHash.hash5(item1.hashCode, item2.hashCode,
|
||||||
|
item3.hashCode, item4.hashCode, item5.hashCode, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Node5List extends Node5 {
|
||||||
|
// This is a pattern that is sometimes used, especially for large numbers of
|
||||||
|
// items.
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hashAll([item1, item2, item3, item4, item5]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a list with most values created by [makeValue], and a few objects of
|
||||||
|
/// different types so that the `hashCode` calls are polymorphic, like the ones
|
||||||
|
/// in the hashed collections.
|
||||||
|
List generateData(Object Function(int) makeValue) {
|
||||||
|
final List data = List.generate(1000, makeValue);
|
||||||
|
final exceptions = [
|
||||||
|
Leaf(),
|
||||||
|
Node5Hash(),
|
||||||
|
Node5Manual(),
|
||||||
|
Node5List(),
|
||||||
|
'',
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
123,
|
||||||
|
Object()
|
||||||
|
];
|
||||||
|
data.setRange(1, 1 + exceptions.length, exceptions);
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
class BenchmarkNode5Hash extends PerfBenchmarkBase {
|
||||||
|
final List data = generateData((_) => Node5Hash());
|
||||||
|
|
||||||
|
BenchmarkNode5Hash() : super('ObjectHashPerf.hash.5');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void run() {
|
||||||
|
for (final e in data) {
|
||||||
|
sink = e.hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BenchmarkNode5Manual extends PerfBenchmarkBase {
|
||||||
|
final List data = generateData((_) => Node5Manual());
|
||||||
|
|
||||||
|
BenchmarkNode5Manual() : super('ObjectHashPerf.manual.5');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void run() {
|
||||||
|
for (final e in data) {
|
||||||
|
sink = e.hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BenchmarkNode5List extends PerfBenchmarkBase {
|
||||||
|
final List data = generateData((_) => Node5List());
|
||||||
|
|
||||||
|
BenchmarkNode5List() : super('ObjectHashPerf.list.5');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void run() {
|
||||||
|
for (final e in data) {
|
||||||
|
sink = e.hashCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BenchmarkNode5HashHashAll extends PerfBenchmarkBase {
|
||||||
|
final List data = generateData((_) => Node5Hash());
|
||||||
|
|
||||||
|
BenchmarkNode5HashHashAll() : super('ObjectHashPerf.hash.5.hashAll');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void run() {
|
||||||
|
sink = Object.hashAll(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BenchmarkNode5ManualHashAll extends PerfBenchmarkBase {
|
||||||
|
final List data = generateData((_) => Node5Manual());
|
||||||
|
|
||||||
|
BenchmarkNode5ManualHashAll() : super('ObjectHashPerf.manual.5.hashAll');
|
||||||
|
|
||||||
|
@override
|
||||||
|
void run() {
|
||||||
|
sink = Object.hashAll(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object? sink;
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
generalUses();
|
||||||
|
|
||||||
|
final benchmarks = [
|
||||||
|
BenchmarkNode5Hash.new,
|
||||||
|
BenchmarkNode5Manual.new,
|
||||||
|
BenchmarkNode5List.new,
|
||||||
|
BenchmarkNode5HashHashAll.new,
|
||||||
|
BenchmarkNode5ManualHashAll.new,
|
||||||
|
];
|
||||||
|
|
||||||
|
// Warmup all benchmarks so that JIT compilers see full polymorphism before
|
||||||
|
// measuring.
|
||||||
|
for (var benchmark in benchmarks) {
|
||||||
|
benchmark().warmup();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sink == null) throw StateError('sink unassigned');
|
||||||
|
|
||||||
|
generalUses();
|
||||||
|
|
||||||
|
for (var benchmark in benchmarks) {
|
||||||
|
await benchmark().reportPerf();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Does a variety of calls to `Object.hash` to ensure the compiler does not
|
||||||
|
/// over-specialize the code on a few benchmark inputs.
|
||||||
|
void generalUses() {
|
||||||
|
void check(int a, int b) {
|
||||||
|
if (a != b) throw StateError('inconsistent');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exercise arity dispatch.
|
||||||
|
check(Object.hash(1, 2), Object.hash(1, 2));
|
||||||
|
check(Object.hash(1, 2, 3), Object.hash(1, 2, 3));
|
||||||
|
check(Object.hash(1, 2, 3, 4), Object.hash(1, 2, 3, 4));
|
||||||
|
check(Object.hash(1, 2, 3, 4, 5), Object.hash(1, 2, 3, 4, 5));
|
||||||
|
check(Object.hash(1, 2, 3, 4, 5, 6), Object.hash(1, 2, 3, 4, 5, 6));
|
||||||
|
check(Object.hash(1, 2, 3, 4, 5, 6, 7), Object.hash(1, 2, 3, 4, 5, 6, 7));
|
||||||
|
|
||||||
|
final xs = Iterable.generate(20).toList();
|
||||||
|
check(Function.apply(Object.hash, xs), Function.apply(Object.hash, xs));
|
||||||
|
|
||||||
|
// Exercise internal hashCode dispatch.
|
||||||
|
final a1 = 123;
|
||||||
|
final a2 = 'hello';
|
||||||
|
final a3 = true;
|
||||||
|
final a4 = Object();
|
||||||
|
final a5 = StringBuffer();
|
||||||
|
const a6 = Point<int>(1, 2);
|
||||||
|
const a7 = Rectangle<int>(100, 200, 1, 1);
|
||||||
|
|
||||||
|
check(Object.hash(a1, a2, a3, a4, a5), Object.hash(a1, a2, a3, a4, a5));
|
||||||
|
check(Object.hash(a2, a3, a4, a5, a6), Object.hash(a2, a3, a4, a5, a6));
|
||||||
|
check(Object.hash(a3, a4, a5, a6, a7), Object.hash(a3, a4, a5, a6, a7));
|
||||||
|
check(Object.hash(a4, a5, a6, a7, a1), Object.hash(a4, a5, a6, a7, a1));
|
||||||
|
check(Object.hash(a5, a6, a7, a1, a2), Object.hash(a5, a6, a7, a1, a2));
|
||||||
|
check(Object.hash(a6, a7, a1, a2, a3), Object.hash(a6, a7, a1, a2, a3));
|
||||||
|
check(Object.hash(a7, a1, a2, a3, a4), Object.hash(a7, a1, a2, a3, a4));
|
||||||
|
|
||||||
|
check(_SystemHash.hash2(1, 2, 0), _SystemHash.hash2(1, 2, 0));
|
||||||
|
check(_SystemHash.hash3(1, 2, 3, 0), _SystemHash.hash3(1, 2, 3, 0));
|
||||||
|
check(_SystemHash.hash4(1, 2, 3, 4, 0), _SystemHash.hash4(1, 2, 3, 4, 0));
|
||||||
|
check(
|
||||||
|
_SystemHash.hash5(1, 2, 3, 4, 5, 0), _SystemHash.hash5(1, 2, 3, 4, 5, 0));
|
||||||
|
|
||||||
|
// Pollute hashAll argument type.
|
||||||
|
check(Object.hashAll({}), Object.hashAll([]));
|
||||||
|
check(Object.hashAll({}.values), Object.hashAll({}.keys));
|
||||||
|
check(Object.hashAll(''.codeUnits), Object.hashAll(const Iterable.empty()));
|
||||||
|
check(Object.hashAll(const [0]), Object.hashAll(Iterable.generate(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial copy of dart:internal `SystemHash` that is used by `Object.hash` so
|
||||||
|
// that we can create comparable manual hashCode methods.
|
||||||
|
class _SystemHash {
|
||||||
|
static int combine(int hash, int value) {
|
||||||
|
hash = 0x1fffffff & (hash + value);
|
||||||
|
hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));
|
||||||
|
return hash ^ (hash >> 6);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int finish(int hash) {
|
||||||
|
hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));
|
||||||
|
hash = hash ^ (hash >> 11);
|
||||||
|
return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hash2(int v1, int v2, int seed) {
|
||||||
|
int hash = seed;
|
||||||
|
hash = combine(hash, v1);
|
||||||
|
hash = combine(hash, v2);
|
||||||
|
return finish(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hash3(int v1, int v2, int v3, int seed) {
|
||||||
|
int hash = seed;
|
||||||
|
hash = combine(hash, v1);
|
||||||
|
hash = combine(hash, v2);
|
||||||
|
hash = combine(hash, v3);
|
||||||
|
return finish(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hash4(int v1, int v2, int v3, int v4, int seed) {
|
||||||
|
int hash = seed;
|
||||||
|
hash = combine(hash, v1);
|
||||||
|
hash = combine(hash, v2);
|
||||||
|
hash = combine(hash, v3);
|
||||||
|
hash = combine(hash, v4);
|
||||||
|
return finish(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hash5(int v1, int v2, int v3, int v4, int v5, int seed) {
|
||||||
|
int hash = seed;
|
||||||
|
hash = combine(hash, v1);
|
||||||
|
hash = combine(hash, v2);
|
||||||
|
hash = combine(hash, v3);
|
||||||
|
hash = combine(hash, v4);
|
||||||
|
hash = combine(hash, v5);
|
||||||
|
return finish(hash);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue