Skip to content

Instantly share code, notes, and snippets.

@PlugFox
Last active January 15, 2025 07:30
Show Gist options
  • Save PlugFox/88ba6027a341fd02d94c58e80123cf05 to your computer and use it in GitHub Desktop.
Save PlugFox/88ba6027a341fd02d94c58e80123cf05 to your computer and use it in GitHub Desktop.
Dart: Nums benchmark
import 'dart:isolate';
import 'dart:math' as math;
import 'dart:typed_data';
import 'package:benchmark_harness/benchmark_harness.dart';
// $ dart run bin/nums.dart
//
// $ dart compile exe bin/nums.dart -o out/nums.exe
// $ ./out/nums.exe
void main() => Future<void>(() async {
const length = 10000;
final pairs = [
(List<int>.filled(length, 0), List<int>.filled(length, 0)),
(List<int>.filled(length, 0), List<double>.filled(length, .0)),
(List<double>.filled(length, .0), List<int>.filled(length, 0)),
(List<double>.filled(length, 0), List<double>.filled(length, .0)),
(Uint32List(length), Uint32List(length)),
(Int32List(length), Int32List(length)),
(Float64List(length), Float64List(length)),
(Float32List(length), Float32List(length)),
];
final randomData = List<int>.generate(
length,
(index) => math.Random().nextInt(1000) + 1,
growable: false,
);
List<T> fill<T extends num>(List<T> list) {
if (list is List<int>) {
for (var i = 0; i < list.length; i++) list[i] = randomData[i] as T;
} else if (list is List<double>) {
for (var i = 0; i < list.length; i++)
list[i] = randomData[i].toDouble() as T;
} else {
throw ArgumentError('Unsupported type: $T for ${list.runtimeType}');
}
return list;
}
for (var (a, b) in pairs) {
fill(a);
fill(b);
}
Future<List<String>> Function() process(
NumBenchmark<A, B> Function<A extends num, B extends num>(
List<A> a,
List<B> b,
) fn,
) {
final benchmarks = pairs.map((pair) => fn(pair.$1, pair.$2));
Future<(String, double)> compute(BenchmarkBase benchmark) =>
Isolate.run(() => benchmark.measure())
.then((result) => (benchmark.name, result));
return () async {
/* final results = await Future.wait<(String, double)>(
benchmarks.map<Future<(String, double)>>(compute),
); */
final results = await Stream.fromFutures(
benchmarks.map<Future<(String, double)>>(compute),
).toList();
results..sort((a, b) => a.$2.compareTo(b.$2));
return results
.map((pair) => '${pair.$1}: ${pair.$2.toStringAsFixed(2)} us')
.toList(growable: false);
};
}
// Summation
final buffer = StringBuffer()
..writeln('#' * 16)
..writeln('# Summation')
..writeln('#' * 16);
final sum = await process(SumBenchmark.new)();
buffer.writeln(sum.join('\n'));
buffer
..writeln()
..writeln();
// Multiplication
buffer
..writeln('#' * 16)
..writeln('# Multiplication')
..writeln('#' * 16);
final mul = await process(MulBenchmark.new)();
buffer.writeln(mul.join('\n'));
buffer
..writeln()
..writeln();
// Division
buffer
..writeln('#' * 16)
..writeln('# Division')
..writeln('#' * 16);
final div = await process(DivBenchmark.new)();
buffer.writeln(div.join('\n'));
print(buffer.toString());
});
abstract interface class NumBenchmark<A extends num, B extends num>
implements BenchmarkBase {}
class SumBenchmark<A extends num, B extends num> extends BenchmarkBase
implements NumBenchmark<A, B> {
SumBenchmark(List<A> a, List<B> b)
: _a = a,
_b = b,
_c = List.filled(math.max(a.length, b.length), 0, growable: false),
assert(a.length == b.length, 'a and b must have the same length'),
super('${a.runtimeType} + ${b.runtimeType}');
final List<A> _a;
final List<B> _b;
final List<num> _c;
@override
void run() {
for (var i = 0; i < _a.length; i++) _c[i] = _a[i] + _b[i];
}
@override
void teardown() {
if (_c.length != _a.length) throw StateError('c.length != a.length');
for (var i = 0; i < _c.length; i++)
if (_c[i] != _a[i] + _b[i])
throw StateError('c[$i] != a[$i] + b[$i]\n'
'${_c[i]} != ${_a[i]} + ${_b[i]} != ${_a[i] + _b[i]}');
super.teardown();
}
}
class MulBenchmark<A extends num, B extends num> extends BenchmarkBase
implements NumBenchmark<A, B> {
MulBenchmark(List<A> a, List<B> b)
: _a = a,
_b = b,
_c = List.filled(math.max(a.length, b.length), 0, growable: false),
assert(a.length == b.length, 'a and b must have the same length'),
super('${a.runtimeType} * ${b.runtimeType}');
final List<A> _a;
final List<B> _b;
final List<num> _c;
@override
void run() {
for (var i = 0; i < _a.length; i++) _c[i] = _a[i] * _b[i];
}
@override
void teardown() {
if (_c.length != _a.length) throw StateError('c.length != a.length');
for (var i = 0; i < _c.length; i++)
if (_c[i] != _a[i] * _b[i])
throw StateError('c[$i] != a[$i] * b[$i]\n'
'${_c[i]} != ${_a[i]} * ${_b[i]} != ${_a[i] * _b[i]}');
super.teardown();
}
}
class DivBenchmark<A extends num, B extends num> extends BenchmarkBase
implements NumBenchmark<A, B> {
DivBenchmark(List<A> a, List<B> b)
: _a = a,
_b = b,
_c = List.filled(math.max(a.length, b.length), 0, growable: false),
assert(a.length == b.length, 'a and b must have the same length'),
super('${a.runtimeType} / ${b.runtimeType}');
final List<A> _a;
final List<B> _b;
final List<num> _c;
@override
void run() {
for (var i = 0; i < _a.length; i++) _c[i] = _a[i] / _b[i];
}
@override
void teardown() {
if (_c.length != _a.length) throw StateError('c.length != a.length');
for (var i = 0; i < _c.length; i++)
if (_c[i] != _a[i] / _b[i])
throw StateError('c[$i] != a[$i] / b[$i]\n'
'${_c[i]} != ${_a[i]} / ${_b[i]} != ${_a[i] / _b[i]}');
super.teardown();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment