[io] Add a benchmark for the IOSink returned by File.openWrite
Change-Id: Ic27d24633a305d0567c19f3694b1a477b3a1eef2
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/285706
Commit-Queue: Brian Quinlan <bquinlan@google.com>
Reviewed-by: Alexander Markov <alexmarkov@google.com>
diff --git a/benchmarks/FileIOSink/dart/FileIOSink.dart b/benchmarks/FileIOSink/dart/FileIOSink.dart
new file mode 100644
index 0000000..85c0cc7
--- /dev/null
+++ b/benchmarks/FileIOSink/dart/FileIOSink.dart
@@ -0,0 +1,114 @@
+// Copyright (c) 2023, 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.
+
+/// Micro-benchmark for the [IOSink] returned by [File.openWrite].
+
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:benchmark_harness/benchmark_harness.dart';
+
+const numBytesToWrite = 200000; // Write 200KiB in all benchmarks runs.
+
+/// Benchmark building a [numBytesToWrite] byte file a single byte at a time.
+///
+/// Based on https://github.com/dart-lang/sdk/issues/32874#issue-314022259
+class BenchmarkManySmallAdds extends AsyncBenchmarkBase {
+ late Directory _tempDir;
+ late IOSink _ioSink;
+ final _singleByte = Uint8List(1);
+
+ BenchmarkManySmallAdds() : super('FileIOSink.Add.ManySmall');
+
+ @override
+ Future<void> setup() async {
+ _tempDir = Directory.systemTemp.createTempSync();
+ _ioSink = File(_tempDir.uri.resolve('many-small').toFilePath()).openWrite();
+ }
+
+ @override
+ Future<void> teardown() async {
+ await _ioSink.close();
+ _tempDir.deleteSync(recursive: true);
+ }
+
+ @override
+ Future<void> run() async {
+ for (var i = 0; i < numBytesToWrite; ++i) {
+ _ioSink.add(_singleByte);
+ }
+ await _ioSink.flush();
+ }
+}
+
+/// Benchmark building a [numBytesToWrite] byte file with a single call to
+/// [IOSink.add].
+class BenchmarkOneLargeAdd extends AsyncBenchmarkBase {
+ late final Directory _tempDir;
+ late final IOSink _ioSink;
+ final _largeData = Uint8List(numBytesToWrite);
+
+ BenchmarkOneLargeAdd() : super('FileIOSink.Add.OneLarge');
+
+ @override
+ Future<void> setup() async {
+ _tempDir = Directory.systemTemp.createTempSync();
+ _ioSink = File(_tempDir.uri.resolve('one-large').toFilePath()).openWrite();
+ }
+
+ @override
+ Future<void> teardown() async {
+ await _ioSink.close();
+ _tempDir.deleteSync(recursive: true);
+ }
+
+ @override
+ Future<void> run() async {
+ _ioSink.add(_largeData);
+ await _ioSink.flush();
+ }
+}
+
+/// Benchmark building a file by alternating calls to [IOSink.add] with large
+/// and small amounts of data.
+class BenchmarkAlternatingSizedAdd extends AsyncBenchmarkBase {
+ late final Directory _tempDir;
+ late final IOSink _ioSink;
+ final _largeData = Uint8List(numBytesToWrite - 1);
+ final _smallData = Uint8List(1);
+
+ BenchmarkAlternatingSizedAdd() : super('FileIOSink.Add.AlternatingAddSize');
+
+ @override
+ Future<void> setup() async {
+ _tempDir = Directory.systemTemp.createTempSync();
+ _ioSink = File(_tempDir.uri.resolve('alternative-add-size').toFilePath())
+ .openWrite();
+ }
+
+ @override
+ Future<void> teardown() async {
+ await _ioSink.close();
+ _tempDir.deleteSync(recursive: true);
+ }
+
+ @override
+ Future<void> run() async {
+ _ioSink.add(_largeData);
+ _ioSink.add(_smallData);
+ await _ioSink.flush();
+ }
+}
+
+void main() {
+ final benchmarks = [
+ BenchmarkManySmallAdds(),
+ BenchmarkOneLargeAdd(),
+ BenchmarkAlternatingSizedAdd(),
+ ];
+
+ for (final benchmark in benchmarks) {
+ benchmark.report();
+ }
+}