// Copyright 2014 The Flutter Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'dart:math' as math; import 'dart:typed_data'; import 'package:flutter/foundation.dart' show kDebugMode; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:microbenchmarks/common.dart'; List<Object?> _makeTestBuffer(int size) { final List<Object?> answer = <Object?>[]; for (int i = 0; i < size; ++i) { switch (i % 9) { case 0: answer.add(1); break; case 1: answer.add(math.pow(2, 65)); break; case 2: answer.add(1234.0); break; case 3: answer.add(null); break; case 4: answer.add(<int>[1234]); break; case 5: answer.add(<String, int>{'hello': 1234}); break; case 6: answer.add('this is a test'); break; case 7: answer.add(true); break; case 8: answer.add(Uint8List(64)); break; } } return answer; } Future<double> _runBasicStandardSmall( BasicMessageChannel<Object?> basicStandard, int count) async { final Stopwatch watch = Stopwatch(); watch.start(); for (int i = 0; i < count; ++i) { await basicStandard.send(1234); } watch.stop(); return watch.elapsedMicroseconds / count; } Future<double> _runBasicStandardLarge(BasicMessageChannel<Object?> basicStandard, List<Object?> largeBuffer, int count) async { int size = 0; final Stopwatch watch = Stopwatch(); watch.start(); for (int i = 0; i < count; ++i) { final List<Object?>? result = await basicStandard.send(largeBuffer) as List<Object?>?; // This check should be tiny compared to the actual channel send/receive. size += (result == null) ? 0 : result.length; } watch.stop(); if (size != largeBuffer.length * count) { throw Exception( "There is an error with the echo channel, the results don't add up: $size", ); } return watch.elapsedMicroseconds / count; } Future<double> _runBasicBinary(BasicMessageChannel<ByteData> basicBinary, ByteData buffer, int count) async { int size = 0; final Stopwatch watch = Stopwatch(); watch.start(); for (int i = 0; i < count; ++i) { final ByteData? result = await basicBinary.send(buffer); // This check should be tiny compared to the actual channel send/receive. size += (result == null) ? 0 : result.lengthInBytes; } watch.stop(); if (size != buffer.lengthInBytes * count) { throw Exception( "There is an error with the echo channel, the results don't add up: $size", ); } return watch.elapsedMicroseconds / count; } Future<void> _runTests() async { if (kDebugMode) { throw Exception( "Must be run in profile mode! Use 'flutter run --profile'.", ); } const BasicMessageChannel<Object?> resetChannel = BasicMessageChannel<Object?>( 'dev.flutter.echo.reset', StandardMessageCodec(), ); const BasicMessageChannel<Object?> basicStandard = BasicMessageChannel<Object?>( 'dev.flutter.echo.basic.standard', StandardMessageCodec(), ); const BasicMessageChannel<ByteData> basicBinary = BasicMessageChannel<ByteData>( 'dev.flutter.echo.basic.binary', BinaryCodec(), ); /// WARNING: Don't change the following line of code, it will invalidate /// `Large` tests. Instead make a different test. The size of largeBuffer /// serialized is 14214 bytes. final List<Object?> largeBuffer = _makeTestBuffer(1000); final ByteData largeBufferBytes = const StandardMessageCodec().encodeMessage(largeBuffer)!; final ByteData oneMB = ByteData(1024 * 1024); const int numMessages = 2500; final BenchmarkResultPrinter printer = BenchmarkResultPrinter(); resetChannel.send(true); await _runBasicStandardSmall(basicStandard, 1); // Warmup. printer.addResult( description: 'BasicMessageChannel/StandardMessageCodec/Flutter->Host/Small', value: await _runBasicStandardSmall(basicStandard, numMessages), unit: 'µs', name: 'platform_channel_basic_standard_2host_small', ); resetChannel.send(true); await _runBasicStandardLarge(basicStandard, largeBuffer, 1); // Warmup. printer.addResult( description: 'BasicMessageChannel/StandardMessageCodec/Flutter->Host/Large', value: await _runBasicStandardLarge(basicStandard, largeBuffer, numMessages), unit: 'µs', name: 'platform_channel_basic_standard_2host_large', ); resetChannel.send(true); await _runBasicBinary(basicBinary, largeBufferBytes, 1); // Warmup. printer.addResult( description: 'BasicMessageChannel/BinaryCodec/Flutter->Host/Large', value: await _runBasicBinary(basicBinary, largeBufferBytes, numMessages), unit: 'µs', name: 'platform_channel_basic_binary_2host_large', ); resetChannel.send(true); await _runBasicBinary(basicBinary, oneMB, 1); // Warmup. printer.addResult( description: 'BasicMessageChannel/BinaryCodec/Flutter->Host/1MB', value: await _runBasicBinary(basicBinary, oneMB, numMessages), unit: 'µs', name: 'platform_channel_basic_binary_2host_1MB', ); printer.printToStdout(); } class _BenchmarkWidget extends StatefulWidget { const _BenchmarkWidget(this.tests, {Key? key}) : super(key: key); final Future<void> Function() tests; @override _BenchmarkWidgetState createState() => _BenchmarkWidgetState(); } class _BenchmarkWidgetState extends State<_BenchmarkWidget> { @override void initState() { widget.tests(); super.initState(); } @override Widget build(BuildContext context) => Container(); } void main() { runApp(const _BenchmarkWidget(_runTests)); }