blob: 0ad135c154c77059b64b4799f350dfab6bdcad1c [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package dart
import (
"bytes"
"fmt"
"strings"
"text/template"
gidlconfig "go.fuchsia.dev/fuchsia/tools/fidl/gidl/config"
gidlir "go.fuchsia.dev/fuchsia/tools/fidl/gidl/ir"
gidlmixer "go.fuchsia.dev/fuchsia/tools/fidl/gidl/mixer"
fidl "go.fuchsia.dev/fuchsia/tools/fidl/lib/fidlgen"
)
var benchmarkTmpl = template.Must(template.New("benchmarkTmpls").Parse(`
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// @dart = 2.8
import 'dart:convert';
import 'dart:io' hide exit;
import 'dart:typed_data';
import 'package:fuchsia/fuchsia.dart';
import 'package:sl4f/sl4f.dart';
import 'package:test/test.dart';
{{- if .UsesHandles }}
import 'package:topaz.lib.gidl/handles.dart';
{{- end }}
import 'package:fidl/fidl.dart';
import 'package:fidl_benchmarkfidl/fidl_async.dart';
typedef _CallbackSetter = void Function(void Function());
typedef _DefinitionBlock = void Function(
_CallbackSetter run, _CallbackSetter teardown);
class _Definition {
final String name;
final _DefinitionBlock block;
void Function() _run;
void Function() _teardown;
_Definition(this.name, this.block);
double execute() {
block((void Function() run) => _run = run,
(void Function() teardown) => _teardown = teardown);
if (_run == null) {
throw Exception("Benchmark $name doesn't declare what to run.");
}
// Warmup for at least 100ms. Discard result.
_measure(100);
// Run the benchmark for at least 1s.
double result = _measure(1000);
if (_teardown != null) {
_teardown();
}
print('$name $result ms\n');
return result;
}
// Measures the score for this benchmark by executing it repeateldy until
// time minimum has been reached.
double _measure(int minimumMillis) {
int minimumMicros = minimumMillis * 1000;
int iter = 0;
Stopwatch watch = Stopwatch()..start();
int elapsed = 0;
while (elapsed < minimumMicros) {
_run();
elapsed = watch.elapsedMicroseconds;
iter++;
}
return elapsed / iter;
}
}
class BenchmarkGroup {
final List<_Definition> definitions;
final String name;
BenchmarkGroup(this.name) : definitions = [];
void benchmark(final String name, final _DefinitionBlock block) {
definitions.add(_Definition(name, block));
}
List<TestCaseResults> run() {
final List<TestCaseResults> results = [];
for (final def in definitions) {
final result = def.execute();
results.add(TestCaseResults(def.name, Unit.nanoseconds, [result * 1000]));
}
return results;
}
}
{{ range .Benchmarks }}
void encode{{ .Name }}Benchmark(run, teardown) {
{{- if .HandleDefs }}
final handleDefs = createHandles({{ .HandleDefs }});
teardown(() {
closeHandles(handleDefs);
});
{{- end }}
final value = {{ .Value }};
run(() {
final Encoder encoder = Encoder()
..alloc({{ .ValueType}}.inlineSize);
{{ .ValueType }}.encode(encoder, value, 0);
});
}
void decode{{ .Name }}Benchmark(run, teardown) {
{{- if .HandleDefs }}
final handleDefs = createHandles({{ .HandleDefs }});
teardown(() {
closeHandles(handleDefs);
});
{{- end }}
final value = {{ .Value }};
final Encoder encoder = Encoder()..alloc({{ .ValueType}}.inlineSize);
{{ .ValueType }}.encode(encoder, value, 0);
run(() {
final Decoder decoder = Decoder(encoder.message)
..claimMemory({{ .ValueType}}.inlineSize);
{{ .ValueType }}.decode(decoder, 0);
});
}
{{ end }}
List<Map<String, dynamic>> resultsToJson(
List<TestCaseResults> results, String suiteName) {
return results.map((result) => result.toJson(testSuite: suiteName)).toList();
}
void main() async {
test('main', () async {
final benchmarks = BenchmarkGroup('fuchsia.fidl_microbenchmarks')
{{ range .Benchmarks }}
..benchmark('Dart/Encode/{{ .ChromeperfPath }}/WallTime', encode{{.Name}}Benchmark)
..benchmark('Dart/Decode/{{ .ChromeperfPath }}/WallTime', decode{{.Name}}Benchmark)
{{ end }};
final results = benchmarks.run();
if (results.isEmpty) {
exit(1);
}
var encoder = JsonEncoder.withIndent(' ');
await File('/data/results.json').writeAsString(
encoder.convert(resultsToJson(results, benchmarks.name)),
mode: FileMode.write);
}, timeout: Timeout(Duration(minutes: 10)));
}
`))
type benchmarkTmplInput struct {
UsesHandles bool
Benchmarks []benchmark
}
type benchmark struct {
Name, ChromeperfPath, Value, ValueType, HandleDefs string
}
// GenerateBenchmarks generates Dart benchmarks.
func GenerateBenchmarks(gidl gidlir.All, fidl fidl.Root, config gidlconfig.GeneratorConfig) ([]byte, error) {
schema := gidlmixer.BuildSchema(fidl)
var benchmarks []benchmark
usesHandles := false
for _, gidlBenchmark := range gidl.Benchmark {
decl, err := schema.ExtractDeclaration(gidlBenchmark.Value, gidlBenchmark.HandleDefs)
if err != nil {
return nil, fmt.Errorf("benchmark %s: %s", gidlBenchmark.Name, err)
}
value := visit(gidlBenchmark.Value, decl)
valueType := typeName(decl)
benchmarks = append(benchmarks, benchmark{
Name: benchmarkName(gidlBenchmark.Name),
ChromeperfPath: gidlBenchmark.Name,
Value: value,
ValueType: valueType,
HandleDefs: buildHandleDefs(gidlBenchmark.HandleDefs),
})
if len(gidlBenchmark.HandleDefs) > 0 {
usesHandles = true
}
}
input := benchmarkTmplInput{
UsesHandles: usesHandles,
Benchmarks: benchmarks,
}
var buf bytes.Buffer
err := benchmarkTmpl.Execute(&buf, input)
return buf.Bytes(), err
}
func benchmarkName(gidlName string) string {
return fidl.ToSnakeCase(strings.ReplaceAll(gidlName, "/", "_"))
}