1
0
mirror of https://github.com/dart-lang/sdk synced 2024-07-01 07:14:29 +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:
William Hesse 2024-04-23 15:19:49 +00:00 committed by Commit Queue
parent bae278ec40
commit 1f80809e0d
2 changed files with 257 additions and 1 deletions

2
DEPS
View File

@ -123,7 +123,7 @@ vars = {
"args_rev": "5c83bc9785d6c32ffe6824ba79fadcc51fbcd1c1",
"async_rev": "47968047eb9888f74ca0691640821bd55b47e763",
"bazel_worker_rev": "79d2ad13c83d5e0883136503d86ddf60fe665900",
"benchmark_harness_rev": "aa139fdf3a3b829fa8c10719102c66729495afb6",
"benchmark_harness_rev": "197702c2c73e58eb2d31c1fd83cc5d8096d3eceb",
"boolean_selector_rev": "24635df68661bb44c1c13fb405562421e24298e5",
"browser_launcher_rev": "c4b2c81aa9debcce3651eda1b68a9bc5d5adf400",
"characters_rev": "7633a16a22c626e19ca750223237396315268a06",

View 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);
}
}