Files
sdk/benchmarks/OmnibusDeferred/dart/OmnibusDeferred.dart
Modestas Valauskas b74add4813 [vm/compiler] Specialize Int32x4 operators in AOT.
The five binary operators on Int32x4 (+, -, |, &, ^) were never added
to the recognized-method list as graph intrinsics, so calls to them
were left as runtime calls through external-name bodies. In JIT the
call specializer picked kInt32x4Cid from IC feedback and emitted a
native SimdOpInstr, but AOT has no IC feedback and therefore fell back
to boxed calls, making Int32x4List inner loops 10-70x slower than both
the JIT version and a hand-written scalar equivalent.

This CL wires the same specialization paths that already exist for
Float32x4.+,-,*,/:
  - recognize the five operators as graph intrinsics and mark them
    with `@pragma("vm:recognized", "graph-intrinsic")` plus an
    exact-result-type pragma;
  - add Build_Int32x4{Add,Sub,BitAnd,BitOr,BitXor} helpers that
    delegate to the existing BuildSimdOp;
  - extend SimdOpInstr::KindForOperator and CreateFromCall;
  - extend CallSpecializer::InlineSimdOp and TryInlineRecognizedMethod
    so the non-speculative null-check path used for Float32x4 operators
    in AOT also applies here.

Measured on macOS arm64 (M-series), `dart compile exe`:

  Issue 63217 orSimd      : 12.58 -> 0.32 us/iter  (39x)
  Issue 63217 andNotSimd  : 23.51 -> 0.34 us/iter  (69x)
  Issue 53662 mandelbrot  : 4038.5 -> 55.5 ms       (72x)

A new benchmark benchmarks/SimdInt32x4 exercises all five operators
with a scalar and a SIMD variant so the specialization stays covered
by the benchmark bots; it is registered in Omnibus and OmnibusDeferred.

Existing tests/lib/typed_data/simd_*_test.dart still pass in JIT and
AOT.

TEST=tests/lib/typed_data/int32x4_arithmetic_test; benchmarks/SimdInt32x4
Bug: https://github.com/dart-lang/sdk/issues/53662
Bug: https://github.com/dart-lang/sdk/issues/63217
Change-Id: I9b76ab4fff228ff1a5e3d3c86f4bfc059e66a49a
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/497000
Reviewed-by: Slava Egorov <vegorov@google.com>
Commit-Queue: Slava Egorov <vegorov@google.com>
Auto-Submit: Modestas Valauskas <valauskasmodestas@gmail.com>
Reviewed-by: Alexander Aprelev <aam@google.com>
2026-04-22 00:01:52 -07:00

127 lines
4.5 KiB
Dart

// Copyright (c) 2020, 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.
//
// A benchmark that contains several other benchmarks.
//
// With no arguments, run all benchmarks once.
// With arguments, run only the specified benchmarks in command-line order.
//
// -N: run benchmarks N times, defaults to once.
// ignore_for_file: library_prefixes
import '../../BigIntParsePrint/dart/BigIntParsePrint.dart'
deferred as lib_BigIntParsePrint;
import '../../Iterators/dart/Iterators.dart' deferred as lib_Iterators;
import '../../ListCopy/dart/ListCopy.dart' deferred as lib_ListCopy;
import '../../MD5/dart/md5.dart' deferred as lib_MD5;
import '../../MapCopy/dart/MapCopy.dart' deferred as lib_MapCopy;
import '../../MultipleReturns/dart/MultipleReturns.dart'
deferred as lib_MultipleReturns;
import '../../RecordCollections/dart/RecordCollections.dart'
deferred as lib_RecordCollections;
import '../../RuntimeType/dart/RuntimeType.dart' deferred as lib_RuntimeType;
import '../../SHA1/dart/sha1.dart' deferred as lib_SHA1;
import '../../SHA256/dart/sha256.dart' deferred as lib_SHA256;
import '../../SimdInt32x4/dart/SimdInt32x4.dart' deferred as lib_SimdInt32x4;
import '../../SkeletalAnimation/dart/SkeletalAnimation.dart'
deferred as lib_SkeletalAnimation;
import '../../SkeletalAnimationSIMD/dart/SkeletalAnimationSIMD.dart'
deferred as lib_SkeletalAnimationSIMD;
import '../../SwitchFSM/dart/SwitchFSM.dart' deferred as lib_SwitchFSM;
import '../../TypedDataDuplicate/dart/TypedDataDuplicate.dart'
deferred as lib_TypedDataDuplicate;
import '../../UiMatrix/dart/UiMatrixHarness.dart' deferred as lib_UiMatrix;
import '../../Utf8Decode/dart/Utf8Decode.dart' deferred as lib_Utf8Decode;
import '../../Utf8Encode/dart/Utf8Encode.dart' deferred as lib_Utf8Encode;
class Lib {
final Future Function() load;
final void Function() main;
Lib(this.load, this.main);
}
final Map<String, Lib> benchmarks = {
'BigIntParsePrint': Lib(
lib_BigIntParsePrint.loadLibrary,
() => lib_BigIntParsePrint.main(),
),
'Iterators': Lib(lib_Iterators.loadLibrary, () => lib_Iterators.main([])),
'ListCopy': Lib(lib_ListCopy.loadLibrary, () => lib_ListCopy.main()),
'MapCopy': Lib(lib_MapCopy.loadLibrary, () => lib_MapCopy.main([])),
'MD5': Lib(lib_MD5.loadLibrary, () => lib_MD5.main()),
'MultipleReturns': Lib(
lib_MultipleReturns.loadLibrary,
() => lib_MultipleReturns.main(),
),
'RecordCollections': Lib(
lib_RecordCollections.loadLibrary,
() => lib_RecordCollections.main(),
),
'RuntimeType': Lib(lib_RuntimeType.loadLibrary, () => lib_RuntimeType.main()),
'SHA1': Lib(lib_SHA1.loadLibrary, () => lib_SHA1.main()),
'SHA256': Lib(lib_SHA256.loadLibrary, () => lib_SHA256.main()),
'SimdInt32x4': Lib(lib_SimdInt32x4.loadLibrary, () => lib_SimdInt32x4.main()),
'SkeletalAnimation': Lib(
lib_SkeletalAnimation.loadLibrary,
() => lib_SkeletalAnimation.main(),
),
'SkeletalAnimationSIMD': Lib(
lib_SkeletalAnimationSIMD.loadLibrary,
() => lib_SkeletalAnimationSIMD.main(),
),
'SwitchFSM': Lib(lib_SwitchFSM.loadLibrary, () => lib_SwitchFSM.main()),
'TypedDataDuplicate': Lib(
lib_TypedDataDuplicate.loadLibrary,
() => lib_TypedDataDuplicate.main(),
),
'UiMatrix': Lib(lib_UiMatrix.loadLibrary, () => lib_UiMatrix.main()),
'Utf8Decode': Lib(lib_Utf8Decode.loadLibrary, () => lib_Utf8Decode.main([])),
'Utf8Encode': Lib(lib_Utf8Encode.loadLibrary, () => lib_Utf8Encode.main([])),
};
void main(List<String> originalArguments) async {
final List<String> args = List.of(originalArguments);
int repeats = 1;
for (final arg in args.toList()) {
final int? count = int.tryParse(arg);
if (count != null && count < 0) {
repeats = 0 - count;
args.remove(arg);
}
}
final preload = args.remove('--preload');
List<Lib> libs = [];
for (final name in args.toList()) {
final lib = benchmarks[name];
if (lib == null) {
print("Unknown benchmark: '$name'");
} else {
libs.add(lib);
args.remove(name);
}
}
if (args.isNotEmpty) return; // We will have printed an error.
if (libs.isEmpty) libs = benchmarks.values.toList();
if (preload) {
for (final lib in libs) {
await lib.load();
}
}
for (var i = 0; i < repeats; i++) {
for (final lib in libs) {
if (!preload) await lib.load();
lib.main();
}
}
}