Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expand benchmarks to different dep graph shapes. #3841

Merged
merged 1 commit into from
Feb 11, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions _benchmark/bin/_benchmark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'dart:io';

import 'package:_benchmark/commands.dart';
import 'package:_benchmark/generators.dart';
import 'package:_benchmark/shape.dart';
import 'package:args/command_runner.dart';

final commandRunner =
Expand All @@ -16,6 +17,10 @@ final commandRunner =
..addCommand(BenchmarkCommand())
..addCommand(MeasureCommand())
..addCommand(CreateCommand())
..argParser.addFlag(
'allow-failures',
help: 'Whether to continue benchmarking despite failures.',
)
..argParser.addOption(
'build-repo-path',
help: 'Path to build repo to benchmark.',
Expand All @@ -30,6 +35,21 @@ final commandRunner =
'root-directory',
help: 'Root directory for generated source and builds.',
defaultsTo: '${Directory.systemTemp.path}/build_benchmark',
)
..argParser.addOption(
'size',
help:
'Benchmark size: number of libraries. Omit to run for a range of '
'sizes.',
)
..argParser.addOption(
'shape',
help: 'Shape of the dependency graph. Omit to run for all shapes.',
allowed: Shape.values.map((e) => e.name).toList(),
)
..argParser.addFlag(
'use-experimental-resolver',
help: 'Whether to pass `--use-experimental-resolver` to build_runner.',
);

Future<void> main(List<String> arguments) async {
Expand Down
19 changes: 9 additions & 10 deletions _benchmark/lib/benchmarks/built_value_generator_benchmark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,12 @@ ${config.dependencyOverrides}
''',
);

final appLines = ['// ignore_for_file: unused_import', '// CACHEBUSTER'];
for (var libraryNumber = 0; libraryNumber != size; ++libraryNumber) {
final libraryName = Benchmarks.libraryName(
libraryNumber,
benchmarkSize: size,
);
appLines.add("import '$libraryName';");
}
workspace.write(
'lib/app.dart',
source: appLines.map((l) => '$l\n').join(''),
source: '''
/// ignore_for_file: unused_import
/// CACHEBUSTER
''',
);

for (var libraryNumber = 0; libraryNumber != size; ++libraryNumber) {
Expand All @@ -57,13 +52,17 @@ ${config.dependencyOverrides}
benchmarkSize: size,
);
final partName = Benchmarks.partName(libraryNumber, benchmarkSize: size);
final importNames = config.shape.importNames(
libraryNumber,
benchmarkSize: size,
);
workspace.write(
'lib/$libraryName',
source: '''
// ignore_for_file: unused_import
import 'package:built_value/built_value.dart';

import 'app.dart';
${[for (final importName in importNames) "import '$importName';"].join('\n')}

part '$partName';

Expand Down
19 changes: 9 additions & 10 deletions _benchmark/lib/benchmarks/freezed_generator_benchmark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,12 @@ ${config.dependencyOverrides}
''',
);

final appLines = ['// ignore_for_file: unused_import', '// CACHEBUSTER'];
for (var libraryNumber = 0; libraryNumber != size; ++libraryNumber) {
final libraryName = Benchmarks.libraryName(
libraryNumber,
benchmarkSize: size,
);
appLines.add("import '$libraryName';");
}
workspace.write(
'lib/app.dart',
source: appLines.map((l) => '$l\n').join(''),
source: '''
/// ignore_for_file: unused_import
/// CACHEBUSTER
''',
);

for (var libraryNumber = 0; libraryNumber != size; ++libraryNumber) {
Expand All @@ -61,13 +56,17 @@ ${config.dependencyOverrides}
benchmarkSize: size,
infix: 'freezed',
);
final importNames = config.shape.importNames(
libraryNumber,
benchmarkSize: size,
);
workspace.write(
'lib/$libraryName',
source: '''
// ignore_for_file: unused_import
import 'package:freezed_annotation/freezed_annotation.dart';

import 'app.dart';
${[for (final importName in importNames) "import '$importName';"].join('\n')}

part '$partName';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,12 @@ ${config.dependencyOverrides}
''',
);

final appLines = ['// ignore_for_file: unused_import', '// CACHEBUSTER'];
for (var libraryNumber = 0; libraryNumber != size; ++libraryNumber) {
final libraryName = Benchmarks.libraryName(
libraryNumber,
benchmarkSize: size,
);
appLines.add("import '$libraryName';");
}
workspace.write(
'lib/app.dart',
source: appLines.map((l) => '$l\n').join(''),
source: '''
/// ignore_for_file: unused_import
/// CACHEBUSTER
''',
);

for (var libraryNumber = 0; libraryNumber != size; ++libraryNumber) {
Expand All @@ -58,13 +53,17 @@ ${config.dependencyOverrides}
benchmarkSize: size,
);
final partName = Benchmarks.partName(libraryNumber, benchmarkSize: size);
final importNames = config.shape.importNames(
libraryNumber,
benchmarkSize: size,
);
workspace.write(
'lib/$libraryName',
source: '''
// ignore_for_file: unused_import
import 'package:json_annotation/json_annotation.dart';

import 'app.dart';
${[for (final importName in importNames) "import '$importName';"].join('\n')}

part '$partName';

Expand Down
19 changes: 9 additions & 10 deletions _benchmark/lib/benchmarks/mockito_generator_benchmark.dart
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,12 @@ ${config.dependencyOverrides}
''',
);

final appLines = ['// ignore_for_file: unused_import', '// CACHEBUSTER'];
for (var libraryNumber = 0; libraryNumber != size; ++libraryNumber) {
final libraryName = Benchmarks.libraryName(
libraryNumber,
benchmarkSize: size,
);
appLines.add("import '$libraryName';");
}
workspace.write(
'lib/app.dart',
source: appLines.map((l) => '$l\n').join(''),
source: '''
/// ignore_for_file: unused_import
/// CACHEBUSTER
''',
);

for (var testNumber = 0; testNumber != size; ++testNumber) {
Expand Down Expand Up @@ -81,11 +76,15 @@ ${config.dependencyOverrides}
libraryNumber,
benchmarkSize: size,
);
final importNames = config.shape.importNames(
libraryNumber,
benchmarkSize: size,
);
workspace.write(
'lib/$libraryName',
source: '''
// ignore_for_file: unused_import
import 'app.dart';
${[for (final importName in importNames) "import '$importName';"].join('\n')}

class Service$libraryNumber {
void doSomething(int value) {}
Expand Down
98 changes: 62 additions & 36 deletions _benchmark/lib/commands.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import 'package:args/args.dart';
import 'package:args/command_runner.dart';

import 'config.dart';
import 'shape.dart';
import 'workspace.dart';

class BenchmarkCommand extends Command<void> {
Expand Down Expand Up @@ -35,19 +36,22 @@ class CreateCommand extends Command<void> {
Future<void> _run(ArgResults globalResults) async {
final config = Config.fromArgResults(globalResults);

for (final size in config.sizes) {
final paddedSize = size.toString().padLeft(4, '0');
final workspace = Workspace(
config: config,
name: '${config.generator.packageName}_$paddedSize',
);
final runConfig = RunConfig(
config: config,
workspace: workspace,
size: size,
paddedSize: paddedSize,
);
config.generator.benchmark.create(runConfig);
for (final shape in config.shapes) {
for (final size in config.sizes) {
final paddedSize = size.toString().padLeft(4, '0');
final workspace = Workspace(
config: config,
name: '${config.generator.packageName}_${shape.name}_$paddedSize',
);
final runConfig = RunConfig(
config: config,
workspace: workspace,
size: size,
paddedSize: paddedSize,
shape: shape,
);
config.generator.benchmark.create(runConfig);
}
}
}
}
Expand All @@ -65,15 +69,17 @@ class MeasureCommand extends Command<void> {
Future<void> _run(ArgResults globalResults) async {
// Launch a benchmark at each size in parallel.
final config = Config.fromArgResults(globalResults);
final pendingResults = <int, PendingResult>{};
for (final size in config.sizes) {
final paddedSize = size.toString().padLeft(4, '0');
final workspace = Workspace(
config: config,
name: '${config.generator.packageName}_$paddedSize',
clean: false,
);
pendingResults[size] = workspace.measure();
final pendingResults = <(Shape, int), PendingResult>{};
for (final shape in config.shapes) {
for (final size in config.sizes) {
final paddedSize = size.toString().padLeft(4, '0');
final workspace = Workspace(
config: config,
name: '${config.generator.packageName}_${shape.name}_$paddedSize',
clean: false,
);
pendingResults[(shape, size)] = workspace.measure();
}
}

// Wait for them to complete, printing status every second only if it
Expand All @@ -83,21 +89,36 @@ class MeasureCommand extends Command<void> {
await Future<void>.delayed(const Duration(seconds: 1));

final update = StringBuffer('${config.generator.packageName}\n');
update.write('libraries,clean/ms,no changes/ms,incremental/ms\n');
for (final size in config.sizes) {
final pendingResult = pendingResults[size]!;
if (pendingResult.isFailure) {
throw StateError(pendingResult.failure!);
update.write('shape,libraries,clean/ms,no changes/ms,incremental/ms\n');
for (final shape in config.shapes) {
for (final size in config.sizes) {
final pendingResult = pendingResults[(shape, size)]!;
if (pendingResult.isFailure) {
if (!config.allowFailures) {
throw StateError(pendingResult.failure!);
}
update.write(
[
shape.name,
size,
pendingResult.cleanBuildTime.renderFailed,
pendingResult.noChangesBuildTime.renderFailed,
pendingResult.incrementalBuildTime.renderFailed,
].join(','),
);
} else {
update.write(
[
shape.name,
size,
pendingResult.cleanBuildTime.render,
pendingResult.noChangesBuildTime.render,
pendingResult.incrementalBuildTime.render,
].join(','),
);
}
update.write('\n');
}
update.write(
[
size,
pendingResults[size]!.cleanBuildTime.render,
pendingResults[size]!.noChangesBuildTime.render,
pendingResults[size]!.incrementalBuildTime.render,
].join(','),
);
update.write('\n');
}

final updateString = update.toString();
Expand All @@ -110,5 +131,10 @@ class MeasureCommand extends Command<void> {
}

extension DurationExtension on Duration? {
/// Renders with `---` for `null`, to mean "pending".
String get render => this == null ? '---' : this!.inMilliseconds.toString();

/// Renders with X` for `null`, to mean "failed".
String get renderFailed =>
this == null ? 'X' : this!.inMilliseconds.toString();
}
22 changes: 21 additions & 1 deletion _benchmark/lib/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,34 +7,53 @@ import 'dart:io';
import 'package:args/args.dart';

import 'generators.dart';
import 'shape.dart';
import 'workspace.dart';

/// Benchmark tool config.
class Config {
final String? buildRepoPath;
final Generator generator;
final List<Shape> shapes;
final Directory rootDirectory;
final List<int> sizes = const [1, 100, 250, 500, 750, 1000];
final List<int> sizes;
final bool useExperimentalResolver;
final bool allowFailures;

Config({
required this.allowFailures,
required this.buildRepoPath,
required this.generator,
required this.rootDirectory,
required this.sizes,
required this.shapes,
required this.useExperimentalResolver,
});

factory Config.fromArgResults(ArgResults argResults) => Config(
allowFailures: argResults['allow-failures'] as bool,
buildRepoPath: argResults['build-repo-path'] as String?,
generator: Generator.values.singleWhere(
(e) => e.packageName == argResults['generator'],
),
rootDirectory: Directory(argResults['root-directory'] as String),
sizes:
argResults['size'] == null
? [1, 100, 250, 500, 750, 1000]
: [int.parse(argResults['size'] as String)],
shapes:
argResults['shape'] == null
? Shape.values
: [Shape.values.singleWhere((e) => e.name == argResults['shape'])],
useExperimentalResolver: argResults['use-experimental-resolver'] as bool,
);
}

/// Single benchmark run config.
class RunConfig {
final Config config;
final int size;
final Shape shape;

/// [size] as a padded-to-consistent-width `String`.
final String paddedSize;
Expand All @@ -46,6 +65,7 @@ class RunConfig {
required this.workspace,
required this.size,
required this.paddedSize,
required this.shape,
});

String get dependencyOverrides {
Expand Down
Loading
Loading