mirror of
https://github.com/dart-lang/sdk
synced 2024-10-02 23:59:16 +00:00
[benchmark] Fork benchmarks for null safety and pin internal ones.
This change forks the benchmarks for the null safety feature where there now is a null safe version at benchmarks/Foo/dart and a legacy Dart 2 version at benchmarks/Foo/dart2. This change allows benchmarks to be migrated per go/dart-nnbd-benchmark-migration. Additionally this change pins the internal benchmarks which have been moved into their own repository and is now versioned along with the Dart SDK. The dart2 implementation will be benchmarked instead of the null safe dart implementation as of this change. The null safe dart implementation will be used when null safe benchmarking is turned on. Change-Id: If039fd7100c960169f4161c1d98167aca0af2ded Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/148440 Reviewed-by: Alexander Thomas <athom@google.com>
This commit is contained in:
parent
91e4856905
commit
6f66f82625
14
DEPS
14
DEPS
|
@ -11,6 +11,7 @@ allowed_hosts = [
|
|||
'chrome-infra-packages.appspot.com',
|
||||
'chromium.googlesource.com',
|
||||
'dart.googlesource.com',
|
||||
'dart-internal.googlesource.com',
|
||||
'fuchsia.googlesource.com',
|
||||
]
|
||||
|
||||
|
@ -25,8 +26,8 @@ vars = {
|
|||
# We mirror our github repos on Dart's git servers.
|
||||
# DO NOT use this var if you don't see a mirror here:
|
||||
# https://dart.googlesource.com/
|
||||
"dart_git":
|
||||
"https://dart.googlesource.com/",
|
||||
"dart_git": "https://dart.googlesource.com/",
|
||||
"dart_internal_git": "https://dart-internal.googlesource.com",
|
||||
# If the repo you want to use is at github.com/dart-lang, but not at
|
||||
# dart.googlesource.com, please file an issue
|
||||
# on github and add the label 'area-infrastructure'.
|
||||
|
@ -46,6 +47,10 @@ vars = {
|
|||
"co19_rev": "9dacb12cf963ce92fb056b7f2fb87096fd576e9a",
|
||||
"co19_2_rev": "620c1148c8b7a3d7f74afacf348c46f109eb64f2",
|
||||
|
||||
# The internal benchmarks to use. See go/dart-benchmarks-internal
|
||||
"benchmarks_internal_rev": "f40aeb69509e77f8ed04096a0a6ea1feca5ff9e1",
|
||||
"checkout_benchmarks_internal": False,
|
||||
|
||||
# As Flutter does, we use Fuchsia's GN and Clang toolchain. These revision
|
||||
# should be kept up to date with the revisions pulled by the Flutter engine.
|
||||
# The list of revisions for these tools comes from Fuchsia, here:
|
||||
|
@ -178,6 +183,11 @@ deps = {
|
|||
Var("chromium_git") + "/chromium/llvm-project/cfe/tools/clang-format.git" +
|
||||
"@" + Var("clang_format_scripts_rev"),
|
||||
|
||||
Var("dart_root") + "/benchmarks-internal": {
|
||||
"url": Var("dart_internal_git") + "/benchmarks-internal.git" +
|
||||
"@" + Var("benchmarks_internal_rev"),
|
||||
"condition": "checkout_benchmarks_internal",
|
||||
},
|
||||
Var("dart_root") + "/tools/sdks": {
|
||||
"packages": [{
|
||||
"package": "dart/dart-sdk/${{platform}}",
|
||||
|
|
318
benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart
Normal file
318
benchmarks/BigIntParsePrint/dart2/BigIntParsePrint.dart
Normal file
|
@ -0,0 +1,318 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
import 'package:fixnum/fixnum.dart';
|
||||
|
||||
import 'native_version_dummy.dart'
|
||||
if (dart.library.js) 'native_version_javascript.dart';
|
||||
|
||||
// Benckmark BigInt and Int64 formatting and parsing.
|
||||
|
||||
// A global sink that is used in the [check] method ensures that the results are
|
||||
// not optimized.
|
||||
dynamic sink1, sink2;
|
||||
|
||||
void check(bool sink2isEven) {
|
||||
if (sink1.codeUnits.last.isEven != sink2isEven) {
|
||||
throw StateError('Inconsistent $sink1 vs $sink2');
|
||||
}
|
||||
}
|
||||
|
||||
// These benchmarks measure digit-throughput for parsing and formatting.
|
||||
//
|
||||
// Each benchmark targets processing [requiredDigits] decimal digits, spread
|
||||
// over a list of input values. This makes the benchmarks for different integer
|
||||
// lengths roughly comparable. The number is chosen so that most benchmarks
|
||||
// have very close to this number of digits. It corresponds to nine 4096-bit
|
||||
// integers.
|
||||
const requiredDigits = 11106;
|
||||
|
||||
class ParseBigIntBenchmark extends BenchmarkBase {
|
||||
final int bits;
|
||||
final BigInt seed;
|
||||
final List<String> strings = [];
|
||||
|
||||
ParseBigIntBenchmark(String name, this.bits)
|
||||
: seed = (BigInt.one << bits) - BigInt.one,
|
||||
super(name);
|
||||
|
||||
void setup() {
|
||||
BigInt b = seed;
|
||||
int totalLength = 0;
|
||||
while (totalLength < requiredDigits) {
|
||||
if (b.bitLength < bits) {
|
||||
b = seed;
|
||||
}
|
||||
String string = b.toString();
|
||||
strings.add(string);
|
||||
totalLength += string.length;
|
||||
b = b - (b >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
for (String s in strings) {
|
||||
BigInt b = BigInt.parse(s);
|
||||
sink1 = s;
|
||||
sink2 = b;
|
||||
}
|
||||
check(sink2.isEven);
|
||||
}
|
||||
}
|
||||
|
||||
int int64UnsignedBitLength(Int64 i) => i.isNegative ? 64 : i.bitLength;
|
||||
|
||||
class ParseInt64Benchmark extends BenchmarkBase {
|
||||
final int bits;
|
||||
final Int64 seed;
|
||||
final List<String> strings = [];
|
||||
|
||||
ParseInt64Benchmark(String name, this.bits)
|
||||
: seed = (Int64.ONE << bits) - Int64.ONE,
|
||||
super(name);
|
||||
|
||||
void setup() {
|
||||
Int64 b = seed;
|
||||
int totalLength = 0;
|
||||
while (totalLength < requiredDigits) {
|
||||
if (int64UnsignedBitLength(b) < bits) {
|
||||
b = seed;
|
||||
}
|
||||
String string = b.toStringUnsigned();
|
||||
strings.add(string);
|
||||
totalLength += string.length;
|
||||
b = b - b.shiftRightUnsigned(8);
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
for (String s in strings) {
|
||||
Int64 b = Int64.parseInt(s);
|
||||
sink1 = s;
|
||||
sink2 = b;
|
||||
}
|
||||
check(sink2.isEven);
|
||||
}
|
||||
}
|
||||
|
||||
class ParseJsBigIntBenchmark extends BenchmarkBase {
|
||||
final int bits;
|
||||
final Object seed;
|
||||
final List<String> strings = [];
|
||||
|
||||
ParseJsBigIntBenchmark(String name, this.bits)
|
||||
: seed = nativeBigInt.subtract(
|
||||
nativeBigInt.shiftLeft(
|
||||
nativeBigInt.one, nativeBigInt.fromInt(bits)),
|
||||
nativeBigInt.one),
|
||||
super(name);
|
||||
|
||||
void setup() {
|
||||
Object b = seed;
|
||||
int totalLength = 0;
|
||||
while (totalLength < requiredDigits) {
|
||||
if (nativeBigInt.bitLength(b) < bits) {
|
||||
b = seed;
|
||||
}
|
||||
String string = nativeBigInt.toStringMethod(b);
|
||||
strings.add(string);
|
||||
totalLength += string.length;
|
||||
b = nativeBigInt.subtract(
|
||||
b, nativeBigInt.shiftRight(b, nativeBigInt.eight));
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
for (String s in strings) {
|
||||
Object b = nativeBigInt.parse(s);
|
||||
sink1 = s;
|
||||
sink2 = b;
|
||||
}
|
||||
check(nativeBigInt.isEven(sink2));
|
||||
}
|
||||
}
|
||||
|
||||
class FormatBigIntBenchmark extends BenchmarkBase {
|
||||
final int bits;
|
||||
final BigInt seed;
|
||||
final List<BigInt> values = [];
|
||||
|
||||
FormatBigIntBenchmark(String name, this.bits)
|
||||
: seed = (BigInt.one << bits) - BigInt.one,
|
||||
super(name);
|
||||
|
||||
void setup() {
|
||||
BigInt b = seed;
|
||||
int totalLength = 0;
|
||||
while (totalLength < requiredDigits) {
|
||||
if (b.bitLength < bits) {
|
||||
b = seed;
|
||||
}
|
||||
String string = b.toString();
|
||||
values.add(b - BigInt.one); // We add 'one' back later.
|
||||
totalLength += string.length;
|
||||
b = b - (b >> 8);
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
for (BigInt b0 in values) {
|
||||
// Instances might cache `toString()`, so use arithmetic to create a new
|
||||
// instance to try to protect against measuring a cached string.
|
||||
BigInt b = b0 + BigInt.one;
|
||||
String s = b.toString();
|
||||
sink1 = s;
|
||||
sink2 = b;
|
||||
}
|
||||
check(sink2.isEven);
|
||||
}
|
||||
}
|
||||
|
||||
class FormatInt64Benchmark extends BenchmarkBase {
|
||||
final int bits;
|
||||
final Int64 seed;
|
||||
final List<Int64> values = [];
|
||||
|
||||
FormatInt64Benchmark(String name, this.bits)
|
||||
: seed = (Int64.ONE << bits) - Int64.ONE,
|
||||
super(name);
|
||||
|
||||
void setup() {
|
||||
Int64 b = seed;
|
||||
int totalLength = 0;
|
||||
while (totalLength < requiredDigits) {
|
||||
if (int64UnsignedBitLength(b) < bits) {
|
||||
b = seed;
|
||||
}
|
||||
String string = b.toStringUnsigned();
|
||||
values.add(b - Int64.ONE);
|
||||
totalLength += string.length;
|
||||
b = b - b.shiftRightUnsigned(8);
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
for (Int64 b0 in values) {
|
||||
// Instances might cache `toString()`, so use arithmetic to create a new
|
||||
// instance to try to protect against measuring a cached string.
|
||||
Int64 b = b0 + Int64.ONE;
|
||||
String s = b.toStringUnsigned();
|
||||
sink1 = s;
|
||||
sink2 = b;
|
||||
}
|
||||
check(sink2.isEven);
|
||||
}
|
||||
}
|
||||
|
||||
class FormatJsBigIntBenchmark extends BenchmarkBase {
|
||||
final int bits;
|
||||
final Object seed;
|
||||
final List<Object> values = [];
|
||||
|
||||
FormatJsBigIntBenchmark(String name, this.bits)
|
||||
: seed = nativeBigInt.subtract(
|
||||
nativeBigInt.shiftLeft(
|
||||
nativeBigInt.one, nativeBigInt.fromInt(bits)),
|
||||
nativeBigInt.one),
|
||||
super(name);
|
||||
|
||||
void setup() {
|
||||
final one = nativeBigInt.one;
|
||||
Object b = seed;
|
||||
int totalLength = 0;
|
||||
while (totalLength < requiredDigits) {
|
||||
if (nativeBigInt.bitLength(b) < bits) {
|
||||
b = seed;
|
||||
}
|
||||
String string = nativeBigInt.toStringMethod(b);
|
||||
values.add(nativeBigInt.subtract(b, one)); // We add 'one' back later.
|
||||
totalLength += string.length;
|
||||
b = nativeBigInt.subtract(
|
||||
b, nativeBigInt.shiftRight(b, nativeBigInt.eight));
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
final one = nativeBigInt.one;
|
||||
for (Object b0 in values) {
|
||||
// Instances might cache `toString()`, so use arithmetic to create a new
|
||||
// instance to try to protect against measuring a cached string.
|
||||
Object b = nativeBigInt.add(b0, one);
|
||||
String s = nativeBigInt.toStringMethod(b);
|
||||
sink1 = s;
|
||||
sink2 = b;
|
||||
}
|
||||
check(nativeBigInt.isEven(sink2));
|
||||
}
|
||||
}
|
||||
|
||||
/// [DummyBenchmark] instantly returns a fixed 'slow' result.
|
||||
class DummyBenchmark extends BenchmarkBase {
|
||||
DummyBenchmark(String name) : super(name);
|
||||
double measure() => 2000 * 1000 * 1.0; // A rate of one run per 2s.
|
||||
}
|
||||
|
||||
/// Create [ParseJsBigIntBenchmark], or a dummy benchmark if JavaScript BigInt
|
||||
/// is not available. This is to satisfy Golem's constraint that group
|
||||
/// benchmarks always produce results for the same set of series.
|
||||
BenchmarkBase Function() selectParseNativeBigIntBenchmark(
|
||||
String name, int bits) {
|
||||
return nativeBigInt.enabled
|
||||
? () => ParseJsBigIntBenchmark(name, bits)
|
||||
: () => DummyBenchmark(name);
|
||||
}
|
||||
|
||||
/// Create [FormatJsBigIntBenchmark], or a dummy benchmark if JavaScript BigInt
|
||||
/// is not available. This is to satisfy Golem's constraint that group
|
||||
/// benchmarks always produce results for the same set of series.
|
||||
BenchmarkBase Function() selectFormatNativeBigIntBenchmark(
|
||||
String name, int bits) {
|
||||
return nativeBigInt.enabled
|
||||
? () => FormatJsBigIntBenchmark(name, bits)
|
||||
: () => DummyBenchmark(name);
|
||||
}
|
||||
|
||||
main() {
|
||||
final benchmarks = [
|
||||
() => ParseInt64Benchmark('Int64.parse.0009.bits', 9),
|
||||
() => ParseInt64Benchmark('Int64.parse.0032.bits', 32),
|
||||
() => ParseInt64Benchmark('Int64.parse.0064.bits', 64),
|
||||
() => ParseBigIntBenchmark('BigInt.parse.0009.bits', 9),
|
||||
() => ParseBigIntBenchmark('BigInt.parse.0032.bits', 32),
|
||||
() => ParseBigIntBenchmark('BigInt.parse.0064.bits', 64),
|
||||
() => ParseBigIntBenchmark('BigInt.parse.0256.bits', 256),
|
||||
() => ParseBigIntBenchmark('BigInt.parse.1024.bits', 1024),
|
||||
() => ParseBigIntBenchmark('BigInt.parse.4096.bits', 4096),
|
||||
selectParseNativeBigIntBenchmark('JsBigInt.parse.0009.bits', 9),
|
||||
selectParseNativeBigIntBenchmark('JsBigInt.parse.0032.bits', 32),
|
||||
selectParseNativeBigIntBenchmark('JsBigInt.parse.0064.bits', 64),
|
||||
selectParseNativeBigIntBenchmark('JsBigInt.parse.0256.bits', 256),
|
||||
selectParseNativeBigIntBenchmark('JsBigInt.parse.1024.bits', 1024),
|
||||
selectParseNativeBigIntBenchmark('JsBigInt.parse.4096.bits', 4096),
|
||||
() => FormatInt64Benchmark('Int64.toString.0009.bits', 9),
|
||||
() => FormatInt64Benchmark('Int64.toString.0032.bits', 32),
|
||||
() => FormatInt64Benchmark('Int64.toString.0064.bits', 64),
|
||||
() => FormatBigIntBenchmark('BigInt.toString.0009.bits', 9),
|
||||
() => FormatBigIntBenchmark('BigInt.toString.0032.bits', 32),
|
||||
() => FormatBigIntBenchmark('BigInt.toString.0064.bits', 64),
|
||||
() => FormatBigIntBenchmark('BigInt.toString.0256.bits', 256),
|
||||
() => FormatBigIntBenchmark('BigInt.toString.1024.bits', 1024),
|
||||
() => FormatBigIntBenchmark('BigInt.toString.4096.bits', 4096),
|
||||
selectFormatNativeBigIntBenchmark('JsBigInt.toString.0009.bits', 9),
|
||||
selectFormatNativeBigIntBenchmark('JsBigInt.toString.0032.bits', 32),
|
||||
selectFormatNativeBigIntBenchmark('JsBigInt.toString.0064.bits', 64),
|
||||
selectFormatNativeBigIntBenchmark('JsBigInt.toString.0256.bits', 256),
|
||||
selectFormatNativeBigIntBenchmark('JsBigInt.toString.1024.bits', 1024),
|
||||
selectFormatNativeBigIntBenchmark('JsBigInt.toString.4096.bits', 4096),
|
||||
];
|
||||
|
||||
// Warm up all benchmarks to ensure consistent behavious of shared code.
|
||||
benchmarks.forEach((bm) => bm()
|
||||
..setup()
|
||||
..run()
|
||||
..run());
|
||||
|
||||
benchmarks.forEach((bm) => bm().report());
|
||||
}
|
23
benchmarks/BigIntParsePrint/dart2/native_version.dart
Normal file
23
benchmarks/BigIntParsePrint/dart2/native_version.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
abstract class NativeBigIntMethods {
|
||||
bool get enabled;
|
||||
|
||||
Object parse(String string);
|
||||
String toStringMethod(Object value);
|
||||
|
||||
Object fromInt(int i);
|
||||
|
||||
Object get one;
|
||||
Object get eight;
|
||||
|
||||
int bitLength(Object value);
|
||||
bool isEven(Object value);
|
||||
|
||||
Object add(Object left, Object right);
|
||||
Object shiftLeft(Object value, Object count);
|
||||
Object shiftRight(Object value, Object count);
|
||||
Object subtract(Object left, Object right);
|
||||
}
|
31
benchmarks/BigIntParsePrint/dart2/native_version_dummy.dart
Normal file
31
benchmarks/BigIntParsePrint/dart2/native_version_dummy.dart
Normal file
|
@ -0,0 +1,31 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'native_version.dart';
|
||||
|
||||
const NativeBigIntMethods nativeBigInt = _DummyMethods();
|
||||
|
||||
class _DummyMethods implements NativeBigIntMethods {
|
||||
const _DummyMethods();
|
||||
|
||||
bool get enabled => false;
|
||||
|
||||
static Object bad(String message) => UnimplementedError(message);
|
||||
|
||||
Object parse(String string) => throw bad('parse');
|
||||
String toStringMethod(Object value) => throw bad('toStringMethod');
|
||||
|
||||
Object fromInt(int i) => throw bad('fromInt');
|
||||
|
||||
Object get one => throw bad('one');
|
||||
Object get eight => throw bad('eight');
|
||||
|
||||
int bitLength(Object value) => throw bad('bitLength');
|
||||
bool isEven(Object value) => throw bad('isEven');
|
||||
|
||||
Object add(Object left, Object right) => throw bad('add');
|
||||
Object shiftLeft(Object value, Object count) => throw bad('shiftLeft');
|
||||
Object shiftRight(Object value, Object count) => throw bad('shiftRight');
|
||||
Object subtract(Object left, Object right) => throw bad('subtract');
|
||||
}
|
111
benchmarks/BigIntParsePrint/dart2/native_version_javascript.dart
Normal file
111
benchmarks/BigIntParsePrint/dart2/native_version_javascript.dart
Normal file
|
@ -0,0 +1,111 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'native_version.dart';
|
||||
import 'package:js/js.dart';
|
||||
|
||||
const NativeBigIntMethods nativeBigInt = _Methods();
|
||||
|
||||
@JS('eval')
|
||||
external Object _eval(String s);
|
||||
|
||||
@JS('bigint_parse')
|
||||
external Object _parse(String s);
|
||||
|
||||
@JS('bigint_toString')
|
||||
external String _toStringMethod(Object o);
|
||||
|
||||
@JS('bigint_bitLength')
|
||||
external int _bitLength(Object o);
|
||||
|
||||
@JS('bigint_isEven')
|
||||
external bool _isEven(Object o);
|
||||
|
||||
@JS('bigint_add')
|
||||
external Object _add(Object left, Object right);
|
||||
|
||||
@JS('bigint_shiftLeft')
|
||||
external Object _shiftLeft(Object o, Object i);
|
||||
|
||||
@JS('bigint_shiftRight')
|
||||
external Object _shiftRight(Object o, Object i);
|
||||
|
||||
@JS('bigint_subtract')
|
||||
external Object _subtract(Object left, Object right);
|
||||
|
||||
@JS('bigint_fromInt')
|
||||
external Object _fromInt(int i);
|
||||
|
||||
class _Methods implements NativeBigIntMethods {
|
||||
static bool _initialized = false;
|
||||
static bool _enabled = false;
|
||||
|
||||
const _Methods();
|
||||
|
||||
bool get enabled {
|
||||
if (!_initialized) {
|
||||
_initialize();
|
||||
}
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
void _initialize() {
|
||||
try {
|
||||
_setup();
|
||||
_enabled = true;
|
||||
} catch (e) {
|
||||
// We get here if the JavaScript implementation does not have BigInt (or
|
||||
// run in a stand-alone JavaScript implementation without the right
|
||||
// 'preamble').
|
||||
//
|
||||
// Print so we can see what failed.
|
||||
print(e);
|
||||
}
|
||||
}
|
||||
|
||||
static Object bad(String message) {
|
||||
throw UnimplementedError(message);
|
||||
}
|
||||
|
||||
Object parse(String string) => _parse(string);
|
||||
|
||||
String toStringMethod(Object value) => _toStringMethod(value);
|
||||
|
||||
Object fromInt(int i) => _fromInt(i);
|
||||
|
||||
Object get one => _one;
|
||||
|
||||
Object get eight => _eight;
|
||||
|
||||
int bitLength(Object value) => _bitLength(value);
|
||||
|
||||
bool isEven(Object value) => _isEven(value);
|
||||
|
||||
Object add(Object left, Object right) => _add(left, right);
|
||||
Object shiftLeft(Object value, Object count) => _shiftLeft(value, count);
|
||||
Object shiftRight(Object value, Object count) => _shiftRight(value, count);
|
||||
Object subtract(Object left, Object right) => _subtract(left, right);
|
||||
}
|
||||
|
||||
void _setup() {
|
||||
_one = _eval('1n'); // Throws if JavaScript does not have BigInt.
|
||||
_eight = _eval('8n');
|
||||
|
||||
_eval('self.bigint_parse = function parse(s) { return BigInt(s); }');
|
||||
_eval('self.bigint_toString = function toString(b) { return b.toString(); }');
|
||||
_eval('self.bigint_add = function add(a, b) { return a + b; }');
|
||||
_eval('self.bigint_shiftLeft = function shl(v, i) { return v << i; }');
|
||||
_eval('self.bigint_shiftRight = function shr(v, i) { return v >> i; }');
|
||||
_eval('self.bigint_subtract = function subtract(a, b) { return a - b; }');
|
||||
_eval('self.bigint_fromInt = function fromInt(i) { return BigInt(i); }');
|
||||
|
||||
_eval('self.bigint_bitLength = function bitLength(b) {'
|
||||
'return b == 0 ? 0 : (b < 0 ? ~b : b).toString(2).length;'
|
||||
'}');
|
||||
_eval('self.bigint_isEven = function isEven(b) { return (b & 1n) == 0n; }');
|
||||
}
|
||||
|
||||
// `dynamic` to allow null initialization pre- and post- NNBD.
|
||||
dynamic _one;
|
||||
dynamic _eight;
|
574
benchmarks/Calls/dart2/Calls.dart
Normal file
574
benchmarks/Calls/dart2/Calls.dart
Normal file
|
@ -0,0 +1,574 @@
|
|||
// Copyright (c) 2019, 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-benchmarks for sync/sync*/async/async* functionality.
|
||||
|
||||
import "dart:async";
|
||||
|
||||
const int iterationLimitAsync = 200;
|
||||
const int sumOfIterationLimitAsync =
|
||||
iterationLimitAsync * (iterationLimitAsync - 1) ~/ 2;
|
||||
|
||||
const int iterationLimitSync = 5000;
|
||||
const int sumOfIterationLimitSync =
|
||||
iterationLimitSync * (iterationLimitSync - 1) ~/ 2;
|
||||
|
||||
main() async {
|
||||
final target = Target();
|
||||
final target2 = Target2();
|
||||
final target3 = Target3();
|
||||
|
||||
// Ensure the call sites will have another target in the ICData.
|
||||
performAwaitCallsClosureTargetPolymorphic(returnAsync);
|
||||
performAwaitCallsClosureTargetPolymorphic(returnFuture);
|
||||
performAwaitCallsClosureTargetPolymorphic(returnFutureOr);
|
||||
performAwaitAsyncCallsInstanceTargetPolymorphic(target);
|
||||
performAwaitAsyncCallsInstanceTargetPolymorphic(target2);
|
||||
performAwaitAsyncCallsInstanceTargetPolymorphic(target3);
|
||||
performAwaitFutureCallsInstanceTargetPolymorphic(target);
|
||||
performAwaitFutureCallsInstanceTargetPolymorphic(target2);
|
||||
performAwaitFutureCallsInstanceTargetPolymorphic(target3);
|
||||
performAwaitFutureOrCallsInstanceTargetPolymorphic(target);
|
||||
performAwaitFutureOrCallsInstanceTargetPolymorphic(target2);
|
||||
performAwaitFutureOrCallsInstanceTargetPolymorphic(target3);
|
||||
performSyncCallsInstanceTargetPolymorphic(target);
|
||||
performSyncCallsInstanceTargetPolymorphic(target2);
|
||||
performSyncCallsInstanceTargetPolymorphic(target3);
|
||||
performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target);
|
||||
performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target2);
|
||||
performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target3);
|
||||
|
||||
performAwaitForIterationPolymorphic(generateNumbersAsyncStar);
|
||||
performAwaitForIterationPolymorphic(generateNumbersAsyncStar2);
|
||||
performAwaitForIterationPolymorphic(generateNumbersManualAsync);
|
||||
performAwaitForIterationPolymorphic(generateNumbersAsyncStarManyYields);
|
||||
performSyncIterationPolymorphic(generateNumbersSyncStar);
|
||||
performSyncIterationPolymorphic(generateNumbersSyncStar2);
|
||||
performSyncIterationPolymorphic(generateNumbersManual);
|
||||
performSyncIterationPolymorphic(generateNumbersSyncStarManyYields);
|
||||
|
||||
await AsyncCallBenchmark('Calls.AwaitAsyncCall', performAwaitAsyncCalls)
|
||||
.report();
|
||||
await AsyncCallBenchmark('Calls.AwaitAsyncCallClosureTargetPolymorphic',
|
||||
() => performAwaitCallsClosureTargetPolymorphic(returnAsync)).report();
|
||||
await AsyncCallBenchmark('Calls.AwaitAsyncCallInstanceTargetPolymorphic',
|
||||
() => performAwaitAsyncCallsInstanceTargetPolymorphic(target)).report();
|
||||
|
||||
await AsyncCallBenchmark('Calls.AwaitFutureCall', performAwaitFutureCalls)
|
||||
.report();
|
||||
await AsyncCallBenchmark('Calls.AwaitFutureCallClosureTargetPolymorphic',
|
||||
() => performAwaitCallsClosureTargetPolymorphic(returnFuture)).report();
|
||||
await AsyncCallBenchmark('Calls.AwaitFutureCallInstanceTargetPolymorphic',
|
||||
() => performAwaitFutureCallsInstanceTargetPolymorphic(target)).report();
|
||||
|
||||
await AsyncCallBenchmark('Calls.AwaitFutureOrCall', performAwaitFutureOrCalls)
|
||||
.report();
|
||||
await AsyncCallBenchmark('Calls.AwaitFutureOrCallClosureTargetPolymorphic',
|
||||
() => performAwaitCallsClosureTargetPolymorphic(returnFutureOr)).report();
|
||||
await AsyncCallBenchmark('Calls.AwaitFutureOrCallInstanceTargetPolymorphic',
|
||||
() => performAwaitFutureOrCallsInstanceTargetPolymorphic(target))
|
||||
.report();
|
||||
await AsyncCallBenchmark(
|
||||
'Calls.AwaitFutureOrCallInstanceTargetPolymorphicManyAwaits',
|
||||
() =>
|
||||
performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(target))
|
||||
.report();
|
||||
|
||||
await AsyncCallBenchmark('Calls.AwaitForAsyncStarStreamPolymorphic',
|
||||
() => performAwaitForIterationPolymorphic(generateNumbersAsyncStar))
|
||||
.report();
|
||||
await AsyncCallBenchmark(
|
||||
'Calls.AwaitForAsyncStarStreamPolymorphicManyYields',
|
||||
() => performAwaitForIterationPolymorphic(
|
||||
generateNumbersAsyncStarManyYields)).report();
|
||||
await AsyncCallBenchmark('Calls.AwaitForManualStreamPolymorphic',
|
||||
() => performAwaitForIterationPolymorphic(generateNumbersManualAsync))
|
||||
.report();
|
||||
|
||||
await SyncCallBenchmark('Calls.SyncCall', performSyncCalls).report();
|
||||
await SyncCallBenchmark('Calls.SyncCallClosureTarget',
|
||||
() => performSyncCallsClosureTarget(returnSync)).report();
|
||||
await SyncCallBenchmark('Calls.SyncCallInstanceTargetPolymorphic',
|
||||
() => performSyncCallsInstanceTargetPolymorphic(target)).report();
|
||||
|
||||
await SyncCallBenchmark('Calls.IterableSyncStarIterablePolymorphic',
|
||||
() => performSyncIterationPolymorphic(generateNumbersSyncStar)).report();
|
||||
await SyncCallBenchmark('Calls.IterableManualIterablePolymorphic',
|
||||
() => performSyncIterationPolymorphic(generateNumbersManual)).report();
|
||||
await SyncCallBenchmark(
|
||||
'Calls.IterableManualIterablePolymorphicManyYields',
|
||||
() => performSyncIterationPolymorphic(
|
||||
generateNumbersSyncStarManyYields)).report();
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> performAwaitCallsClosureTargetPolymorphic(
|
||||
FutureOr<int> fun(int count)) async {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitAsync; ++i) {
|
||||
sum += await fun(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitAsync) throw 'BUG';
|
||||
return iterationLimitAsync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> performAwaitAsyncCallsInstanceTargetPolymorphic(
|
||||
Target target) async {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitAsync; ++i) {
|
||||
sum += await target.returnAsync(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitAsync) throw 'BUG';
|
||||
return iterationLimitAsync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> performAwaitFutureCallsInstanceTargetPolymorphic(
|
||||
Target target) async {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitAsync; ++i) {
|
||||
sum += await target.returnFuture(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitAsync) throw 'BUG';
|
||||
return iterationLimitAsync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> performAwaitFutureOrCallsInstanceTargetPolymorphic(
|
||||
Target target) async {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitAsync; ++i) {
|
||||
sum += await target.returnFutureOr(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitAsync) throw 'BUG';
|
||||
return iterationLimitAsync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> performAwaitAsyncCalls() async {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitAsync; ++i) {
|
||||
sum += await returnAsync(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitAsync) throw 'BUG';
|
||||
return iterationLimitAsync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> performAwaitFutureCalls() async {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitAsync; ++i) {
|
||||
sum += await returnFuture(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitAsync) throw 'BUG';
|
||||
return iterationLimitAsync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> performAwaitFutureOrCalls() async {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitAsync; ++i) {
|
||||
sum += await returnFutureOr(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitAsync) throw 'BUG';
|
||||
return iterationLimitAsync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> performAwaitAsyncCallsInstanceTargetPolymorphicManyAwaits(Target t) async {
|
||||
int sum = 0;
|
||||
int i = 0;
|
||||
|
||||
final int blockLimit = iterationLimitAsync - (iterationLimitAsync % 80);
|
||||
while (i < blockLimit) {
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
sum += await t.returnAsync(i++); sum += await t.returnAsync(i++);
|
||||
}
|
||||
|
||||
while (i < iterationLimitAsync) {
|
||||
sum += await t.returnAsync(i++);
|
||||
}
|
||||
|
||||
if (sum != sumOfIterationLimitAsync) throw 'BUG';
|
||||
|
||||
return iterationLimitAsync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> performAwaitForIterationPolymorphic(
|
||||
Stream<int> fun(int count)) async {
|
||||
int sum = 0;
|
||||
await for (int value in fun(iterationLimitAsync)) {
|
||||
sum += value;
|
||||
}
|
||||
if (sum != sumOfIterationLimitAsync) throw 'BUG';
|
||||
return iterationLimitAsync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
int performSyncCallsClosureTarget(int fun(int count)) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitSync; ++i) {
|
||||
sum += fun(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitSync) throw 'BUG';
|
||||
return iterationLimitSync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
int performSyncCallsInstanceTargetPolymorphic(Target target) {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitSync; ++i) {
|
||||
sum += target.returnSync(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitSync) throw 'BUG';
|
||||
return iterationLimitSync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
int performSyncCalls() {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < iterationLimitSync; ++i) {
|
||||
sum += returnSync(i);
|
||||
}
|
||||
if (sum != sumOfIterationLimitSync) throw 'BUG';
|
||||
return iterationLimitSync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
int performSyncIterationPolymorphic(Iterable<int> fun(int count)) {
|
||||
int sum = 0;
|
||||
for (int value in fun(iterationLimitSync)) {
|
||||
sum += value;
|
||||
}
|
||||
if (sum != sumOfIterationLimitSync) throw 'BUG';
|
||||
return iterationLimitSync;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
FutureOr<int> returnFutureOr(int i) => i;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> returnFuture(int i) => Future.value(i);
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> returnAsync(int i) async => i;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Stream<int> generateNumbersAsyncStar(int limit) async* {
|
||||
for (int i = 0; i < limit; ++i) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Stream<int> generateNumbersAsyncStar2(int limit) async* {
|
||||
for (int i = 0; i < limit; ++i) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Stream<int> generateNumbersManualAsync(int limit) {
|
||||
int current = 0;
|
||||
StreamController<int> controller = StreamController(sync: true);
|
||||
void emit() {
|
||||
while (true) {
|
||||
if (controller.isPaused || !controller.hasListener) return;
|
||||
if (current < limit) {
|
||||
controller.add(current++);
|
||||
} else {
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
scheduleMicrotask(emit);
|
||||
}
|
||||
|
||||
controller.onListen = run;
|
||||
controller.onResume = run;
|
||||
|
||||
return controller.stream;
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
int returnSync(int i) => i;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Iterable<int> generateNumbersSyncStar(int limit) sync* {
|
||||
for (int i = 0; i < limit; ++i) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Iterable<int> generateNumbersSyncStar2(int limit) sync* {
|
||||
for (int i = 0; i < limit; ++i) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Iterable<int> generateNumbersManual(int limit) =>
|
||||
Iterable<int>.generate(limit, (int i) => i);
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Iterable<int> generateNumbersSyncStarManyYields(int limit) sync* {
|
||||
int i = 0;
|
||||
|
||||
final int blockLimit = limit - (limit % (20 * 7));
|
||||
while (i < blockLimit) {
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
}
|
||||
|
||||
while (i < limit) {
|
||||
yield i++;
|
||||
}
|
||||
}
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Stream<int> generateNumbersAsyncStarManyYields(int limit) async* {
|
||||
int i = 0;
|
||||
|
||||
final int blockLimit = limit - (limit % (20 * 7));
|
||||
while (i < blockLimit) {
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
yield i++; yield i++; yield i++; yield i++; yield i++; yield i++; yield i++;
|
||||
}
|
||||
|
||||
while (i < limit) {
|
||||
yield i++;
|
||||
}
|
||||
}
|
||||
|
||||
class Target {
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
FutureOr<int> returnFutureOr(int i) => i;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> returnFuture(int i) => Future.value(i);
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> returnAsync(int i) async => i;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
int returnSync(int i) => i;
|
||||
}
|
||||
|
||||
class Target2 extends Target {
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
FutureOr<int> returnFutureOr(int i) => i;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> returnFuture(int i) => Future.value(i);
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> returnAsync(int i) async => i;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
int returnSync(int i) => i;
|
||||
}
|
||||
|
||||
class Target3 extends Target {
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
FutureOr<int> returnFutureOr(int i) => i;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> returnFuture(int i) => Future.value(i);
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
Future<int> returnAsync(int i) async => i;
|
||||
|
||||
@pragma('vm:never-inline')
|
||||
@pragma('dart2js:noInline')
|
||||
int returnSync(int i) => i;
|
||||
}
|
||||
|
||||
typedef PerformSyncCallsFunction = int Function();
|
||||
typedef PerformAsyncCallsFunction = Future<int> Function();
|
||||
|
||||
class SyncCallBenchmark {
|
||||
final String name;
|
||||
final PerformSyncCallsFunction performCalls;
|
||||
|
||||
SyncCallBenchmark(this.name, this.performCalls);
|
||||
|
||||
// Returns the number of nanoseconds per call.
|
||||
double measureFor(Duration duration) {
|
||||
final sw = Stopwatch()..start();
|
||||
final durationInMicroseconds = duration.inMicroseconds;
|
||||
|
||||
int numberOfCalls = 0;
|
||||
int totalMicroseconds = 0;
|
||||
do {
|
||||
numberOfCalls += performCalls();
|
||||
totalMicroseconds = sw.elapsedMicroseconds;
|
||||
} while (totalMicroseconds < durationInMicroseconds);
|
||||
|
||||
final int totalNanoseconds = sw.elapsed.inMicroseconds * 1000;
|
||||
return totalNanoseconds / numberOfCalls;
|
||||
}
|
||||
|
||||
// Runs warmup phase, runs benchmark and reports result.
|
||||
void report() {
|
||||
// Warmup for 200 ms.
|
||||
measureFor(const Duration(milliseconds: 100));
|
||||
|
||||
// Run benchmark for 2 seconds.
|
||||
final double nsPerCall = measureFor(const Duration(seconds: 2));
|
||||
|
||||
// Report result.
|
||||
print("$name(RunTimeRaw): $nsPerCall ns.");
|
||||
}
|
||||
}
|
||||
|
||||
class AsyncCallBenchmark {
|
||||
final String name;
|
||||
final PerformAsyncCallsFunction performCalls;
|
||||
|
||||
AsyncCallBenchmark(this.name, this.performCalls);
|
||||
|
||||
// Returns the number of nanoseconds per call.
|
||||
Future<double> measureFor(Duration duration) async {
|
||||
final sw = Stopwatch()..start();
|
||||
final durationInMicroseconds = duration.inMicroseconds;
|
||||
|
||||
int numberOfCalls = 0;
|
||||
int totalMicroseconds = 0;
|
||||
do {
|
||||
numberOfCalls += await performCalls();
|
||||
totalMicroseconds = sw.elapsedMicroseconds;
|
||||
} while (totalMicroseconds < durationInMicroseconds);
|
||||
|
||||
final int totalNanoseconds = sw.elapsed.inMicroseconds * 1000;
|
||||
return totalNanoseconds / numberOfCalls;
|
||||
}
|
||||
|
||||
// Runs warmup phase, runs benchmark and reports result.
|
||||
Future report() async {
|
||||
// Warmup for 100 ms.
|
||||
await measureFor(const Duration(milliseconds: 100));
|
||||
|
||||
// Run benchmark for 2 seconds.
|
||||
final double nsPerCall = await measureFor(const Duration(seconds: 2));
|
||||
|
||||
// Report result.
|
||||
print("$name(RunTimeRaw): $nsPerCall ns.");
|
||||
}
|
||||
}
|
22
benchmarks/Example/dart2/Example.dart
Normal file
22
benchmarks/Example/dart2/Example.dart
Normal file
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
|
||||
class Example extends BenchmarkBase {
|
||||
const Example() : super("Example");
|
||||
|
||||
// The benchmark code.
|
||||
void run() {}
|
||||
|
||||
// Not measured setup code executed prior to the benchmark runs.
|
||||
void setup() {}
|
||||
|
||||
// Not measures teardown code executed after the benchark runs.
|
||||
void teardown() {}
|
||||
}
|
||||
|
||||
main() {
|
||||
const Example().report();
|
||||
}
|
141
benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart
Normal file
141
benchmarks/FfiBoringssl/dart2/FfiBoringssl.dart
Normal file
|
@ -0,0 +1,141 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
// Macro-benchmark for ffi with boringssl.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
import 'package:ffi/ffi.dart';
|
||||
|
||||
import 'digest.dart';
|
||||
import 'types.dart';
|
||||
|
||||
//
|
||||
// BoringSSL functions
|
||||
//
|
||||
|
||||
Uint8List inventData(int length) {
|
||||
final result = Uint8List(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
result[i] = i % 256;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Uint8List toUint8List(Bytes bytes, int length) {
|
||||
final result = Uint8List(length);
|
||||
final uint8bytes = bytes.asUint8Pointer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
result[i] = uint8bytes[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void copyFromUint8ListToTarget(Uint8List source, Data target) {
|
||||
final int length = source.length;
|
||||
final uint8target = target.asUint8Pointer();
|
||||
for (int i = 0; i < length; i++) {
|
||||
uint8target[i] = source[i];
|
||||
}
|
||||
}
|
||||
|
||||
String hash(Pointer<Data> data, int length, Pointer<EVP_MD> hashAlgorithm) {
|
||||
final context = EVP_MD_CTX_new();
|
||||
EVP_DigestInit(context, hashAlgorithm);
|
||||
EVP_DigestUpdate(context, data, length);
|
||||
final int resultSize = EVP_MD_CTX_size(context);
|
||||
final Pointer<Bytes> result = allocate<Uint8>(count: resultSize).cast();
|
||||
EVP_DigestFinal(context, result, nullptr);
|
||||
EVP_MD_CTX_free(context);
|
||||
final String hash = base64Encode(toUint8List(result.ref, resultSize));
|
||||
free(result);
|
||||
return hash;
|
||||
}
|
||||
|
||||
//
|
||||
// Benchmark fixtures.
|
||||
//
|
||||
|
||||
// Number of repeats: 1 && Length in bytes: 10000000
|
||||
// * CPU: Intel(R) Xeon(R) Gold 6154
|
||||
// * Architecture: x64
|
||||
// * 23000 - 52000000 us (without optimizations)
|
||||
// * 23000 - 30000 us (with optimizations)
|
||||
const int L = 1000; // Length of data in bytes.
|
||||
|
||||
final hashAlgorithm = EVP_sha512();
|
||||
|
||||
// Hash of generated data of `L` bytes with `hashAlgorithm`.
|
||||
const String expectedHash =
|
||||
"bNLtqb+cBZcSkCmwBUuB5DP2uLe0madetwXv10usGUFJg1sdGhTEi+aW5NWIRW1RKiLq56obV74rVurn014Iyw==";
|
||||
|
||||
/// This benchmark runs a digest algorithm on data residing in C memory.
|
||||
///
|
||||
/// This benchmark is intended as macro benchmark with a realistic workload.
|
||||
class DigestCMemory extends BenchmarkBase {
|
||||
DigestCMemory() : super("FfiBoringssl.DigestCMemory");
|
||||
|
||||
Pointer<Data> data; // Data in C memory that we want to digest.
|
||||
|
||||
void setup() {
|
||||
data = allocate<Uint8>(count: L).cast();
|
||||
copyFromUint8ListToTarget(inventData(L), data.ref);
|
||||
hash(data, L, hashAlgorithm);
|
||||
}
|
||||
|
||||
void teardown() {
|
||||
free(data);
|
||||
}
|
||||
|
||||
void run() {
|
||||
final String result = hash(data, L, hashAlgorithm);
|
||||
if (result != expectedHash) {
|
||||
throw Exception("$name: Unexpected result: $result");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This benchmark runs a digest algorithm on data residing in Dart memory.
|
||||
///
|
||||
/// This benchmark is intended as macro benchmark with a realistic workload.
|
||||
class DigestDartMemory extends BenchmarkBase {
|
||||
DigestDartMemory() : super("FfiBoringssl.DigestDartMemory");
|
||||
|
||||
Uint8List data; // Data in C memory that we want to digest.
|
||||
|
||||
void setup() {
|
||||
data = inventData(L);
|
||||
final Pointer<Data> dataInC = allocate<Uint8>(count: L).cast();
|
||||
copyFromUint8ListToTarget(data, dataInC.ref);
|
||||
hash(dataInC, L, hashAlgorithm);
|
||||
free(dataInC);
|
||||
}
|
||||
|
||||
void teardown() {}
|
||||
|
||||
void run() {
|
||||
final Pointer<Data> dataInC = allocate<Uint8>(count: L).cast();
|
||||
copyFromUint8ListToTarget(data, dataInC.ref);
|
||||
final String result = hash(dataInC, L, hashAlgorithm);
|
||||
free(dataInC);
|
||||
if (result != expectedHash) {
|
||||
throw Exception("$name: Unexpected result: $result");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Main driver.
|
||||
//
|
||||
|
||||
main() {
|
||||
final benchmarks = [
|
||||
() => DigestCMemory(),
|
||||
() => DigestDartMemory(),
|
||||
];
|
||||
benchmarks.forEach((benchmark) => benchmark().report());
|
||||
}
|
99
benchmarks/FfiBoringssl/dart2/digest.dart
Normal file
99
benchmarks/FfiBoringssl/dart2/digest.dart
Normal file
|
@ -0,0 +1,99 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'dlopen_helper.dart';
|
||||
import 'types.dart';
|
||||
|
||||
// See:
|
||||
// https://commondatastorage.googleapis.com/chromium-boringssl-docs/digest.h.html
|
||||
|
||||
DynamicLibrary openSsl() {
|
||||
// Force load crypto.
|
||||
dlopenPlatformSpecific("crypto",
|
||||
path: Platform.script.resolve("../native/out/").path);
|
||||
DynamicLibrary ssl = dlopenPlatformSpecific("ssl",
|
||||
path: Platform.script.resolve("../native/out/").path);
|
||||
return ssl;
|
||||
}
|
||||
|
||||
final DynamicLibrary ssl = openSsl();
|
||||
|
||||
/// The following functions return EVP_MD objects that implement the named
|
||||
/// hash function.
|
||||
///
|
||||
/// ```c
|
||||
/// const EVP_MD *EVP_sha512(void);
|
||||
/// ```
|
||||
final Pointer<EVP_MD> Function() EVP_sha512 =
|
||||
ssl.lookupFunction<Pointer<EVP_MD> Function(), Pointer<EVP_MD> Function()>(
|
||||
'EVP_sha512');
|
||||
|
||||
/// EVP_MD_CTX_new allocates and initialises a fresh EVP_MD_CTX and returns it,
|
||||
/// or NULL on allocation failure. The caller must use EVP_MD_CTX_free to
|
||||
/// release the resulting object.
|
||||
///
|
||||
/// ```c
|
||||
/// EVP_MD_CTX *EVP_MD_CTX_new(void);
|
||||
/// ```
|
||||
final Pointer<EVP_MD_CTX> Function() EVP_MD_CTX_new = ssl.lookupFunction<
|
||||
Pointer<EVP_MD_CTX> Function(),
|
||||
Pointer<EVP_MD_CTX> Function()>('EVP_MD_CTX_new');
|
||||
|
||||
/// EVP_MD_CTX_free calls EVP_MD_CTX_cleanup and then frees ctx itself.
|
||||
///
|
||||
/// ```c
|
||||
/// void EVP_MD_CTX_free(EVP_MD_CTX *ctx);
|
||||
/// ```
|
||||
final void Function(Pointer<EVP_MD_CTX>) EVP_MD_CTX_free = ssl.lookupFunction<
|
||||
Void Function(Pointer<EVP_MD_CTX>),
|
||||
void Function(Pointer<EVP_MD_CTX>)>('EVP_MD_CTX_free');
|
||||
|
||||
/// EVP_DigestInit acts like EVP_DigestInit_ex except that ctx is initialised
|
||||
/// before use.
|
||||
///
|
||||
/// ```c
|
||||
/// int EVP_DigestInit(EVP_MD_CTX *ctx, const EVP_MD *type);
|
||||
/// ```
|
||||
final int Function(Pointer<EVP_MD_CTX>, Pointer<EVP_MD>) EVP_DigestInit =
|
||||
ssl.lookupFunction<Int32 Function(Pointer<EVP_MD_CTX>, Pointer<EVP_MD>),
|
||||
int Function(Pointer<EVP_MD_CTX>, Pointer<EVP_MD>)>('EVP_DigestInit');
|
||||
|
||||
/// EVP_DigestUpdate hashes len bytes from data into the hashing operation
|
||||
/// in ctx. It returns one.
|
||||
///
|
||||
/// ```c
|
||||
/// int EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *data,
|
||||
/// size_t len);
|
||||
/// ```
|
||||
final int Function(Pointer<EVP_MD_CTX>, Pointer<Data>, int) EVP_DigestUpdate =
|
||||
ssl.lookupFunction<
|
||||
Int32 Function(Pointer<EVP_MD_CTX>, Pointer<Data>, IntPtr),
|
||||
int Function(
|
||||
Pointer<EVP_MD_CTX>, Pointer<Data>, int)>('EVP_DigestUpdate');
|
||||
|
||||
/// EVP_DigestFinal acts like EVP_DigestFinal_ex except that EVP_MD_CTX_cleanup
|
||||
/// is called on ctx before returning.
|
||||
///
|
||||
/// ```c
|
||||
/// int EVP_DigestFinal(EVP_MD_CTX *ctx, uint8_t *md_out,
|
||||
/// unsigned int *out_size);
|
||||
/// ```
|
||||
final int Function(Pointer<EVP_MD_CTX>, Pointer<Bytes>, Pointer<Uint32>)
|
||||
EVP_DigestFinal = ssl.lookupFunction<
|
||||
Int32 Function(Pointer<EVP_MD_CTX>, Pointer<Bytes>, Pointer<Uint32>),
|
||||
int Function(Pointer<EVP_MD_CTX>, Pointer<Bytes>,
|
||||
Pointer<Uint32>)>('EVP_DigestFinal');
|
||||
|
||||
/// EVP_MD_CTX_size returns the digest size of ctx, in bytes. It will crash if
|
||||
/// a digest hasn't been set on ctx.
|
||||
///
|
||||
/// ```c
|
||||
/// size_t EVP_MD_CTX_size(const EVP_MD_CTX *ctx);
|
||||
/// ```
|
||||
final int Function(Pointer<EVP_MD_CTX>) EVP_MD_CTX_size = ssl.lookupFunction<
|
||||
IntPtr Function(Pointer<EVP_MD_CTX>),
|
||||
int Function(Pointer<EVP_MD_CTX>)>('EVP_MD_CTX_size');
|
58
benchmarks/FfiBoringssl/dart2/dlopen_helper.dart
Normal file
58
benchmarks/FfiBoringssl/dart2/dlopen_helper.dart
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
const kArm = "arm";
|
||||
const kArm64 = "arm64";
|
||||
const kIa32 = "ia32";
|
||||
const kX64 = "x64";
|
||||
|
||||
// https://stackoverflow.com/questions/45125516/possible-values-for-uname-m
|
||||
final _unames = {
|
||||
"arm": kArm,
|
||||
"aarch64_be": kArm64,
|
||||
"aarch64": kArm64,
|
||||
"armv8b": kArm64,
|
||||
"armv8l": kArm64,
|
||||
"i386": kIa32,
|
||||
"i686": kIa32,
|
||||
"x86_64": kX64,
|
||||
};
|
||||
|
||||
String _checkRunningMode(String architecture) {
|
||||
// Check if we're running in 32bit mode.
|
||||
final int pointerSize = sizeOf<IntPtr>();
|
||||
if (pointerSize == 4 && architecture == kX64) return kIa32;
|
||||
if (pointerSize == 4 && architecture == kArm64) return kArm;
|
||||
|
||||
return architecture;
|
||||
}
|
||||
|
||||
String _architecture() {
|
||||
final String uname = Process.runSync("uname", ["-m"]).stdout.trim();
|
||||
final String architecture = _unames[uname];
|
||||
if (architecture == null)
|
||||
throw Exception("Unrecognized architecture: '$uname'");
|
||||
|
||||
// Check if we're running in 32bit mode.
|
||||
return _checkRunningMode(architecture);
|
||||
}
|
||||
|
||||
String _platformPath(String name, {String path = ""}) {
|
||||
if (Platform.isMacOS || Platform.isIOS)
|
||||
return "${path}mac/${_architecture()}/lib$name.dylib";
|
||||
|
||||
if (Platform.isWindows)
|
||||
return "${path}win/${_checkRunningMode(kX64)}/$name.dll";
|
||||
|
||||
// Unknown platforms default to Unix implementation.
|
||||
return "${path}linux/${_architecture()}/lib$name.so";
|
||||
}
|
||||
|
||||
DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
|
||||
final String fullPath = _platformPath(name, path: path);
|
||||
return DynamicLibrary.open(fullPath);
|
||||
}
|
25
benchmarks/FfiBoringssl/dart2/types.dart
Normal file
25
benchmarks/FfiBoringssl/dart2/types.dart
Normal file
|
@ -0,0 +1,25 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
/// digest algorithm.
|
||||
class EVP_MD extends Struct {}
|
||||
|
||||
/// digest context.
|
||||
class EVP_MD_CTX extends Struct {}
|
||||
|
||||
/// Type for `void*` used to represent opaque data.
|
||||
class Data extends Struct {
|
||||
static Data fromUint8Pointer(Pointer<Uint8> p) => p.cast<Data>().ref;
|
||||
|
||||
Pointer<Uint8> asUint8Pointer() => this.addressOf.cast();
|
||||
}
|
||||
|
||||
/// Type for `uint8_t*` used to represent byte data.
|
||||
class Bytes extends Struct {
|
||||
static Data fromUint8Pointer(Pointer<Uint8> p) => p.cast<Data>().ref;
|
||||
|
||||
Pointer<Uint8> asUint8Pointer() => this.addressOf.cast();
|
||||
}
|
1149
benchmarks/FfiCall/dart2/FfiCall.dart
Normal file
1149
benchmarks/FfiCall/dart2/FfiCall.dart
Normal file
File diff suppressed because it is too large
Load diff
58
benchmarks/FfiCall/dart2/dlopen_helper.dart
Normal file
58
benchmarks/FfiCall/dart2/dlopen_helper.dart
Normal file
|
@ -0,0 +1,58 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
const kArm = "arm";
|
||||
const kArm64 = "arm64";
|
||||
const kIa32 = "ia32";
|
||||
const kX64 = "x64";
|
||||
|
||||
// https://stackoverflow.com/questions/45125516/possible-values-for-uname-m
|
||||
final _unames = {
|
||||
"arm": kArm,
|
||||
"aarch64_be": kArm64,
|
||||
"aarch64": kArm64,
|
||||
"armv8b": kArm64,
|
||||
"armv8l": kArm64,
|
||||
"i386": kIa32,
|
||||
"i686": kIa32,
|
||||
"x86_64": kX64,
|
||||
};
|
||||
|
||||
String _checkRunningMode(String architecture) {
|
||||
// Check if we're running in 32bit mode.
|
||||
final int pointerSize = sizeOf<IntPtr>();
|
||||
if (pointerSize == 4 && architecture == kX64) return kIa32;
|
||||
if (pointerSize == 4 && architecture == kArm64) return kArm;
|
||||
|
||||
return architecture;
|
||||
}
|
||||
|
||||
String _architecture() {
|
||||
final String uname = Process.runSync("uname", ["-m"]).stdout.trim();
|
||||
final String architecture = _unames[uname];
|
||||
if (architecture == null)
|
||||
throw Exception("Unrecognized architecture: '$uname'");
|
||||
|
||||
// Check if we're running in 32bit mode.
|
||||
return _checkRunningMode(architecture);
|
||||
}
|
||||
|
||||
String _platformPath(String name, {String path = ""}) {
|
||||
if (Platform.isMacOS || Platform.isIOS)
|
||||
return "${path}mac/${_architecture()}/lib$name.dylib";
|
||||
|
||||
if (Platform.isWindows)
|
||||
return "${path}win/${_checkRunningMode(kX64)}/$name.dll";
|
||||
|
||||
// Unknown platforms default to Unix implementation.
|
||||
return "${path}linux/${_architecture()}/lib$name.so";
|
||||
}
|
||||
|
||||
DynamicLibrary dlopenPlatformSpecific(String name, {String path}) {
|
||||
final String fullPath = _platformPath(name, path: path);
|
||||
return DynamicLibrary.open(fullPath);
|
||||
}
|
5
benchmarks/FfiCall/dart2/native/.gitignore
vendored
Normal file
5
benchmarks/FfiCall/dart2/native/.gitignore
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# Copyright (c) 2019, 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.
|
||||
|
||||
out/
|
60
benchmarks/FfiCall/dart2/native/Makefile
Normal file
60
benchmarks/FfiCall/dart2/native/Makefile
Normal file
|
@ -0,0 +1,60 @@
|
|||
# Copyright (c) 2019, 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.
|
||||
|
||||
# TODO(37531): Remove this makefile and build with sdk instead when
|
||||
# benchmark runner gets support for that.
|
||||
|
||||
CC=gcc
|
||||
CCARM=arm-linux-gnueabihf-gcc
|
||||
CCARM64=aarch64-linux-gnu-gcc
|
||||
CFLAGS=-Wall -g -O -fPIC
|
||||
|
||||
# Bump this whenever the benchmark is updated.
|
||||
VERSION=1
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: out/linux/x64/libnative_functions.so out/linux/ia32/libnative_functions.so out/linux/arm64/libnative_functions.so out/linux/arm/libnative_functions.so
|
||||
|
||||
cipd:
|
||||
cipd create -name dart/benchmarks/fficall -in out -install-mode copy -tag version:$(VERSION)
|
||||
|
||||
clean:
|
||||
rm -rf *.o *.so out
|
||||
|
||||
out/linux/x64:
|
||||
mkdir -p out/linux/x64
|
||||
|
||||
out/linux/x64/native_functions.o: native_functions.c | out/linux/x64
|
||||
$(CC) $(CFLAGS) -c -o $@ native_functions.c
|
||||
|
||||
out/linux/x64/libnative_functions.so: out/linux/x64/native_functions.o
|
||||
$(CC) $(CFLAGS) -s -shared -o $@ out/linux/x64/native_functions.o
|
||||
|
||||
out/linux/ia32:
|
||||
mkdir -p out/linux/ia32
|
||||
|
||||
out/linux/ia32/native_functions.o: native_functions.c | out/linux/ia32
|
||||
$(CC) $(CFLAGS) -m32 -c -o $@ native_functions.c
|
||||
|
||||
out/linux/ia32/libnative_functions.so: out/linux/ia32/native_functions.o
|
||||
$(CC) $(CFLAGS) -m32 -s -shared -o $@ out/linux/ia32/native_functions.o
|
||||
|
||||
out/linux/arm64:
|
||||
mkdir -p out/linux/arm64
|
||||
|
||||
out/linux/arm64/native_functions.o: native_functions.c | out/linux/arm64
|
||||
$(CCARM64) $(CFLAGS) -c -o $@ native_functions.c
|
||||
|
||||
out/linux/arm64/libnative_functions.so: out/linux/arm64/native_functions.o
|
||||
$(CCARM64) $(CFLAGS) -s -shared -o $@ out/linux/arm64/native_functions.o
|
||||
|
||||
out/linux/arm:
|
||||
mkdir -p out/linux/arm
|
||||
|
||||
out/linux/arm/native_functions.o: native_functions.c | out/linux/arm
|
||||
$(CCARM) $(CFLAGS) -c -o $@ native_functions.c
|
||||
|
||||
out/linux/arm/libnative_functions.so: out/linux/arm/native_functions.o
|
||||
$(CCARM) $(CFLAGS) -s -shared -o $@ out/linux/arm/native_functions.o
|
132
benchmarks/FfiCall/dart2/native/native_functions.c
Normal file
132
benchmarks/FfiCall/dart2/native/native_functions.c
Normal file
|
@ -0,0 +1,132 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint8_t Function1Uint8(uint8_t x) { return x + 42; }
|
||||
|
||||
uint16_t Function1Uint16(uint16_t x) { return x + 42; }
|
||||
|
||||
uint32_t Function1Uint32(uint32_t x) { return x + 42; }
|
||||
|
||||
uint64_t Function1Uint64(uint64_t x) { return x + 42; }
|
||||
|
||||
int8_t Function1Int8(int8_t x) { return x + 42; }
|
||||
|
||||
int16_t Function1Int16(int16_t x) { return x + 42; }
|
||||
|
||||
int32_t Function1Int32(int32_t x) { return x + 42; }
|
||||
|
||||
int32_t Function2Int32(int32_t a, int32_t b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int32_t Function4Int32(int32_t a, int32_t b, int32_t c, int32_t d) {
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
int32_t Function10Int32(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e,
|
||||
int32_t f, int32_t g, int32_t h, int32_t i, int32_t j) {
|
||||
return a + b + c + d + e + f + g + h + i + j;
|
||||
}
|
||||
|
||||
int32_t Function20Int32(int32_t a, int32_t b, int32_t c, int32_t d, int32_t e,
|
||||
int32_t f, int32_t g, int32_t h, int32_t i, int32_t j,
|
||||
int32_t k, int32_t l, int32_t m, int32_t n, int32_t o,
|
||||
int32_t p, int32_t q, int32_t r, int32_t s, int32_t t) {
|
||||
return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o +
|
||||
p + q + r + s + t;
|
||||
}
|
||||
|
||||
int64_t Function1Int64(int64_t x) { return x + 42; }
|
||||
|
||||
int64_t Function2Int64(int64_t a, int64_t b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
int64_t Function4Int64(int64_t a, int64_t b, int64_t c, int64_t d) {
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
int64_t Function10Int64(int64_t a, int64_t b, int64_t c, int64_t d, int64_t e,
|
||||
int64_t f, int64_t g, int64_t h, int64_t i, int64_t j) {
|
||||
return a + b + c + d + e + f + g + h + i + j;
|
||||
}
|
||||
|
||||
int64_t Function20Int64(int64_t a, int64_t b, int64_t c, int64_t d, int64_t e,
|
||||
int64_t f, int64_t g, int64_t h, int64_t i, int64_t j,
|
||||
int64_t k, int64_t l, int64_t m, int64_t n, int64_t o,
|
||||
int64_t p, int64_t q, int64_t r, int64_t s, int64_t t) {
|
||||
return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o +
|
||||
p + q + r + s + t;
|
||||
}
|
||||
|
||||
float Function1Float(float x) { return x + 42.0f; }
|
||||
|
||||
float Function2Float(float a, float b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
float Function4Float(float a, float b, float c, float d) {
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
float Function10Float(float a, float b, float c, float d, float e, float f,
|
||||
float g, float h, float i, float j) {
|
||||
return a + b + c + d + e + f + g + h + i + j;
|
||||
}
|
||||
|
||||
float Function20Float(float a, float b, float c, float d, float e, float f,
|
||||
float g, float h, float i, float j, float k, float l,
|
||||
float m, float n, float o, float p, float q, float r,
|
||||
float s, float t) {
|
||||
return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o + p +
|
||||
q + r + s + t;
|
||||
}
|
||||
|
||||
double Function1Double(double x) { return x + 42.0; }
|
||||
|
||||
double Function2Double(double a, double b) {
|
||||
return a + b;
|
||||
}
|
||||
|
||||
double Function4Double(double a, double b, double c, double d) {
|
||||
return a + b + c + d;
|
||||
}
|
||||
|
||||
double Function10Double(double a, double b, double c, double d, double e,
|
||||
double f, double g, double h, double i, double j) {
|
||||
return a + b + c + d + e + f + g + h + i + j;
|
||||
}
|
||||
|
||||
double Function20Double(double a, double b, double c, double d, double e,
|
||||
double f, double g, double h, double i, double j,
|
||||
double k, double l, double m, double n, double o,
|
||||
double p, double q, double r, double s, double t) {
|
||||
return a + b + c + d + e + f + g + h + i + j + k + l + m + n + o +
|
||||
p + q + r + s + t;
|
||||
}
|
||||
|
||||
uint8_t *Function1PointerUint8(uint8_t *a) { return a + 1; }
|
||||
|
||||
uint8_t *Function2PointerUint8(uint8_t *a, uint8_t *b) { return a + 1; }
|
||||
|
||||
uint8_t *Function4PointerUint8(uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d) {
|
||||
return a + 1;
|
||||
}
|
||||
|
||||
uint8_t *Function10PointerUint8(uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d,
|
||||
uint8_t *e, uint8_t *f, uint8_t *g, uint8_t *h,
|
||||
uint8_t *i, uint8_t *j) {
|
||||
return a + 1;
|
||||
}
|
||||
|
||||
uint8_t *Function20PointerUint8(uint8_t *a, uint8_t *b, uint8_t *c, uint8_t *d,
|
||||
uint8_t *e, uint8_t *f, uint8_t *g, uint8_t *h,
|
||||
uint8_t *i, uint8_t *j, uint8_t *k, uint8_t *l,
|
||||
uint8_t *m, uint8_t *n, uint8_t *o, uint8_t *p,
|
||||
uint8_t *q, uint8_t *r, uint8_t *s,
|
||||
uint8_t *t) {
|
||||
return a + 1;
|
||||
}
|
429
benchmarks/FfiMemory/dart2/FfiMemory.dart
Normal file
429
benchmarks/FfiMemory/dart2/FfiMemory.dart
Normal file
|
@ -0,0 +1,429 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
// TODO(37581): Generate this file.
|
||||
|
||||
// Micro-benchmarks for ffi memory stores and loads.
|
||||
//
|
||||
// These micro benchmarks track the speed of reading and writing C memory from
|
||||
// Dart with a specific marshalling and unmarshalling of data.
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
|
||||
//
|
||||
// Pointer store.
|
||||
//
|
||||
|
||||
void doStoreInt8(Pointer<Int8> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreUint8(Pointer<Uint8> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreInt16(Pointer<Int16> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreUint16(Pointer<Uint16> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreInt32(Pointer<Int32> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreUint32(Pointer<Uint32> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreInt64(Pointer<Int64> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreUint64(Pointer<Uint64> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreFloat(Pointer<Float> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreDouble(Pointer<Double> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void doStorePointer(
|
||||
Pointer<Pointer<Int8>> pointer, int length, Pointer<Int8> data) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = data;
|
||||
}
|
||||
}
|
||||
|
||||
void doStoreInt64Mint(Pointer<Int64> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i] = 0x7FFFFFFFFFFFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Pointer load.
|
||||
//
|
||||
|
||||
int doLoadInt8(Pointer<Int8> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadUint8(Pointer<Uint8> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadInt16(Pointer<Int16> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadUint16(Pointer<Uint16> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadInt32(Pointer<Int32> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadUint32(Pointer<Uint32> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadInt64(Pointer<Int64> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
int doLoadUint64(Pointer<Uint64> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
double doLoadFloat(Pointer<Float> pointer, int length) {
|
||||
double x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
double doLoadDouble(Pointer<Double> pointer, int length) {
|
||||
double x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
// Aggregates pointers through aggregrating their addresses.
|
||||
int doLoadPointer(Pointer<Pointer<Int8>> pointer, int length) {
|
||||
Pointer<Int8> x;
|
||||
int address_xor = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x = pointer[i];
|
||||
address_xor ^= x.address;
|
||||
}
|
||||
return address_xor;
|
||||
}
|
||||
|
||||
int doLoadInt64Mint(Pointer<Int64> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i];
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
//
|
||||
// Benchmark fixtures.
|
||||
//
|
||||
|
||||
// Number of repeats: 1000
|
||||
// * CPU: Intel(R) Xeon(R) Gold 6154
|
||||
// * Architecture: x64
|
||||
// * 48000 - 125000 us (without optimizations)
|
||||
// * 14 - ??? us (expected with optimizations, on par with typed data)
|
||||
const N = 1000;
|
||||
|
||||
class PointerInt8 extends BenchmarkBase {
|
||||
Pointer<Int8> pointer;
|
||||
PointerInt8() : super("FfiMemory.PointerInt8");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreInt8(pointer, N);
|
||||
final int x = doLoadInt8(pointer, N);
|
||||
if (x != N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerUint8 extends BenchmarkBase {
|
||||
Pointer<Uint8> pointer;
|
||||
PointerUint8() : super("FfiMemory.PointerUint8");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreUint8(pointer, N);
|
||||
final int x = doLoadUint8(pointer, N);
|
||||
if (x != N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerInt16 extends BenchmarkBase {
|
||||
Pointer<Int16> pointer;
|
||||
PointerInt16() : super("FfiMemory.PointerInt16");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreInt16(pointer, N);
|
||||
final int x = doLoadInt16(pointer, N);
|
||||
if (x != N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerUint16 extends BenchmarkBase {
|
||||
Pointer<Uint16> pointer;
|
||||
PointerUint16() : super("FfiMemory.PointerUint16");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreUint16(pointer, N);
|
||||
final int x = doLoadUint16(pointer, N);
|
||||
if (x != N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerInt32 extends BenchmarkBase {
|
||||
Pointer<Int32> pointer;
|
||||
PointerInt32() : super("FfiMemory.PointerInt32");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreInt32(pointer, N);
|
||||
final int x = doLoadInt32(pointer, N);
|
||||
if (x != N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerUint32 extends BenchmarkBase {
|
||||
Pointer<Uint32> pointer;
|
||||
PointerUint32() : super("FfiMemory.PointerUint32");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreUint32(pointer, N);
|
||||
final int x = doLoadUint32(pointer, N);
|
||||
if (x != N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerInt64 extends BenchmarkBase {
|
||||
Pointer<Int64> pointer;
|
||||
PointerInt64() : super("FfiMemory.PointerInt64");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreInt64(pointer, N);
|
||||
final int x = doLoadInt64(pointer, N);
|
||||
if (x != N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerUint64 extends BenchmarkBase {
|
||||
Pointer<Uint64> pointer;
|
||||
PointerUint64() : super("FfiMemory.PointerUint64");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreUint64(pointer, N);
|
||||
final int x = doLoadUint64(pointer, N);
|
||||
if (x != N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerFloat extends BenchmarkBase {
|
||||
Pointer<Float> pointer;
|
||||
PointerFloat() : super("FfiMemory.PointerFloat");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreFloat(pointer, N);
|
||||
final double x = doLoadFloat(pointer, N);
|
||||
if (0.99 * N > x || x > 1.01 * N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerDouble extends BenchmarkBase {
|
||||
Pointer<Double> pointer;
|
||||
PointerDouble() : super("FfiMemory.PointerDouble");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreDouble(pointer, N);
|
||||
final double x = doLoadDouble(pointer, N);
|
||||
if (0.99 * N > x || x > 1.01 * N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerPointer extends BenchmarkBase {
|
||||
Pointer<Pointer<Int8>> pointer;
|
||||
Pointer<Int8> data;
|
||||
PointerPointer() : super("FfiMemory.PointerPointer");
|
||||
|
||||
void setup() {
|
||||
pointer = allocate(count: N);
|
||||
data = allocate();
|
||||
}
|
||||
|
||||
void teardown() {
|
||||
free(pointer);
|
||||
free(data);
|
||||
}
|
||||
|
||||
void run() {
|
||||
doStorePointer(pointer, N, data);
|
||||
final int x = doLoadPointer(pointer, N);
|
||||
if (x != 0 || x == data.address) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PointerInt64Mint extends BenchmarkBase {
|
||||
Pointer<Int64> pointer;
|
||||
PointerInt64Mint() : super("FfiMemory.PointerInt64Mint");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreInt64Mint(pointer, N);
|
||||
final int x = doLoadInt64Mint(pointer, N);
|
||||
// Using overflow semantics in aggregation.
|
||||
if (x != -N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Main driver.
|
||||
//
|
||||
|
||||
main() {
|
||||
final benchmarks = [
|
||||
() => PointerInt8(),
|
||||
() => PointerUint8(),
|
||||
() => PointerInt16(),
|
||||
() => PointerUint16(),
|
||||
() => PointerInt32(),
|
||||
() => PointerUint32(),
|
||||
() => PointerInt64(),
|
||||
() => PointerInt64Mint(),
|
||||
() => PointerUint64(),
|
||||
() => PointerFloat(),
|
||||
() => PointerDouble(),
|
||||
() => PointerPointer(),
|
||||
];
|
||||
benchmarks.forEach((benchmark) => benchmark().report());
|
||||
}
|
121
benchmarks/FfiStruct/dart2/FfiStruct.dart
Normal file
121
benchmarks/FfiStruct/dart2/FfiStruct.dart
Normal file
|
@ -0,0 +1,121 @@
|
|||
// Copyright (c) 2019, 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 ffi struct field stores and loads.
|
||||
//
|
||||
// Only tests a single field because the FfiMemory benchmark already tests loads
|
||||
// and stores of different field sizes.
|
||||
|
||||
import 'dart:ffi';
|
||||
|
||||
import 'package:ffi/ffi.dart';
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
|
||||
//
|
||||
// Struct field store (plus Pointer elementAt and load).
|
||||
//
|
||||
|
||||
void doStoreInt32(Pointer<VeryLargeStruct> pointer, int length) {
|
||||
for (int i = 0; i < length; i++) {
|
||||
pointer[i].c = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Struct field load (plus Pointer elementAt and load).
|
||||
//
|
||||
|
||||
int doLoadInt32(Pointer<VeryLargeStruct> pointer, int length) {
|
||||
int x = 0;
|
||||
for (int i = 0; i < length; i++) {
|
||||
x += pointer[i].c;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
//
|
||||
// Benchmark fixture.
|
||||
//
|
||||
|
||||
// Number of repeats: 1000
|
||||
// * CPU: Intel(R) Xeon(R) Gold 6154
|
||||
// * Architecture: x64
|
||||
// * 150000 - 465000 us (without optimizations)
|
||||
// * 14 - ??? us (expected with optimizations, on par with typed data)
|
||||
const N = 1000;
|
||||
|
||||
class FieldLoadStore extends BenchmarkBase {
|
||||
Pointer<VeryLargeStruct> pointer;
|
||||
FieldLoadStore() : super("FfiStruct.FieldLoadStore");
|
||||
|
||||
void setup() => pointer = allocate(count: N);
|
||||
void teardown() => free(pointer);
|
||||
|
||||
void run() {
|
||||
doStoreInt32(pointer, N);
|
||||
final int x = doLoadInt32(pointer, N);
|
||||
if (x != N) {
|
||||
throw Exception("$name: Unexpected result: $x");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Main driver.
|
||||
//
|
||||
|
||||
main() {
|
||||
final benchmarks = [
|
||||
() => FieldLoadStore(),
|
||||
];
|
||||
benchmarks.forEach((benchmark) => benchmark().report());
|
||||
}
|
||||
|
||||
//
|
||||
// Test struct.
|
||||
//
|
||||
class VeryLargeStruct extends Struct {
|
||||
@Int8()
|
||||
int a;
|
||||
|
||||
@Int16()
|
||||
int b;
|
||||
|
||||
@Int32()
|
||||
int c;
|
||||
|
||||
@Int64()
|
||||
int d;
|
||||
|
||||
@Uint8()
|
||||
int e;
|
||||
|
||||
@Uint16()
|
||||
int f;
|
||||
|
||||
@Uint32()
|
||||
int g;
|
||||
|
||||
@Uint64()
|
||||
int h;
|
||||
|
||||
@IntPtr()
|
||||
int i;
|
||||
|
||||
@Double()
|
||||
double j;
|
||||
|
||||
@Float()
|
||||
double k;
|
||||
|
||||
Pointer<VeryLargeStruct> parent;
|
||||
|
||||
@IntPtr()
|
||||
int numChildren;
|
||||
|
||||
Pointer<VeryLargeStruct> children;
|
||||
|
||||
@Int8()
|
||||
int smallLastField;
|
||||
}
|
186
benchmarks/Isolate/dart2/Isolate.dart
Normal file
186
benchmarks/Isolate/dart2/Isolate.dart
Normal file
|
@ -0,0 +1,186 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:benchmark_harness/benchmark_harness.dart'
|
||||
show PrintEmitter, ScoreEmitter;
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
class SendReceiveBytes extends AsyncBenchmarkBase {
|
||||
SendReceiveBytes(String name,
|
||||
{@required int this.size, @required bool this.useTransferable})
|
||||
: super(name);
|
||||
|
||||
@override
|
||||
Future<void> run() async {
|
||||
await helper.run();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> setup() async {
|
||||
helper = SendReceiveHelper(size, useTransferable: useTransferable);
|
||||
await helper.setup();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> teardown() async {
|
||||
await helper.finalize();
|
||||
}
|
||||
|
||||
final bool useTransferable;
|
||||
final int size;
|
||||
SendReceiveHelper helper;
|
||||
}
|
||||
|
||||
// Identical to BenchmarkBase from package:benchmark_harness but async.
|
||||
abstract class AsyncBenchmarkBase {
|
||||
final String name;
|
||||
final ScoreEmitter emitter;
|
||||
|
||||
Future<void> run();
|
||||
Future<void> setup();
|
||||
Future<void> teardown();
|
||||
|
||||
const AsyncBenchmarkBase(this.name, {this.emitter = const PrintEmitter()});
|
||||
|
||||
// Returns the number of microseconds per call.
|
||||
Future<double> measureFor(int minimumMillis) async {
|
||||
final minimumMicros = minimumMillis * 1000;
|
||||
int iter = 0;
|
||||
final watch = Stopwatch();
|
||||
watch.start();
|
||||
int elapsed = 0;
|
||||
while (elapsed < minimumMicros) {
|
||||
await run();
|
||||
elapsed = watch.elapsedMicroseconds;
|
||||
iter++;
|
||||
}
|
||||
return elapsed / iter;
|
||||
}
|
||||
|
||||
// Measures the score for the benchmark and returns it.
|
||||
Future<double> measure() async {
|
||||
await setup();
|
||||
await measureFor(500); // warm-up
|
||||
final result = await measureFor(4000); // actual measurement
|
||||
await teardown();
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<void> report() async {
|
||||
emitter.emit(name, await measure());
|
||||
}
|
||||
}
|
||||
|
||||
class StartMessage {
|
||||
final SendPort sendPort;
|
||||
final bool useTransferable;
|
||||
final int size;
|
||||
|
||||
StartMessage(this.sendPort, this.useTransferable, this.size);
|
||||
}
|
||||
|
||||
// Measures how long sending and receiving of [size]-length Uint8List takes.
|
||||
class SendReceiveHelper {
|
||||
SendReceiveHelper(this.size, {@required bool this.useTransferable});
|
||||
|
||||
Future<void> setup() async {
|
||||
data = new Uint8List(size);
|
||||
|
||||
port = ReceivePort();
|
||||
inbox = StreamIterator<dynamic>(port);
|
||||
workerCompleted = Completer<bool>();
|
||||
workerExitedPort = ReceivePort()
|
||||
..listen((_) => workerCompleted.complete(true));
|
||||
worker = await Isolate.spawn(
|
||||
isolate, StartMessage(port.sendPort, useTransferable, size),
|
||||
onExit: workerExitedPort.sendPort);
|
||||
await inbox.moveNext();
|
||||
outbox = inbox.current;
|
||||
}
|
||||
|
||||
Future<void> finalize() async {
|
||||
outbox.send(null);
|
||||
await workerCompleted.future;
|
||||
workerExitedPort.close();
|
||||
port.close();
|
||||
}
|
||||
|
||||
// Send data to worker, wait for an answer.
|
||||
Future<void> run() async {
|
||||
outbox.send(packageList(data, useTransferable));
|
||||
await inbox.moveNext();
|
||||
final received = inbox.current;
|
||||
if (useTransferable) {
|
||||
final TransferableTypedData transferable = received;
|
||||
transferable.materialize();
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List data;
|
||||
ReceivePort port;
|
||||
StreamIterator<dynamic> inbox;
|
||||
SendPort outbox;
|
||||
Isolate worker;
|
||||
Completer<bool> workerCompleted;
|
||||
ReceivePort workerExitedPort;
|
||||
final int size;
|
||||
final bool useTransferable;
|
||||
}
|
||||
|
||||
packageList(Uint8List data, bool useTransferable) =>
|
||||
useTransferable ? TransferableTypedData.fromList(<Uint8List>[data]) : data;
|
||||
|
||||
Future<void> isolate(StartMessage startMessage) async {
|
||||
final port = ReceivePort();
|
||||
final inbox = StreamIterator<dynamic>(port);
|
||||
final data = Uint8List.view(new Uint8List(startMessage.size).buffer);
|
||||
|
||||
startMessage.sendPort.send(port.sendPort);
|
||||
while (true) {
|
||||
await inbox.moveNext();
|
||||
final received = inbox.current;
|
||||
if (received == null) {
|
||||
break;
|
||||
}
|
||||
if (startMessage.useTransferable) {
|
||||
final TransferableTypedData transferable = received;
|
||||
transferable.materialize();
|
||||
}
|
||||
startMessage.sendPort.send(packageList(data, startMessage.useTransferable));
|
||||
}
|
||||
port.close();
|
||||
}
|
||||
|
||||
class SizeName {
|
||||
const SizeName(this.size, this.name);
|
||||
|
||||
final int size;
|
||||
final String name;
|
||||
}
|
||||
|
||||
final List<SizeName> sizes = <SizeName>[
|
||||
SizeName(1 * 1024, "1KB"),
|
||||
SizeName(10 * 1024, "10KB"),
|
||||
SizeName(100 * 1024, "100KB"),
|
||||
SizeName(1 * 1024 * 1024, "1MB"),
|
||||
SizeName(10 * 1024 * 1024, "10MB"),
|
||||
SizeName(100 * 1024 * 1024, "100MB")
|
||||
];
|
||||
|
||||
Future<void> main() async {
|
||||
for (SizeName sizeName in sizes) {
|
||||
await SendReceiveBytes("Isolate.SendReceiveBytes${sizeName.name}",
|
||||
size: sizeName.size, useTransferable: false)
|
||||
.report();
|
||||
await SendReceiveBytes(
|
||||
"Isolate.SendReceiveBytesTransferable${sizeName.name}",
|
||||
size: sizeName.size,
|
||||
useTransferable: true)
|
||||
.report();
|
||||
}
|
||||
}
|
161
benchmarks/IsolateJson/dart2/IsolateJson.dart
Normal file
161
benchmarks/IsolateJson/dart2/IsolateJson.dart
Normal file
|
@ -0,0 +1,161 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:benchmark_harness/benchmark_harness.dart' show BenchmarkBase;
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'runtime/tests/vm/dart/export_sendAndExit_helper.dart' show sendAndExit;
|
||||
|
||||
class JsonDecodingBenchmark {
|
||||
JsonDecodingBenchmark(this.name,
|
||||
{@required this.sample,
|
||||
@required this.numTasks,
|
||||
@required this.useSendAndExit});
|
||||
|
||||
Future<void> report() async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
// Benchmark harness counts 10 iterations as one.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
final decodedFutures = <Future>[];
|
||||
for (int i = 0; i < numTasks; i++) {
|
||||
decodedFutures.add(decodeJson(useSendAndExit, sample));
|
||||
}
|
||||
await Future.wait(decodedFutures);
|
||||
}
|
||||
|
||||
print("$name(RunTime): ${stopwatch.elapsedMicroseconds} us.");
|
||||
}
|
||||
|
||||
final String name;
|
||||
final Uint8List sample;
|
||||
final int numTasks;
|
||||
final bool useSendAndExit;
|
||||
}
|
||||
|
||||
Uint8List createSampleJson(final size) {
|
||||
final list = List.generate(size, (i) => i);
|
||||
final map = <dynamic, dynamic>{};
|
||||
for (int i = 0; i < size; i++) {
|
||||
map['$i'] = list;
|
||||
}
|
||||
return utf8.encode(json.encode(map));
|
||||
}
|
||||
|
||||
class JsonDecodeRequest {
|
||||
final bool useSendAndExit;
|
||||
final SendPort sendPort;
|
||||
final Uint8List encodedJson;
|
||||
const JsonDecodeRequest(this.useSendAndExit, this.sendPort, this.encodedJson);
|
||||
}
|
||||
|
||||
Future<Map> decodeJson(bool useSendAndExit, Uint8List encodedJson) async {
|
||||
final port = ReceivePort();
|
||||
final inbox = StreamIterator<dynamic>(port);
|
||||
final completer = Completer<bool>();
|
||||
final workerExitedPort = RawReceivePort((v) {
|
||||
completer.complete(true);
|
||||
});
|
||||
final workerErroredPort = RawReceivePort((v) {
|
||||
stderr.writeln('worker errored out $v');
|
||||
completer.completeError(true);
|
||||
});
|
||||
await Isolate.spawn(jsonDecodingIsolate,
|
||||
JsonDecodeRequest(useSendAndExit, port.sendPort, encodedJson),
|
||||
onError: workerErroredPort.sendPort, onExit: workerExitedPort.sendPort);
|
||||
await completer.future;
|
||||
workerExitedPort.close();
|
||||
workerErroredPort.close();
|
||||
await inbox.moveNext();
|
||||
final decodedJson = inbox.current;
|
||||
port.close();
|
||||
return decodedJson;
|
||||
}
|
||||
|
||||
Future<void> jsonDecodingIsolate(JsonDecodeRequest request) async {
|
||||
final result = json.decode(utf8.decode(request.encodedJson));
|
||||
if (request.useSendAndExit) {
|
||||
sendAndExit(request.sendPort, result);
|
||||
} else {
|
||||
request.sendPort.send(result);
|
||||
}
|
||||
}
|
||||
|
||||
class SyncJsonDecodingBenchmark extends BenchmarkBase {
|
||||
SyncJsonDecodingBenchmark(String name,
|
||||
{@required this.sample, @required this.iterations})
|
||||
: super(name);
|
||||
|
||||
@override
|
||||
void run() {
|
||||
int l = 0;
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
final Map map = json.decode(utf8.decode(sample));
|
||||
l += map.length;
|
||||
}
|
||||
assert(l > 0);
|
||||
}
|
||||
|
||||
final Uint8List sample;
|
||||
final int iterations;
|
||||
}
|
||||
|
||||
class BenchmarkConfig {
|
||||
BenchmarkConfig(this.suffix, this.sample);
|
||||
|
||||
final String suffix;
|
||||
final Uint8List sample;
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
final jsonString =
|
||||
File('benchmarks/IsolateJson/dart/sample.json').readAsStringSync();
|
||||
final json250KB = utf8.encode(jsonString); // 294356 bytes
|
||||
final decoded = json.decode(utf8.decode(json250KB));
|
||||
final decoded1MB = <dynamic, dynamic>{
|
||||
"1": decoded["1"],
|
||||
"2": decoded["1"],
|
||||
"3": decoded["1"],
|
||||
"4": decoded["1"],
|
||||
};
|
||||
final json1MB = utf8.encode(json.encode(decoded1MB)); // 1177397 bytes
|
||||
decoded["1"] = (decoded["1"] as List).sublist(0, 200);
|
||||
final json100KB = utf8.encode(json.encode(decoded)); // 104685 bytes
|
||||
decoded["1"] = (decoded["1"] as List).sublist(0, 100);
|
||||
final json50KB = utf8.encode(json.encode(decoded)); // 51760 bytes
|
||||
|
||||
final configs = <BenchmarkConfig>[
|
||||
BenchmarkConfig("50KB", json50KB),
|
||||
BenchmarkConfig("100KB", json100KB),
|
||||
BenchmarkConfig("250KB", json250KB),
|
||||
BenchmarkConfig("1MB", json1MB),
|
||||
];
|
||||
|
||||
for (BenchmarkConfig config in configs) {
|
||||
for (final iterations in <int>[1, 4]) {
|
||||
await JsonDecodingBenchmark(
|
||||
"IsolateJson.Decode${config.suffix}x$iterations",
|
||||
useSendAndExit: false,
|
||||
sample: config.sample,
|
||||
numTasks: iterations)
|
||||
.report();
|
||||
await JsonDecodingBenchmark(
|
||||
"IsolateJson.SendAndExit_Decode${config.suffix}x$iterations",
|
||||
useSendAndExit: true,
|
||||
sample: config.sample,
|
||||
numTasks: iterations)
|
||||
.report();
|
||||
SyncJsonDecodingBenchmark(
|
||||
"IsolateJson.SyncDecode${config.suffix}x$iterations",
|
||||
sample: config.sample,
|
||||
iterations: iterations)
|
||||
.report();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export 'dart:_internal' show sendAndExit;
|
1
benchmarks/IsolateJson/dart2/sample.json
Normal file
1
benchmarks/IsolateJson/dart2/sample.json
Normal file
File diff suppressed because one or more lines are too long
165
benchmarks/IsolateSpawn/dart2/IsolateSpawn.dart
Normal file
165
benchmarks/IsolateSpawn/dart2/IsolateSpawn.dart
Normal file
|
@ -0,0 +1,165 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import 'package:compiler/src/dart2js.dart' as dart2js_main;
|
||||
|
||||
class SpawnLatency {
|
||||
SpawnLatency(this.name);
|
||||
|
||||
Future<ResultMessageLatency> run() async {
|
||||
final completerResult = Completer();
|
||||
final receivePort = ReceivePort()..listen(completerResult.complete);
|
||||
final Completer<DateTime> isolateExitedCompleter = Completer<DateTime>();
|
||||
final onExitReceivePort = ReceivePort()
|
||||
..listen((_) {
|
||||
isolateExitedCompleter.complete(DateTime.now());
|
||||
});
|
||||
final DateTime beforeSpawn = DateTime.now();
|
||||
await Isolate.spawn(
|
||||
isolateCompiler, StartMessageLatency(receivePort.sendPort, beforeSpawn),
|
||||
onExit: onExitReceivePort.sendPort,
|
||||
onError: onExitReceivePort.sendPort);
|
||||
final DateTime afterSpawn = DateTime.now();
|
||||
|
||||
final ResultMessageLatency result = await completerResult.future;
|
||||
receivePort.close();
|
||||
final DateTime isolateExited = await isolateExitedCompleter.future;
|
||||
result.timeToExitUs = isolateExited.difference(beforeSpawn).inMicroseconds;
|
||||
result.timeToIsolateSpawnUs =
|
||||
afterSpawn.difference(beforeSpawn).inMicroseconds;
|
||||
onExitReceivePort.close();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Future<AggregatedResultMessageLatency> measureFor(int minimumMillis) async {
|
||||
final minimumMicros = minimumMillis * 1000;
|
||||
final watch = Stopwatch()..start();
|
||||
final Metric toAfterIsolateSpawnUs = LatencyMetric("${name}ToAfterSpawn");
|
||||
final Metric toStartRunningCodeUs = LatencyMetric("${name}ToStartRunning");
|
||||
final Metric toFinishRunningCodeUs =
|
||||
LatencyMetric("${name}ToFinishRunning");
|
||||
final Metric toExitUs = LatencyMetric("${name}ToExit");
|
||||
while (watch.elapsedMicroseconds < minimumMicros) {
|
||||
final ResultMessageLatency result = await run();
|
||||
toAfterIsolateSpawnUs.add(result.timeToIsolateSpawnUs);
|
||||
toStartRunningCodeUs.add(result.timeToStartRunningCodeUs);
|
||||
toFinishRunningCodeUs.add(result.timeToFinishRunningCodeUs);
|
||||
toExitUs.add(result.timeToExitUs);
|
||||
}
|
||||
return AggregatedResultMessageLatency(toAfterIsolateSpawnUs,
|
||||
toStartRunningCodeUs, toFinishRunningCodeUs, toExitUs);
|
||||
}
|
||||
|
||||
Future<AggregatedResultMessageLatency> measure() async {
|
||||
await measureFor(500); // warm-up
|
||||
return measureFor(4000); // actual measurement
|
||||
}
|
||||
|
||||
Future<void> report() async {
|
||||
final AggregatedResultMessageLatency result = await measure();
|
||||
print(result);
|
||||
}
|
||||
|
||||
final String name;
|
||||
RawReceivePort receivePort;
|
||||
}
|
||||
|
||||
class Metric {
|
||||
Metric({@required this.prefix, @required this.suffix});
|
||||
|
||||
void add(int value) {
|
||||
if (value > max) {
|
||||
max = value;
|
||||
}
|
||||
sum += value;
|
||||
sumOfSquares += value * value;
|
||||
count++;
|
||||
}
|
||||
|
||||
double _average() => sum / count;
|
||||
double _rms() => sqrt(sumOfSquares / count);
|
||||
|
||||
toString() => "$prefix): ${_average()}$suffix\n"
|
||||
"${prefix}Max): $max$suffix\n"
|
||||
"${prefix}RMS): ${_rms()}$suffix";
|
||||
|
||||
final String prefix;
|
||||
final String suffix;
|
||||
int max = 0;
|
||||
double sum = 0;
|
||||
double sumOfSquares = 0;
|
||||
int count = 0;
|
||||
}
|
||||
|
||||
class LatencyMetric extends Metric {
|
||||
LatencyMetric(String name) : super(prefix: "$name(Latency", suffix: " us.");
|
||||
}
|
||||
|
||||
class StartMessageLatency {
|
||||
StartMessageLatency(this.sendPort, this.spawned);
|
||||
|
||||
final SendPort sendPort;
|
||||
final DateTime spawned;
|
||||
}
|
||||
|
||||
class ResultMessageLatency {
|
||||
ResultMessageLatency(
|
||||
{this.timeToStartRunningCodeUs,
|
||||
this.timeToFinishRunningCodeUs,
|
||||
this.deltaHeap});
|
||||
|
||||
final int timeToStartRunningCodeUs;
|
||||
final int timeToFinishRunningCodeUs;
|
||||
final int deltaHeap;
|
||||
|
||||
int timeToIsolateSpawnUs;
|
||||
int timeToExitUs;
|
||||
}
|
||||
|
||||
class AggregatedResultMessageLatency {
|
||||
AggregatedResultMessageLatency(
|
||||
this.toAfterIsolateSpawnUs,
|
||||
this.toStartRunningCodeUs,
|
||||
this.toFinishRunningCodeUs,
|
||||
this.toExitUs,
|
||||
);
|
||||
|
||||
String toString() => """$toAfterIsolateSpawnUs
|
||||
$toStartRunningCodeUs
|
||||
$toFinishRunningCodeUs
|
||||
$toExitUs""";
|
||||
|
||||
final Metric toAfterIsolateSpawnUs;
|
||||
final Metric toStartRunningCodeUs;
|
||||
final Metric toFinishRunningCodeUs;
|
||||
final Metric toExitUs;
|
||||
}
|
||||
|
||||
Future<void> isolateCompiler(StartMessageLatency start) async {
|
||||
final DateTime timeRunningCodeUs = DateTime.now();
|
||||
await runZoned(
|
||||
() => dart2js_main.internalMain(<String>[
|
||||
"benchmarks/IsolateSpawn/dart/helloworld.dart",
|
||||
'--libraries-spec=sdk/lib/libraries.json'
|
||||
]),
|
||||
zoneSpecification: ZoneSpecification(
|
||||
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {}));
|
||||
final DateTime timeFinishRunningCodeUs = DateTime.now();
|
||||
start.sendPort.send(ResultMessageLatency(
|
||||
timeToStartRunningCodeUs:
|
||||
timeRunningCodeUs.difference(start.spawned).inMicroseconds,
|
||||
timeToFinishRunningCodeUs:
|
||||
timeFinishRunningCodeUs.difference(start.spawned).inMicroseconds));
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
await SpawnLatency("IsolateSpawn.Dart2JS").report();
|
||||
}
|
3
benchmarks/IsolateSpawn/dart2/helloworld.dart
Normal file
3
benchmarks/IsolateSpawn/dart2/helloworld.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
main() {
|
||||
print('Hello, world!');
|
||||
}
|
191
benchmarks/IsolateSpawnMemory/dart2/IsolateSpawnMemory.dart
Normal file
191
benchmarks/IsolateSpawnMemory/dart2/IsolateSpawnMemory.dart
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:compiler/src/dart2js.dart' as dart2js_main;
|
||||
import 'package:vm_service/vm_service.dart' as vm_service;
|
||||
import 'package:vm_service/vm_service_io.dart' as vm_service_io;
|
||||
|
||||
const String compilerIsolateName = 'isolate-compiler';
|
||||
|
||||
class Result {
|
||||
const Result(
|
||||
this.rssOnStart, this.rssOnEnd, this.heapOnStart, this.heapOnEnd);
|
||||
|
||||
final int rssOnStart;
|
||||
final int rssOnEnd;
|
||||
final int heapOnStart;
|
||||
final int heapOnEnd;
|
||||
}
|
||||
|
||||
class StartMessage {
|
||||
const StartMessage(this.wsUri, this.sendPort);
|
||||
|
||||
final String wsUri;
|
||||
final SendPort sendPort;
|
||||
}
|
||||
|
||||
class SpawnMemory {
|
||||
SpawnMemory(this.name, this.wsUri);
|
||||
|
||||
Future<void> report() async {
|
||||
int maxProcessRss = 0;
|
||||
final timer = Timer.periodic(const Duration(microseconds: 100), (_) {
|
||||
maxProcessRss = math.max(maxProcessRss, ProcessInfo.currentRss);
|
||||
});
|
||||
|
||||
const numberOfBenchmarks = 3;
|
||||
|
||||
final beforeRss = ProcessInfo.currentRss;
|
||||
final beforeHeap = await currentHeapUsage(wsUri);
|
||||
|
||||
final iterators = <StreamIterator>[];
|
||||
final continuations = <SendPort>[];
|
||||
|
||||
// Start all isolates & make them wait.
|
||||
for (int i = 0; i < numberOfBenchmarks; i++) {
|
||||
final receivePort = ReceivePort();
|
||||
final startMessage = StartMessage(wsUri, receivePort.sendPort);
|
||||
await Isolate.spawn(isolateCompiler, startMessage,
|
||||
debugName: compilerIsolateName);
|
||||
final iterator = StreamIterator(receivePort);
|
||||
|
||||
if (!await iterator.moveNext()) throw 'failed';
|
||||
continuations.add(iterator.current as SendPort);
|
||||
|
||||
iterators.add(iterator);
|
||||
}
|
||||
|
||||
final readyRss = ProcessInfo.currentRss;
|
||||
final readyHeap = await currentHeapUsage(wsUri);
|
||||
|
||||
// Let all isolates do the dart2js compilation.
|
||||
for (int i = 0; i < numberOfBenchmarks; i++) {
|
||||
final iterator = iterators[i];
|
||||
final continuation = continuations[i];
|
||||
continuation.send(null);
|
||||
if (!await iterator.moveNext()) throw 'failed';
|
||||
if (iterator.current != 'done') throw 'failed';
|
||||
}
|
||||
|
||||
final doneRss = ProcessInfo.currentRss;
|
||||
final doneHeap = await currentHeapUsage(wsUri);
|
||||
|
||||
// Shut down helper isolates
|
||||
for (int i = 0; i < numberOfBenchmarks; i++) {
|
||||
final iterator = iterators[i];
|
||||
final continuation = continuations[i];
|
||||
continuation.send(null);
|
||||
if (!await iterator.moveNext()) throw 'failed';
|
||||
if (iterator.current != 'shutdown') throw 'failed';
|
||||
await iterator.cancel();
|
||||
}
|
||||
timer.cancel();
|
||||
|
||||
final readyDiffRss =
|
||||
math.max(0, readyRss - beforeRss) ~/ numberOfBenchmarks;
|
||||
final readyDiffHeap =
|
||||
math.max(0, readyHeap - beforeHeap) ~/ numberOfBenchmarks;
|
||||
final doneDiffRss = math.max(0, doneRss - beforeRss) ~/ numberOfBenchmarks;
|
||||
final doneDiffHeap =
|
||||
math.max(0, doneHeap - beforeHeap) ~/ numberOfBenchmarks;
|
||||
|
||||
print("${name}RssOnStart(MemoryUse): $readyDiffRss");
|
||||
print("${name}RssOnEnd(MemoryUse): $doneDiffRss");
|
||||
print("${name}HeapOnStart(MemoryUse): $readyDiffHeap");
|
||||
print("${name}HeapOnEnd(MemoryUse): $doneDiffHeap");
|
||||
print("${name}PeakProcessRss(MemoryUse): $maxProcessRss");
|
||||
}
|
||||
|
||||
final String name;
|
||||
final String wsUri;
|
||||
}
|
||||
|
||||
Future<void> isolateCompiler(StartMessage startMessage) async {
|
||||
final port = ReceivePort();
|
||||
final iterator = StreamIterator(port);
|
||||
|
||||
// Let main isolate know we're ready.
|
||||
startMessage.sendPort.send(port.sendPort);
|
||||
await iterator.moveNext();
|
||||
|
||||
await runZoned(
|
||||
() => dart2js_main.internalMain(<String>[
|
||||
"benchmarks/IsolateSpawnMemory/dart/helloworld.dart",
|
||||
'--libraries-spec=sdk/lib/libraries.json'
|
||||
]),
|
||||
zoneSpecification: ZoneSpecification(
|
||||
print: (Zone self, ZoneDelegate parent, Zone zone, String line) {}));
|
||||
|
||||
// Let main isolate know we're done.
|
||||
startMessage.sendPort.send('done');
|
||||
await iterator.moveNext();
|
||||
|
||||
// Closes the port.
|
||||
startMessage.sendPort.send('shutdown');
|
||||
await iterator.cancel();
|
||||
}
|
||||
|
||||
Future<int> currentHeapUsage(String wsUri) async {
|
||||
final vm_service.VmService vmService =
|
||||
await vm_service_io.vmServiceConnectUri(wsUri);
|
||||
final groupIds = await getGroupIds(vmService);
|
||||
int sum = 0;
|
||||
for (final groupId in groupIds) {
|
||||
final vm_service.MemoryUsage usage =
|
||||
await vmService.getIsolateGroupMemoryUsage(groupId);
|
||||
sum += usage.heapUsage + usage.externalUsage;
|
||||
}
|
||||
vmService.dispose();
|
||||
return sum;
|
||||
}
|
||||
|
||||
Future<void> main() async {
|
||||
// Only if we successfully reach the end will we set 0 exit code.
|
||||
exitCode = 255;
|
||||
|
||||
final ServiceProtocolInfo info = await Service.controlWebServer(enable: true);
|
||||
final Uri observatoryUri = info.serverUri;
|
||||
final String wsUri =
|
||||
'ws://${observatoryUri.authority}${observatoryUri.path}ws';
|
||||
await SpawnMemory("IsolateSpawnMemory.Dart2JSDelta", wsUri).report();
|
||||
|
||||
// Only if we successfully reach the end will we set 0 exit code.
|
||||
exitCode = 0;
|
||||
}
|
||||
|
||||
// Returns the set of isolate groups for which we should count the heap usage.
|
||||
//
|
||||
// We have two cases
|
||||
//
|
||||
// a) --enable-isolate-groups: All isolates will be within the same isolate
|
||||
// group.
|
||||
//
|
||||
// b) --no-enable-isolate-groups: All isolates will be within their own,
|
||||
// separate isolate group.
|
||||
//
|
||||
// In both cases we want to sum up the heap sizes of all isolate groups.
|
||||
Future<List<String>> getGroupIds(vm_service.VmService vmService) async {
|
||||
final groupIds = <String>{};
|
||||
final vm = await vmService.getVM();
|
||||
for (vm_service.IsolateGroupRef groupRef in vm.isolateGroups) {
|
||||
final vm_service.IsolateGroup group =
|
||||
await vmService.getIsolateGroup(groupRef.id);
|
||||
for (vm_service.IsolateRef isolateRef in group.isolates) {
|
||||
final isolateOrSentinel = await vmService.getIsolate(isolateRef.id);
|
||||
if (isolateOrSentinel is vm_service.Isolate) {
|
||||
groupIds.add(groupRef.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (groupIds.isEmpty) {
|
||||
throw "Could not find main isolate";
|
||||
}
|
||||
return groupIds.toList();
|
||||
}
|
3
benchmarks/IsolateSpawnMemory/dart2/helloworld.dart
Normal file
3
benchmarks/IsolateSpawnMemory/dart2/helloworld.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
main() {
|
||||
print('Hello, world!');
|
||||
}
|
152
benchmarks/ListCopy/dart2/ListCopy.dart
Normal file
152
benchmarks/ListCopy/dart2/ListCopy.dart
Normal file
|
@ -0,0 +1,152 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
|
||||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
|
||||
// Benchmark for polymorphic list copying.
|
||||
//
|
||||
// Each benchmark creates a list from an Iterable. There are many slightly
|
||||
// different ways to do this.
|
||||
//
|
||||
// In each benchmark the call site is polymorphic in the input type to simulate
|
||||
// the behaviour of library methods in the context of a large application. The
|
||||
// input lists are skewed heavily to the default growable list. This attempts to
|
||||
// model 'real world' copying of 'ordinary' lists.
|
||||
//
|
||||
// The benchmarks are run for small lists (2 elements, names ending in
|
||||
// `.2`) and 'large' lists (100 elements or `.100`). The benchmarks are
|
||||
// normalized on the number of elements to make the input sizes comparable.
|
||||
//
|
||||
// Most inputs have type `Iterable<num>`, but contain only `int` values. This
|
||||
// allows is to compare the down-conversion versions of copying where each
|
||||
// element must be checked.
|
||||
|
||||
class Benchmark extends BenchmarkBase {
|
||||
final int length;
|
||||
final Function() copy;
|
||||
|
||||
final List<Iterable<num>> inputs = [];
|
||||
|
||||
Benchmark(String name, this.length, this.copy)
|
||||
: super('ListCopy.$name.$length');
|
||||
|
||||
void setup() {
|
||||
// Ensure setup() is idempotent.
|
||||
if (inputs.isNotEmpty) return;
|
||||
List<num> base = List.generate(length, (i) => i + 1);
|
||||
List<Iterable<num>> makeVariants() {
|
||||
return [
|
||||
// Weight ordinary lists more.
|
||||
...List.generate(19, (_) => List<num>.of(base)),
|
||||
|
||||
base.toList(growable: false),
|
||||
List<num>.unmodifiable(base),
|
||||
UnmodifiableListView(base),
|
||||
base.reversed,
|
||||
String.fromCharCodes(List<int>.from(base)).codeUnits,
|
||||
Uint8List.fromList(List<int>.from(base)),
|
||||
];
|
||||
}
|
||||
|
||||
const elements = 10000;
|
||||
int totalLength = 0;
|
||||
while (totalLength < elements) {
|
||||
var variants = makeVariants();
|
||||
inputs.addAll(variants);
|
||||
totalLength +=
|
||||
variants.fold<int>(0, (sum, iterable) => sum + iterable.length);
|
||||
}
|
||||
|
||||
// Sanity checks.
|
||||
for (var sample in inputs) {
|
||||
if (sample.length != length) throw 'Wrong length: $length $sample';
|
||||
}
|
||||
if (totalLength != elements) {
|
||||
throw 'totalLength $totalLength != expected $elements';
|
||||
}
|
||||
}
|
||||
|
||||
void run() {
|
||||
for (var sample in inputs) {
|
||||
input = sample;
|
||||
// Unroll loop 10 times to reduce loop overhead, which is about 15% for
|
||||
// the fastest short input benchmarks.
|
||||
copy();
|
||||
copy();
|
||||
copy();
|
||||
copy();
|
||||
copy();
|
||||
copy();
|
||||
copy();
|
||||
copy();
|
||||
copy();
|
||||
copy();
|
||||
}
|
||||
if (output.length != inputs.first.length) throw 'Bad result: $output';
|
||||
}
|
||||
}
|
||||
|
||||
// All the 'copy' methods use [input] and [output] rather than a parameter and
|
||||
// return value to avoid any possibility of type check in the call sequence.
|
||||
Iterable<num> input = const [];
|
||||
var output;
|
||||
|
||||
List<Benchmark> makeBenchmarks(int length) => [
|
||||
Benchmark('toList', length, () {
|
||||
output = input.toList();
|
||||
}),
|
||||
Benchmark('toList.fixed', length, () {
|
||||
output = input.toList(growable: false);
|
||||
}),
|
||||
Benchmark('List.of', length, () {
|
||||
output = List<num>.of(input);
|
||||
}),
|
||||
Benchmark('List.of.fixed', length, () {
|
||||
output = List<num>.of(input, growable: false);
|
||||
}),
|
||||
Benchmark('List.num.from', length, () {
|
||||
output = List<num>.from(input);
|
||||
}),
|
||||
Benchmark('List.int.from', length, () {
|
||||
output = List<int>.from(input);
|
||||
}),
|
||||
Benchmark('List.num.from.fixed', length, () {
|
||||
output = List<num>.from(input, growable: false);
|
||||
}),
|
||||
Benchmark('List.int.from.fixed', length, () {
|
||||
output = List<int>.from(input, growable: false);
|
||||
}),
|
||||
Benchmark('List.num.unmodifiable', length, () {
|
||||
output = List<num>.unmodifiable(input);
|
||||
}),
|
||||
Benchmark('List.int.unmodifiable', length, () {
|
||||
output = List<int>.unmodifiable(input);
|
||||
}),
|
||||
Benchmark('spread.num', length, () {
|
||||
output = <num>[...input];
|
||||
}),
|
||||
Benchmark('spread.int', length, () {
|
||||
output = <int>[...input];
|
||||
}),
|
||||
];
|
||||
|
||||
main() {
|
||||
var benchmarks = [...makeBenchmarks(2), ...makeBenchmarks(100)];
|
||||
|
||||
// Warmup all benchmarks to ensure JIT compilers see full polymorphism.
|
||||
for (var benchmark in benchmarks) {
|
||||
benchmark.setup();
|
||||
}
|
||||
|
||||
for (var benchmark in benchmarks) {
|
||||
benchmark.warmup();
|
||||
}
|
||||
|
||||
for (var benchmark in benchmarks) {
|
||||
// `report` calls `setup`, but `setup` is idempotent.
|
||||
benchmark.report();
|
||||
}
|
||||
}
|
176
benchmarks/RuntimeType/dart2/RuntimeType.dart
Normal file
176
benchmarks/RuntimeType/dart2/RuntimeType.dart
Normal file
|
@ -0,0 +1,176 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
//
|
||||
// Benchmark for runtimeType patterns as used in Flutter.
|
||||
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
// ignore_for_file: avoid_function_literals_in_foreach_calls
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
|
||||
abstract class Key {
|
||||
const factory Key(String value) = ValueKey<String>;
|
||||
const Key.empty();
|
||||
}
|
||||
|
||||
abstract class LocalKey extends Key {
|
||||
const LocalKey() : super.empty();
|
||||
}
|
||||
|
||||
class ValueKey<T> extends LocalKey {
|
||||
const ValueKey(this.value);
|
||||
final T value;
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (other.runtimeType != runtimeType) return false;
|
||||
return other is ValueKey<T> && other.value == value;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => value.hashCode;
|
||||
}
|
||||
|
||||
abstract class Widget {
|
||||
const Widget({this.key});
|
||||
final Key key;
|
||||
|
||||
@pragma('dart2js:noInline')
|
||||
static bool canUpdate(Widget oldWidget, Widget newWidget) {
|
||||
return oldWidget.runtimeType == newWidget.runtimeType &&
|
||||
oldWidget.key == newWidget.key;
|
||||
}
|
||||
}
|
||||
|
||||
class AWidget extends Widget {
|
||||
const AWidget({Key key}) : super(key: key);
|
||||
}
|
||||
|
||||
class BWidget extends Widget {
|
||||
const BWidget({Key key}) : super(key: key);
|
||||
}
|
||||
|
||||
class CWidget extends Widget {
|
||||
const CWidget({Key key}) : super(key: key);
|
||||
}
|
||||
|
||||
class DWidget extends Widget {
|
||||
const DWidget({Key key}) : super(key: key);
|
||||
}
|
||||
|
||||
class EWidget extends Widget {
|
||||
const EWidget({Key key}) : super(key: key);
|
||||
}
|
||||
|
||||
class FWidget extends Widget {
|
||||
const FWidget({Key key}) : super(key: key);
|
||||
}
|
||||
|
||||
class WWidget<W extends Widget> extends Widget {
|
||||
final W /*?*/ ref;
|
||||
const WWidget({this.ref, Key key}) : super(key: key);
|
||||
}
|
||||
|
||||
class WidgetCanUpdateBenchmark extends BenchmarkBase {
|
||||
WidgetCanUpdateBenchmark() : super('RuntimeType.Widget.canUpdate.byType');
|
||||
|
||||
// All widgets have different types.
|
||||
static List<Widget> _widgets() => [
|
||||
AWidget(),
|
||||
BWidget(),
|
||||
CWidget(),
|
||||
DWidget(),
|
||||
EWidget(),
|
||||
FWidget(),
|
||||
WWidget<AWidget>(),
|
||||
WWidget<BWidget>(ref: const BWidget()),
|
||||
WWidget<CWidget>(ref: CWidget()),
|
||||
const WWidget<DWidget>(ref: DWidget()),
|
||||
];
|
||||
// Bulk up list to reduce loop overheads.
|
||||
final List<Widget> widgets = _widgets() + _widgets() + _widgets();
|
||||
|
||||
@override
|
||||
void exercise() => run();
|
||||
|
||||
@override
|
||||
void run() {
|
||||
for (var w1 in widgets) {
|
||||
for (var w2 in widgets) {
|
||||
if (Widget.canUpdate(w1, w2) != Widget.canUpdate(w2, w1)) {
|
||||
throw 'Hmm $w1 $w2';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize by number of calls to [Widgets.canUpdate].
|
||||
@override
|
||||
double measure() => super.measure() / (widgets.length * widgets.length * 2);
|
||||
}
|
||||
|
||||
class ValueKeyEqualBenchmark extends BenchmarkBase {
|
||||
ValueKeyEqualBenchmark() : super('RuntimeType.Widget.canUpdate.byKey');
|
||||
|
||||
// All widgets the same class but distinguished on keys.
|
||||
static List<Widget> _widgets() => [
|
||||
AWidget(),
|
||||
AWidget(key: ValueKey(1)),
|
||||
AWidget(key: ValueKey(1)),
|
||||
AWidget(key: ValueKey(2)),
|
||||
AWidget(key: ValueKey(2)),
|
||||
AWidget(key: ValueKey(3)),
|
||||
AWidget(key: ValueKey('one')),
|
||||
AWidget(key: ValueKey('two')),
|
||||
AWidget(key: ValueKey('three')),
|
||||
AWidget(key: ValueKey(Duration(seconds: 5))),
|
||||
];
|
||||
// Bulk up list to reduce loop overheads.
|
||||
final List<Widget> widgets = _widgets() + _widgets() + _widgets();
|
||||
|
||||
@override
|
||||
void exercise() => run();
|
||||
|
||||
@override
|
||||
void run() {
|
||||
for (var w1 in widgets) {
|
||||
for (var w2 in widgets) {
|
||||
if (Widget.canUpdate(w1, w2) != Widget.canUpdate(w2, w1)) {
|
||||
throw 'Hmm $w1 $w2';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Normalize by number of calls to [Widgets.canUpdate].
|
||||
@override
|
||||
double measure() => super.measure() / (widgets.length * widgets.length * 2);
|
||||
}
|
||||
|
||||
void pollute() {
|
||||
// Various bits of code to make environment less unrealistic.
|
||||
void check(dynamic a, dynamic b) {
|
||||
if (a.runtimeType != b.runtimeType) throw 'mismatch $a $b';
|
||||
}
|
||||
|
||||
check(Uint8List(1), Uint8List(2)); // dart2js needs native interceptors.
|
||||
check(Int16List(1), Int16List(2));
|
||||
check([], []);
|
||||
check(<bool>{}, <bool>{});
|
||||
}
|
||||
|
||||
void main() {
|
||||
pollute();
|
||||
|
||||
final benchmarks = [
|
||||
WidgetCanUpdateBenchmark(),
|
||||
ValueKeyEqualBenchmark(),
|
||||
];
|
||||
|
||||
// Warm up all benchmarks before running any.
|
||||
benchmarks.forEach((bm) => bm.run());
|
||||
|
||||
benchmarks.forEach((bm) => bm.report());
|
||||
}
|
31
benchmarks/SoundSplayTreeSieve/dart2/README.md
Normal file
31
benchmarks/SoundSplayTreeSieve/dart2/README.md
Normal file
|
@ -0,0 +1,31 @@
|
|||
# SoundSplayTreeSieve
|
||||
The SoundSplayTreeSieve benchmark reports the runtime of the `sieve9` Golem benchmark
|
||||
for a `SplayTreeSet` from `dart:collection` and a `SoundSplayTreeSet` that
|
||||
declares variance modifiers for its type parameters.
|
||||
|
||||
## Running the benchmark
|
||||
These are instructions for running the benchmark, assuming you are in the `sdk`
|
||||
directory.
|
||||
|
||||
These benchmarks print a result similar to this (with varying runtimes):
|
||||
```
|
||||
CollectionSieves-SplayTreeSet-removeLoop(RunTime): 4307.52688172043 us.
|
||||
CollectionSieves-SoundSplayTreeSet-removeLoop(RunTime): 4344.902386117137 us.
|
||||
```
|
||||
|
||||
**Dart2JS**
|
||||
```
|
||||
$ sdk/bin/dart2js_developer benchmarks/SoundSplayTreeSieve/dart/SoundSplayTreeSieve.dart --enable-experiment=variance --experiment-new-rti --out=soundsplay_d2js.js
|
||||
$ third_party/d8/linux/d8 soundsplay_d2js.js
|
||||
```
|
||||
|
||||
**Dart2JS (Omit implicit checks)**
|
||||
```
|
||||
$ sdk/bin/dart2js_developer benchmarks/SoundSplayTreeSieve/dart/SoundSplayTreeSieve.dart --enable-experiment=variance --experiment-new-rti --omit-implicit-checks --out=soundsplay_d2js_omit.js --lax-runtime-type-to-string
|
||||
$ third_party/d8/linux/d8 soundsplay_d2js_omit.js
|
||||
```
|
||||
|
||||
**DDK**
|
||||
```
|
||||
$ pkg/dev_compiler/tool/ddb -d -r chrome --enable-experiment=variance -k benchmarks/SoundSplayTreeSieve/dart/SoundSplayTreeSieve.dart
|
||||
```
|
112
benchmarks/SoundSplayTreeSieve/dart2/SoundSplayTreeSieve.dart
Normal file
112
benchmarks/SoundSplayTreeSieve/dart2/SoundSplayTreeSieve.dart
Normal file
|
@ -0,0 +1,112 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:collection';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
import 'sound_splay_tree.dart';
|
||||
|
||||
List<int> sieve(List<int> initialCandidates) {
|
||||
final candidates = SplayTreeSet<int>.from(initialCandidates);
|
||||
final int last = candidates.last;
|
||||
final primes = <int>[];
|
||||
// ignore: literal_only_boolean_expressions
|
||||
while (true) {
|
||||
final int prime = candidates.first;
|
||||
if (prime * prime > last) break;
|
||||
primes.add(prime);
|
||||
for (int i = prime; i <= last; i += prime) {
|
||||
candidates.remove(i);
|
||||
}
|
||||
}
|
||||
return primes..addAll(candidates);
|
||||
}
|
||||
|
||||
List<int> sieveSound(List<int> initialCandidates) {
|
||||
final candidates = SoundSplayTreeSet<int>.from(initialCandidates);
|
||||
final int last = candidates.last;
|
||||
final primes = <int>[];
|
||||
// ignore: literal_only_boolean_expressions
|
||||
while (true) {
|
||||
final int prime = candidates.first;
|
||||
if (prime * prime > last) break;
|
||||
primes.add(prime);
|
||||
for (int i = prime; i <= last; i += prime) {
|
||||
candidates.remove(i);
|
||||
}
|
||||
}
|
||||
return primes..addAll(candidates);
|
||||
}
|
||||
|
||||
/// Returns a list of integers from [first] to [last], both inclusive.
|
||||
List<int> range(int first, int last) {
|
||||
return List<int>.generate(last - first + 1, (int i) => i + first);
|
||||
}
|
||||
|
||||
int id(int x) => x;
|
||||
int add1(int i) => 1 + i;
|
||||
bool isEven(int i) => i.isEven;
|
||||
void exercise(Iterable<int> hello) {
|
||||
if (hello.toList().length != 5) throw 'x1';
|
||||
if (List.from(hello).length != 5) throw 'x1';
|
||||
if (Set.from(hello).length != 4) throw 'x1';
|
||||
if (List<int>.from(hello).where(isEven).length != 3) throw 'x1';
|
||||
if (hello.where(isEven).length != 3) throw 'x1';
|
||||
if (hello.map(add1).where(isEven).length != 2) throw 'x1';
|
||||
if (hello.where(isEven).map(add1).length != 3) throw 'x1';
|
||||
}
|
||||
|
||||
void busyWork() {
|
||||
// A lot of busy-work calling map/where/toList/List.from to ensure the core
|
||||
// library is used with some degree of polymorphism.
|
||||
final L1 = 'hello'.codeUnits;
|
||||
final L2 = Uint16List(5)..setRange(0, 5, L1);
|
||||
final L3 = Uint32List(5)..setRange(0, 5, L1);
|
||||
exercise(L1);
|
||||
exercise(L2);
|
||||
exercise(L3);
|
||||
exercise(UnmodifiableListView<int>(L1));
|
||||
exercise(UnmodifiableListView<int>(L2));
|
||||
exercise(UnmodifiableListView<int>(L3));
|
||||
exercise(L1.asMap().values);
|
||||
exercise(L1.toList().asMap().values);
|
||||
final M1 =
|
||||
Map<String, int>.fromIterables(<String>['a', 'b', 'c', 'd', 'e'], L1);
|
||||
final M2 = const <String, int>{
|
||||
'a': 104,
|
||||
'b': 101,
|
||||
'c': 108,
|
||||
'd': 108,
|
||||
'e': 111
|
||||
};
|
||||
exercise(M1.values);
|
||||
exercise(M2.values);
|
||||
}
|
||||
|
||||
main() {
|
||||
final benchmarks = [
|
||||
Base(sieve, 'CollectionSieves-SplayTreeSet-removeLoop'),
|
||||
Base(sieveSound, 'CollectionSieves-SoundSplayTreeSet-removeLoop'),
|
||||
];
|
||||
for (int i = 0; i < 10; i++) {
|
||||
busyWork();
|
||||
for (var bm in benchmarks) {
|
||||
bm.run();
|
||||
}
|
||||
}
|
||||
for (var bm in benchmarks) {
|
||||
bm.report();
|
||||
}
|
||||
}
|
||||
|
||||
class Base extends BenchmarkBase {
|
||||
final algorithm;
|
||||
Base(this.algorithm, String name) : super(name);
|
||||
static final input = range(2, 5000);
|
||||
void run() {
|
||||
final primes = algorithm(input);
|
||||
if (primes.length != 669) throw 'Wrong result for $name: ${primes.length}';
|
||||
}
|
||||
}
|
23
benchmarks/SoundSplayTreeSieve/dart2/iterable.dart
Normal file
23
benchmarks/SoundSplayTreeSieve/dart2/iterable.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
/// Marker interface for [Iterable] subclasses that have an efficient
|
||||
/// [length] implementation.
|
||||
abstract class EfficientLengthIterable<T> extends Iterable<T> {
|
||||
const EfficientLengthIterable();
|
||||
|
||||
/// Returns the number of elements in the iterable.
|
||||
///
|
||||
/// This is an efficient operation that doesn't require iterating through
|
||||
/// the elements.
|
||||
int get length;
|
||||
}
|
||||
|
||||
/// Creates errors throw by [Iterable] when the element count is wrong.
|
||||
abstract class IterableElementError {
|
||||
/// Error thrown thrown by, e.g., [Iterable.first] when there is no result.
|
||||
static StateError noElement() => StateError("No element");
|
||||
|
||||
/// Error thrown by, e.g., [Iterable.single] if there are too many results.
|
||||
static StateError tooMany() => StateError("Too many elements");
|
||||
|
||||
/// Error thrown by, e.g., [List.setRange] if there are too few elements.
|
||||
static StateError tooFew() => StateError("Too few elements");
|
||||
}
|
882
benchmarks/SoundSplayTreeSieve/dart2/sound_splay_tree.dart
Normal file
882
benchmarks/SoundSplayTreeSieve/dart2/sound_splay_tree.dart
Normal file
|
@ -0,0 +1,882 @@
|
|||
// Copyright (c) 2019, 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.
|
||||
|
||||
import 'dart:collection';
|
||||
import 'iterable.dart';
|
||||
|
||||
typedef _Predicate<T> = bool Function(T value);
|
||||
|
||||
/// A node in a splay tree. It holds the sorting key and the left
|
||||
/// and right children in the tree.
|
||||
class _SoundSplayTreeNode<inout K> {
|
||||
final K key;
|
||||
_SoundSplayTreeNode<K> left;
|
||||
_SoundSplayTreeNode<K> right;
|
||||
|
||||
_SoundSplayTreeNode(this.key);
|
||||
}
|
||||
|
||||
/// A node in a splay tree based map.
|
||||
///
|
||||
/// A [_SoundSplayTreeNode] that also contains a value
|
||||
class _SoundSplayTreeMapNode<inout K, inout V> extends _SoundSplayTreeNode<K> {
|
||||
V value;
|
||||
_SoundSplayTreeMapNode(K key, this.value) : super(key);
|
||||
}
|
||||
|
||||
/// A splay tree is a self-balancing binary search tree.
|
||||
///
|
||||
/// It has the additional property that recently accessed elements
|
||||
/// are quick to access again.
|
||||
/// It performs basic operations such as insertion, look-up and
|
||||
/// removal, in O(log(n)) amortized time.
|
||||
/// TODO(kallentu): Add a variance modifier to the Node type parameter.
|
||||
abstract class _SoundSplayTree<inout K, Node extends _SoundSplayTreeNode<K>> {
|
||||
// The root node of the splay tree. It will contain either the last
|
||||
// element inserted or the last element looked up.
|
||||
Node get _root;
|
||||
set _root(Node newValue);
|
||||
|
||||
// The dummy node used when performing a splay on the tree. Reusing it
|
||||
// avoids allocating a node each time a splay is performed.
|
||||
Node get _dummy;
|
||||
|
||||
// Number of elements in the splay tree.
|
||||
int _count = 0;
|
||||
|
||||
/// Counter incremented whenever the keys in the map changes.
|
||||
///
|
||||
/// Used to detect concurrent modifications.
|
||||
int _modificationCount = 0;
|
||||
|
||||
/// Counter incremented whenever the tree structure changes.
|
||||
///
|
||||
/// Used to detect that an in-place traversal cannot use
|
||||
/// cached information that relies on the tree structure.
|
||||
int _splayCount = 0;
|
||||
|
||||
/// The comparator that is used for this splay tree.
|
||||
Comparator<K> get _comparator;
|
||||
|
||||
/// The predicate to determine that a given object is a valid key.
|
||||
_Predicate get _validKey;
|
||||
|
||||
/// Comparison used to compare keys.
|
||||
int _compare(K key1, K key2);
|
||||
|
||||
/// Perform the splay operation for the given key. Moves the node with
|
||||
/// the given key to the top of the tree. If no node has the given
|
||||
/// key, the last node on the search path is moved to the top of the
|
||||
/// tree. This is the simplified top-down splaying algorithm from:
|
||||
/// "Self-adjusting Binary Search Trees" by Sleator and Tarjan.
|
||||
///
|
||||
/// Returns the result of comparing the new root of the tree to [key].
|
||||
/// Returns -1 if the table is empty.
|
||||
int _splay(K key) {
|
||||
if (_root == null) return -1;
|
||||
|
||||
// The right child of the dummy node will hold
|
||||
// the L tree of the algorithm. The left child of the dummy node
|
||||
// will hold the R tree of the algorithm. Using a dummy node, left
|
||||
// and right will always be nodes and we avoid special cases.
|
||||
Node left = _dummy;
|
||||
Node right = _dummy;
|
||||
Node current = _root;
|
||||
int comp;
|
||||
while (true) {
|
||||
comp = _compare(current.key, key);
|
||||
if (comp > 0) {
|
||||
if (current.left == null) break;
|
||||
comp = _compare(current.left.key, key);
|
||||
if (comp > 0) {
|
||||
// Rotate right.
|
||||
_SoundSplayTreeNode<K> tmp = current.left;
|
||||
current.left = tmp.right;
|
||||
tmp.right = current;
|
||||
current = tmp;
|
||||
if (current.left == null) break;
|
||||
}
|
||||
// Link right.
|
||||
right.left = current;
|
||||
right = current;
|
||||
current = current.left;
|
||||
} else if (comp < 0) {
|
||||
if (current.right == null) break;
|
||||
comp = _compare(current.right.key, key);
|
||||
if (comp < 0) {
|
||||
// Rotate left.
|
||||
Node tmp = current.right;
|
||||
current.right = tmp.left;
|
||||
tmp.left = current;
|
||||
current = tmp;
|
||||
if (current.right == null) break;
|
||||
}
|
||||
// Link left.
|
||||
left.right = current;
|
||||
left = current;
|
||||
current = current.right;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Assemble.
|
||||
left.right = current.left;
|
||||
right.left = current.right;
|
||||
current.left = _dummy.right;
|
||||
current.right = _dummy.left;
|
||||
_root = current;
|
||||
|
||||
_dummy.right = null;
|
||||
_dummy.left = null;
|
||||
_splayCount++;
|
||||
return comp;
|
||||
}
|
||||
|
||||
// Emulates splaying with a key that is smaller than any in the subtree
|
||||
// anchored at [node].
|
||||
// and that node is returned. It should replace the reference to [node]
|
||||
// in any parent tree or root pointer.
|
||||
Node _splayMin(Node node) {
|
||||
Node current = node;
|
||||
while (current.left != null) {
|
||||
Node left = current.left;
|
||||
current.left = left.right;
|
||||
left.right = current;
|
||||
current = left;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
// Emulates splaying with a key that is greater than any in the subtree
|
||||
// anchored at [node].
|
||||
// After this, the largest element in the tree is the root of the subtree,
|
||||
// and that node is returned. It should replace the reference to [node]
|
||||
// in any parent tree or root pointer.
|
||||
Node _splayMax(Node node) {
|
||||
Node current = node;
|
||||
while (current.right != null) {
|
||||
Node right = current.right;
|
||||
current.right = right.left;
|
||||
right.left = current;
|
||||
current = right;
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
Node _remove(K key) {
|
||||
if (_root == null) return null;
|
||||
int comp = _splay(key);
|
||||
if (comp != 0) return null;
|
||||
Node result = _root;
|
||||
_count--;
|
||||
// assert(_count >= 0);
|
||||
if (_root.left == null) {
|
||||
_root = _root.right;
|
||||
} else {
|
||||
Node right = _root.right;
|
||||
// Splay to make sure that the new root has an empty right child.
|
||||
_root = _splayMax(_root.left);
|
||||
// Insert the original right child as the right child of the new
|
||||
// root.
|
||||
_root.right = right;
|
||||
}
|
||||
_modificationCount++;
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Adds a new root node with the given [key] or [value].
|
||||
///
|
||||
/// The [comp] value is the result of comparing the existing root's key
|
||||
/// with key.
|
||||
void _addNewRoot(Node node, int comp) {
|
||||
_count++;
|
||||
_modificationCount++;
|
||||
if (_root == null) {
|
||||
_root = node;
|
||||
return;
|
||||
}
|
||||
// assert(_count >= 0);
|
||||
if (comp < 0) {
|
||||
node.left = _root;
|
||||
node.right = _root.right;
|
||||
_root.right = null;
|
||||
} else {
|
||||
node.right = _root;
|
||||
node.left = _root.left;
|
||||
_root.left = null;
|
||||
}
|
||||
_root = node;
|
||||
}
|
||||
|
||||
Node get _first {
|
||||
if (_root == null) return null;
|
||||
_root = _splayMin(_root);
|
||||
return _root;
|
||||
}
|
||||
|
||||
Node get _last {
|
||||
if (_root == null) return null;
|
||||
_root = _splayMax(_root);
|
||||
return _root;
|
||||
}
|
||||
|
||||
void _clear() {
|
||||
_root = null;
|
||||
_count = 0;
|
||||
_modificationCount++;
|
||||
}
|
||||
}
|
||||
|
||||
class _TypeTest<T> {
|
||||
bool test(v) => v is T;
|
||||
}
|
||||
|
||||
int _dynamicCompare(dynamic a, dynamic b) => Comparable.compare(a, b);
|
||||
|
||||
Comparator<K> _defaultCompare<K>() {
|
||||
// If K <: Comparable, then we can just use Comparable.compare
|
||||
// with no casts.
|
||||
Object compare = Comparable.compare;
|
||||
if (compare is Comparator<K>) {
|
||||
return compare;
|
||||
}
|
||||
// Otherwise wrap and cast the arguments on each call.
|
||||
return _dynamicCompare;
|
||||
}
|
||||
|
||||
/// A [Map] of objects that can be ordered relative to each other.
|
||||
///
|
||||
/// The map is based on a self-balancing binary tree. It allows most operations
|
||||
/// in amortized logarithmic time.
|
||||
///
|
||||
/// Keys of the map are compared using the `compare` function passed in
|
||||
/// the constructor, both for ordering and for equality.
|
||||
/// If the map contains only the key `a`, then `map.containsKey(b)`
|
||||
/// will return `true` if and only if `compare(a, b) == 0`,
|
||||
/// and the value of `a == b` is not even checked.
|
||||
/// If the compare function is omitted, the objects are assumed to be
|
||||
/// [Comparable], and are compared using their [Comparable.compareTo] method.
|
||||
/// Non-comparable objects (including `null`) will not work as keys
|
||||
/// in that case.
|
||||
///
|
||||
/// To allow calling [operator []], [remove] or [containsKey] with objects
|
||||
/// that are not supported by the `compare` function, an extra `isValidKey`
|
||||
/// predicate function can be supplied. This function is tested before
|
||||
/// using the `compare` function on an argument value that may not be a [K]
|
||||
/// value. If omitted, the `isValidKey` function defaults to testing if the
|
||||
/// value is a [K].
|
||||
class SoundSplayTreeMap<inout K, inout V> extends _SoundSplayTree<K, _SoundSplayTreeMapNode<K, V>>
|
||||
with MapMixin<K, V> {
|
||||
_SoundSplayTreeMapNode<K, V> _root;
|
||||
final _SoundSplayTreeMapNode<K, V> _dummy = _SoundSplayTreeMapNode<K, V>(null, null);
|
||||
|
||||
Comparator<K> _comparator;
|
||||
_Predicate _validKey;
|
||||
|
||||
SoundSplayTreeMap([int compare(K key1, K key2), bool isValidKey(potentialKey)])
|
||||
: _comparator = compare ?? _defaultCompare<K>(),
|
||||
_validKey = isValidKey ?? ((v) => v is K);
|
||||
|
||||
/// Creates a [SoundSplayTreeMap] that contains all key/value pairs of [other].
|
||||
///
|
||||
/// The keys must all be instances of [K] and the values of [V].
|
||||
/// The [other] map itself can have any type.
|
||||
factory SoundSplayTreeMap.from(Map other,
|
||||
[int compare(K key1, K key2), bool isValidKey(potentialKey)]) {
|
||||
SoundSplayTreeMap<K, V> result = SoundSplayTreeMap<K, V>(compare, isValidKey);
|
||||
other.forEach((k, v) {
|
||||
result[k] = v;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Creates a [SoundSplayTreeMap] that contains all key/value pairs of [other].
|
||||
factory SoundSplayTreeMap.of(Map<K, V> other,
|
||||
[int compare(K key1, K key2), bool isValidKey(potentialKey)]) =>
|
||||
SoundSplayTreeMap<K, V>(compare, isValidKey)..addAll(other);
|
||||
|
||||
/// Creates a [SoundSplayTreeMap] where the keys and values are computed from the
|
||||
/// [iterable].
|
||||
///
|
||||
/// For each element of the [iterable] this constructor computes a key/value
|
||||
/// pair, by applying [key] and [value] respectively.
|
||||
///
|
||||
/// The keys of the key/value pairs do not need to be unique. The last
|
||||
/// occurrence of a key will simply overwrite any previous value.
|
||||
///
|
||||
/// If no functions are specified for [key] and [value] the default is to
|
||||
/// use the iterable value itself.
|
||||
factory SoundSplayTreeMap.fromIterable(Iterable iterable,
|
||||
{K key(element),
|
||||
V value(element),
|
||||
int compare(K key1, K key2),
|
||||
bool isValidKey(potentialKey)}) {
|
||||
SoundSplayTreeMap<K, V> map = SoundSplayTreeMap<K, V>(compare, isValidKey);
|
||||
fillMapWithMappedIterable(map, iterable, key, value);
|
||||
return map;
|
||||
}
|
||||
|
||||
static _id(x) => x;
|
||||
|
||||
static void fillMapWithMappedIterable(
|
||||
Map map, Iterable iterable, key(element), value(element)) {
|
||||
key ??= _id;
|
||||
value ??= _id;
|
||||
|
||||
for (var element in iterable) {
|
||||
map[key(element)] = value(element);
|
||||
}
|
||||
}
|
||||
|
||||
static void fillMapWithIterables(Map map, Iterable keys, Iterable values) {
|
||||
Iterator keyIterator = keys.iterator;
|
||||
Iterator valueIterator = values.iterator;
|
||||
|
||||
bool hasNextKey = keyIterator.moveNext();
|
||||
bool hasNextValue = valueIterator.moveNext();
|
||||
|
||||
while (hasNextKey && hasNextValue) {
|
||||
map[keyIterator.current] = valueIterator.current;
|
||||
hasNextKey = keyIterator.moveNext();
|
||||
hasNextValue = valueIterator.moveNext();
|
||||
}
|
||||
|
||||
if (hasNextKey || hasNextValue) {
|
||||
throw ArgumentError("Iterables do not have same length.");
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a [SoundSplayTreeMap] associating the given [keys] to [values].
|
||||
///
|
||||
/// This constructor iterates over [keys] and [values] and maps each element
|
||||
/// of [keys] to the corresponding element of [values].
|
||||
///
|
||||
/// If [keys] contains the same object multiple times, the last occurrence
|
||||
/// overwrites the previous value.
|
||||
///
|
||||
/// It is an error if the two [Iterable]s don't have the same length.
|
||||
factory SoundSplayTreeMap.fromIterables(Iterable<K> keys, Iterable<V> values,
|
||||
[int compare(K key1, K key2), bool isValidKey(potentialKey)]) {
|
||||
SoundSplayTreeMap<K, V> map = SoundSplayTreeMap<K, V>(compare, isValidKey);
|
||||
fillMapWithIterables(map, keys, values);
|
||||
return map;
|
||||
}
|
||||
|
||||
int _compare(K key1, K key2) => _comparator(key1, key2);
|
||||
|
||||
SoundSplayTreeMap._internal();
|
||||
|
||||
V operator [](Object key) {
|
||||
if (!_validKey(key)) return null;
|
||||
if (_root != null) {
|
||||
int comp = _splay(key);
|
||||
if (comp == 0) {
|
||||
return _root.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
V remove(Object key) {
|
||||
if (!_validKey(key)) return null;
|
||||
_SoundSplayTreeMapNode<K, V> mapRoot = _remove(key);
|
||||
if (mapRoot != null) return mapRoot.value;
|
||||
return null;
|
||||
}
|
||||
|
||||
void operator []=(K key, V value) {
|
||||
if (key == null) throw ArgumentError(key);
|
||||
// Splay on the key to move the last node on the search path for
|
||||
// the key to the root of the tree.
|
||||
int comp = _splay(key);
|
||||
if (comp == 0) {
|
||||
_root.value = value;
|
||||
return;
|
||||
}
|
||||
_addNewRoot(_SoundSplayTreeMapNode(key, value), comp);
|
||||
}
|
||||
|
||||
V putIfAbsent(K key, V ifAbsent()) {
|
||||
if (key == null) throw ArgumentError(key);
|
||||
int comp = _splay(key);
|
||||
if (comp == 0) {
|
||||
return _root.value;
|
||||
}
|
||||
int modificationCount = _modificationCount;
|
||||
int splayCount = _splayCount;
|
||||
V value = ifAbsent();
|
||||
if (modificationCount != _modificationCount) {
|
||||
throw ConcurrentModificationError(this);
|
||||
}
|
||||
if (splayCount != _splayCount) {
|
||||
comp = _splay(key);
|
||||
// Key is still not there, otherwise _modificationCount would be changed.
|
||||
assert(comp != 0);
|
||||
}
|
||||
_addNewRoot(_SoundSplayTreeMapNode(key, value), comp);
|
||||
return value;
|
||||
}
|
||||
|
||||
void addAll(Map<K, V> other) {
|
||||
other.forEach((K key, V value) {
|
||||
this[key] = value;
|
||||
});
|
||||
}
|
||||
|
||||
bool get isEmpty {
|
||||
return (_root == null);
|
||||
}
|
||||
|
||||
bool get isNotEmpty => !isEmpty;
|
||||
|
||||
void forEach(void f(K key, V value)) {
|
||||
Iterator<_SoundSplayTreeNode<K>> nodes = _SoundSplayTreeNodeIterator<K>(this);
|
||||
while (nodes.moveNext()) {
|
||||
_SoundSplayTreeMapNode<K, V> node = nodes.current;
|
||||
f(node.key, node.value);
|
||||
}
|
||||
}
|
||||
|
||||
int get length {
|
||||
return _count;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_clear();
|
||||
}
|
||||
|
||||
bool containsKey(Object key) {
|
||||
return _validKey(key) && _splay(key) == 0;
|
||||
}
|
||||
|
||||
bool containsValue(Object value) {
|
||||
int initialSplayCount = _splayCount;
|
||||
bool visit(_SoundSplayTreeMapNode<K, V> node) {
|
||||
while (node != null) {
|
||||
if (node.value == value) return true;
|
||||
if (initialSplayCount != _splayCount) {
|
||||
throw ConcurrentModificationError(this);
|
||||
}
|
||||
if (node.right != null && visit(node.right)) return true;
|
||||
node = node.left;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return visit(_root);
|
||||
}
|
||||
|
||||
Iterable<K> get keys => _SoundSplayTreeKeyIterable<K>(this);
|
||||
|
||||
Iterable<V> get values => _SoundSplayTreeValueIterable<K, V>(this);
|
||||
|
||||
/// Get the first key in the map. Returns [:null:] if the map is empty.
|
||||
K firstKey() {
|
||||
if (_root == null) return null;
|
||||
return _first.key;
|
||||
}
|
||||
|
||||
/// Get the last key in the map. Returns [:null:] if the map is empty.
|
||||
K lastKey() {
|
||||
if (_root == null) return null;
|
||||
return _last.key;
|
||||
}
|
||||
|
||||
/// Get the last key in the map that is strictly smaller than [key]. Returns
|
||||
/// [:null:] if no key was not found.
|
||||
K lastKeyBefore(K key) {
|
||||
if (key == null) throw ArgumentError(key);
|
||||
if (_root == null) return null;
|
||||
int comp = _splay(key);
|
||||
if (comp < 0) return _root.key;
|
||||
_SoundSplayTreeNode<K> node = _root.left;
|
||||
if (node == null) return null;
|
||||
while (node.right != null) {
|
||||
node = node.right;
|
||||
}
|
||||
return node.key;
|
||||
}
|
||||
|
||||
/// Get the first key in the map that is strictly larger than [key]. Returns
|
||||
/// [:null:] if no key was not found.
|
||||
K firstKeyAfter(K key) {
|
||||
if (key == null) throw ArgumentError(key);
|
||||
if (_root == null) return null;
|
||||
int comp = _splay(key);
|
||||
if (comp > 0) return _root.key;
|
||||
_SoundSplayTreeNode<K> node = _root.right;
|
||||
if (node == null) return null;
|
||||
while (node.left != null) {
|
||||
node = node.left;
|
||||
}
|
||||
return node.key;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract class _SoundSplayTreeIterator<inout K, inout T> implements Iterator<T> {
|
||||
final _SoundSplayTree<K, _SoundSplayTreeNode<K>> _tree;
|
||||
|
||||
/// Worklist of nodes to visit.
|
||||
///
|
||||
/// These nodes have been passed over on the way down in a
|
||||
/// depth-first left-to-right traversal. Visiting each node,
|
||||
/// and their right subtrees will visit the remainder of
|
||||
/// the nodes of a full traversal.
|
||||
///
|
||||
/// Only valid as long as the original tree isn't reordered.
|
||||
final List<_SoundSplayTreeNode<K>> _workList = <_SoundSplayTreeNode<K>>[];
|
||||
|
||||
/// Original modification counter of [_tree].
|
||||
///
|
||||
/// Incremented on [_tree] when a key is added or removed.
|
||||
/// If it changes, iteration is aborted.
|
||||
///
|
||||
/// Not final because some iterators may modify the tree knowingly,
|
||||
/// and they update the modification count in that case.
|
||||
int _modificationCount;
|
||||
|
||||
/// Count of splay operations on [_tree] when [_workList] was built.
|
||||
///
|
||||
/// If the splay count on [_tree] increases, [_workList] becomes invalid.
|
||||
int _splayCount;
|
||||
|
||||
/// Current node.
|
||||
_SoundSplayTreeNode<K> _currentNode;
|
||||
|
||||
_SoundSplayTreeIterator(_SoundSplayTree<K, _SoundSplayTreeNode<K>> tree)
|
||||
: _tree = tree,
|
||||
_modificationCount = tree._modificationCount,
|
||||
_splayCount = tree._splayCount {
|
||||
_findLeftMostDescendent(tree._root);
|
||||
}
|
||||
|
||||
_SoundSplayTreeIterator.startAt(_SoundSplayTree<K, _SoundSplayTreeNode<K>> tree, K startKey)
|
||||
: _tree = tree,
|
||||
_modificationCount = tree._modificationCount {
|
||||
if (tree._root == null) return;
|
||||
int compare = tree._splay(startKey);
|
||||
_splayCount = tree._splayCount;
|
||||
if (compare < 0) {
|
||||
// Don't include the root, start at the next element after the root.
|
||||
_findLeftMostDescendent(tree._root.right);
|
||||
} else {
|
||||
_workList.add(tree._root);
|
||||
}
|
||||
}
|
||||
|
||||
T get current {
|
||||
if (_currentNode == null) return null;
|
||||
return _getValue(_currentNode);
|
||||
}
|
||||
|
||||
void _findLeftMostDescendent(_SoundSplayTreeNode<K> node) {
|
||||
while (node != null) {
|
||||
_workList.add(node);
|
||||
node = node.left;
|
||||
}
|
||||
}
|
||||
|
||||
/// Called when the tree structure of the tree has changed.
|
||||
///
|
||||
/// This can be caused by a splay operation.
|
||||
/// If the key-set changes, iteration is aborted before getting
|
||||
/// here, so we know that the keys are the same as before, it's
|
||||
/// only the tree that has been reordered.
|
||||
void _rebuildWorkList(_SoundSplayTreeNode<K> currentNode) {
|
||||
assert(_workList.isNotEmpty);
|
||||
_workList.clear();
|
||||
if (currentNode == null) {
|
||||
_findLeftMostDescendent(_tree._root);
|
||||
} else {
|
||||
_tree._splay(currentNode.key);
|
||||
_findLeftMostDescendent(_tree._root.right);
|
||||
assert(_workList.isNotEmpty);
|
||||
}
|
||||
}
|
||||
|
||||
bool moveNext() {
|
||||
if (_modificationCount != _tree._modificationCount) {
|
||||
throw ConcurrentModificationError(_tree);
|
||||
}
|
||||
// Picks the next element in the worklist as current.
|
||||
// Updates the worklist with the left-most path of the current node's
|
||||
// right-hand child.
|
||||
// If the worklist is no longer valid (after a splay), it is rebuild
|
||||
// from scratch.
|
||||
if (_workList.isEmpty) {
|
||||
_currentNode = null;
|
||||
return false;
|
||||
}
|
||||
if (_tree._splayCount != _splayCount && _currentNode != null) {
|
||||
_rebuildWorkList(_currentNode);
|
||||
}
|
||||
_currentNode = _workList.removeLast();
|
||||
_findLeftMostDescendent(_currentNode.right);
|
||||
return true;
|
||||
}
|
||||
|
||||
T _getValue(_SoundSplayTreeNode<K> node);
|
||||
}
|
||||
|
||||
class _SoundSplayTreeKeyIterable<inout K> extends EfficientLengthIterable<K> {
|
||||
_SoundSplayTree<K, _SoundSplayTreeNode<K>> _tree;
|
||||
_SoundSplayTreeKeyIterable(this._tree);
|
||||
int get length => _tree._count;
|
||||
bool get isEmpty => _tree._count == 0;
|
||||
Iterator<K> get iterator => _SoundSplayTreeKeyIterator<K>(_tree);
|
||||
|
||||
Set<K> toSet() {
|
||||
SoundSplayTreeSet<K> set = SoundSplayTreeSet<K>(_tree._comparator, _tree._validKey);
|
||||
set._count = _tree._count;
|
||||
set._root = set._copyNode(_tree._root);
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
class _SoundSplayTreeValueIterable<inout K, inout V> extends EfficientLengthIterable<V> {
|
||||
SoundSplayTreeMap<K, V> _map;
|
||||
_SoundSplayTreeValueIterable(this._map);
|
||||
int get length => _map._count;
|
||||
bool get isEmpty => _map._count == 0;
|
||||
Iterator<V> get iterator => _SoundSplayTreeValueIterator<K, V>(_map);
|
||||
}
|
||||
|
||||
class _SoundSplayTreeKeyIterator<inout K> extends _SoundSplayTreeIterator<K, K> {
|
||||
_SoundSplayTreeKeyIterator(_SoundSplayTree<K, _SoundSplayTreeNode<K>> map) : super(map);
|
||||
K _getValue(_SoundSplayTreeNode<K> node) => node.key;
|
||||
}
|
||||
|
||||
class _SoundSplayTreeValueIterator<inout K, inout V> extends _SoundSplayTreeIterator<K, V> {
|
||||
_SoundSplayTreeValueIterator(SoundSplayTreeMap<K, V> map) : super(map);
|
||||
V _getValue(_SoundSplayTreeNode<K> node) {
|
||||
_SoundSplayTreeMapNode<K, V> mapNode = node;
|
||||
return mapNode.value;
|
||||
}
|
||||
}
|
||||
|
||||
class _SoundSplayTreeNodeIterator<inout K>
|
||||
extends _SoundSplayTreeIterator<K, _SoundSplayTreeNode<K>> {
|
||||
_SoundSplayTreeNodeIterator(_SoundSplayTree<K, _SoundSplayTreeNode<K>> tree) : super(tree);
|
||||
_SoundSplayTreeNodeIterator.startAt(
|
||||
_SoundSplayTree<K, _SoundSplayTreeNode<K>> tree, K startKey)
|
||||
: super.startAt(tree, startKey);
|
||||
_SoundSplayTreeNode<K> _getValue(_SoundSplayTreeNode<K> node) => node;
|
||||
}
|
||||
|
||||
/// A [Set] of objects that can be ordered relative to each other.
|
||||
///
|
||||
/// The set is based on a self-balancing binary tree. It allows most operations
|
||||
/// in amortized logarithmic time.
|
||||
///
|
||||
/// Elements of the set are compared using the `compare` function passed in
|
||||
/// the constructor, both for ordering and for equality.
|
||||
/// If the set contains only an object `a`, then `set.contains(b)`
|
||||
/// will return `true` if and only if `compare(a, b) == 0`,
|
||||
/// and the value of `a == b` is not even checked.
|
||||
/// If the compare function is omitted, the objects are assumed to be
|
||||
/// [Comparable], and are compared using their [Comparable.compareTo] method.
|
||||
/// Non-comparable objects (including `null`) will not work as an element
|
||||
/// in that case.
|
||||
class SoundSplayTreeSet<inout E> extends _SoundSplayTree<E, _SoundSplayTreeNode<E>>
|
||||
with IterableMixin<E>, SetMixin<E> {
|
||||
_SoundSplayTreeNode<E> _root;
|
||||
final _SoundSplayTreeNode<E> _dummy = _SoundSplayTreeNode<E>(null);
|
||||
|
||||
Comparator<E> _comparator;
|
||||
_Predicate _validKey;
|
||||
|
||||
/// Create a new [SoundSplayTreeSet] with the given compare function.
|
||||
///
|
||||
/// If the [compare] function is omitted, it defaults to [Comparable.compare],
|
||||
/// and the elements must be comparable.
|
||||
///
|
||||
/// A provided `compare` function may not work on all objects. It may not even
|
||||
/// work on all `E` instances.
|
||||
///
|
||||
/// For operations that add elements to the set, the user is supposed to not
|
||||
/// pass in objects that doesn't work with the compare function.
|
||||
///
|
||||
/// The methods [contains], [remove], [lookup], [removeAll] or [retainAll]
|
||||
/// are typed to accept any object(s), and the [isValidKey] test can used to
|
||||
/// filter those objects before handing them to the `compare` function.
|
||||
///
|
||||
/// If [isValidKey] is provided, only values satisfying `isValidKey(other)`
|
||||
/// are compared using the `compare` method in the methods mentioned above.
|
||||
/// If the `isValidKey` function returns false for an object, it is assumed to
|
||||
/// not be in the set.
|
||||
///
|
||||
/// If omitted, the `isValidKey` function defaults to checking against the
|
||||
/// type parameter: `other is E`.
|
||||
SoundSplayTreeSet([int compare(E key1, E key2), bool isValidKey(potentialKey)])
|
||||
: _comparator = compare ?? _defaultCompare<E>(),
|
||||
_validKey = isValidKey ?? ((v) => v is E);
|
||||
|
||||
/// Creates a [SoundSplayTreeSet] that contains all [elements].
|
||||
///
|
||||
/// The set works as if created by `new SplayTreeSet<E>(compare, isValidKey)`.
|
||||
///
|
||||
/// All the [elements] should be instances of [E] and valid arguments to
|
||||
/// [compare].
|
||||
/// The `elements` iterable itself may have any element type, so this
|
||||
/// constructor can be used to down-cast a `Set`, for example as:
|
||||
/// ```dart
|
||||
/// Set<SuperType> superSet = ...;
|
||||
/// Set<SubType> subSet =
|
||||
/// new SplayTreeSet<SubType>.from(superSet.whereType<SubType>());
|
||||
/// ```
|
||||
factory SoundSplayTreeSet.from(Iterable elements,
|
||||
[int compare(E key1, E key2), bool isValidKey(potentialKey)]) {
|
||||
SoundSplayTreeSet<E> result = SoundSplayTreeSet<E>(compare, isValidKey);
|
||||
for (final element in elements) {
|
||||
E e = element;
|
||||
result.add(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Creates a [SoundSplayTreeSet] from [elements].
|
||||
///
|
||||
/// The set works as if created by `new SplayTreeSet<E>(compare, isValidKey)`.
|
||||
///
|
||||
/// All the [elements] should be valid as arguments to the [compare] function.
|
||||
factory SoundSplayTreeSet.of(Iterable<E> elements,
|
||||
[int compare(E key1, E key2), bool isValidKey(potentialKey)]) =>
|
||||
SoundSplayTreeSet(compare, isValidKey)..addAll(elements);
|
||||
|
||||
Set<T> _newSet<T>() =>
|
||||
SoundSplayTreeSet<T>((T a, T b) => _comparator(a as E, b as E), _validKey);
|
||||
|
||||
Set<R> cast<R>() => Set.castFrom<E, R>(this, newSet: _newSet);
|
||||
int _compare(E e1, E e2) => _comparator(e1, e2);
|
||||
|
||||
// From Iterable.
|
||||
|
||||
Iterator<E> get iterator => _SoundSplayTreeKeyIterator<E>(this);
|
||||
|
||||
int get length => _count;
|
||||
bool get isEmpty => _root == null;
|
||||
bool get isNotEmpty => _root != null;
|
||||
|
||||
E get first {
|
||||
if (_count == 0) throw IterableElementError.noElement();
|
||||
return _first.key;
|
||||
}
|
||||
|
||||
E get last {
|
||||
if (_count == 0) throw IterableElementError.noElement();
|
||||
return _last.key;
|
||||
}
|
||||
|
||||
E get single {
|
||||
if (_count == 0) throw IterableElementError.noElement();
|
||||
if (_count > 1) throw IterableElementError.tooMany();
|
||||
return _root.key;
|
||||
}
|
||||
|
||||
// From Set.
|
||||
bool contains(Object element) {
|
||||
return _validKey(element) && _splay(element) == 0;
|
||||
}
|
||||
|
||||
bool add(E element) {
|
||||
int compare = _splay(element);
|
||||
if (compare == 0) return false;
|
||||
_addNewRoot(_SoundSplayTreeNode(element), compare);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool remove(Object object) {
|
||||
if (!_validKey(object)) return false;
|
||||
return _remove(object) != null;
|
||||
}
|
||||
|
||||
void addAll(Iterable<E> elements) {
|
||||
for (E element in elements) {
|
||||
int compare = _splay(element);
|
||||
if (compare != 0) {
|
||||
_addNewRoot(_SoundSplayTreeNode(element), compare);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void removeAll(Iterable<Object> elements) {
|
||||
for (Object element in elements) {
|
||||
if (_validKey(element)) _remove(element);
|
||||
}
|
||||
}
|
||||
|
||||
void retainAll(Iterable<Object> elements) {
|
||||
// Build a set with the same sense of equality as this set.
|
||||
SoundSplayTreeSet<E> retainSet = SoundSplayTreeSet<E>(_comparator, _validKey);
|
||||
int modificationCount = _modificationCount;
|
||||
for (Object object in elements) {
|
||||
if (modificationCount != _modificationCount) {
|
||||
// The iterator should not have side effects.
|
||||
throw ConcurrentModificationError(this);
|
||||
}
|
||||
// Equivalent to this.contains(object).
|
||||
if (_validKey(object) && _splay(object) == 0) {
|
||||
retainSet.add(_root.key);
|
||||
}
|
||||
}
|
||||
// Take over the elements from the retained set, if it differs.
|
||||
if (retainSet._count != _count) {
|
||||
_root = retainSet._root;
|
||||
_count = retainSet._count;
|
||||
_modificationCount++;
|
||||
}
|
||||
}
|
||||
|
||||
E lookup(Object object) {
|
||||
if (!_validKey(object)) return null;
|
||||
int comp = _splay(object);
|
||||
if (comp != 0) return null;
|
||||
return _root.key;
|
||||
}
|
||||
|
||||
Set<E> intersection(Set<Object> other) {
|
||||
Set<E> result = SoundSplayTreeSet<E>(_comparator, _validKey);
|
||||
for (E element in this) {
|
||||
if (other.contains(element)) result.add(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Set<E> difference(Set<Object> other) {
|
||||
Set<E> result = SoundSplayTreeSet<E>(_comparator, _validKey);
|
||||
for (E element in this) {
|
||||
if (!other.contains(element)) result.add(element);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Set<E> union(Set<E> other) {
|
||||
return _clone()..addAll(other);
|
||||
}
|
||||
|
||||
SoundSplayTreeSet<E> _clone() {
|
||||
var set = SoundSplayTreeSet<E>(_comparator, _validKey);
|
||||
set._count = _count;
|
||||
set._root = _copyNode(_root);
|
||||
return set;
|
||||
}
|
||||
|
||||
// Copies the structure of a SplayTree into a new similar structure.
|
||||
// Works on _SplayTreeMapNode as well, but only copies the keys,
|
||||
_SoundSplayTreeNode<E> _copyNode(_SoundSplayTreeNode<E> node) {
|
||||
if (node == null) return null;
|
||||
return _SoundSplayTreeNode<E>(node.key)
|
||||
..left = _copyNode(node.left)
|
||||
..right = _copyNode(node.right);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_clear();
|
||||
}
|
||||
|
||||
Set<E> toSet() => _clone();
|
||||
|
||||
String toString() => IterableBase.iterableToFullString(this, '{', '}');
|
||||
}
|
118
benchmarks/TypedDataDuplicate/dart2/TypedDataDuplicate.dart
Normal file
118
benchmarks/TypedDataDuplicate/dart2/TypedDataDuplicate.dart
Normal file
|
@ -0,0 +1,118 @@
|
|||
// Copyright (c) 2020, 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-benchmarks for copying typed data lists.
|
||||
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
|
||||
abstract class Uint8ListCopyBenchmark extends BenchmarkBase {
|
||||
final int size;
|
||||
Uint8List input;
|
||||
Uint8List result;
|
||||
|
||||
Uint8ListCopyBenchmark(String method, this.size)
|
||||
: super('TypedDataDuplicate.Uint8List.$size.$method');
|
||||
|
||||
void setup() {
|
||||
input = Uint8List(size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = (i + 3) & 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
void warmup() {
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
run();
|
||||
}
|
||||
}
|
||||
|
||||
void teardown() {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (result[i] != ((i + 3) & 0xff)) {
|
||||
throw 'Unexpected result';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Uint8ListCopyViaFromListBenchmark extends Uint8ListCopyBenchmark {
|
||||
Uint8ListCopyViaFromListBenchmark(int size) : super('fromList', size);
|
||||
|
||||
void run() {
|
||||
result = Uint8List.fromList(input);
|
||||
}
|
||||
}
|
||||
|
||||
class Uint8ListCopyViaLoopBenchmark extends Uint8ListCopyBenchmark {
|
||||
Uint8ListCopyViaLoopBenchmark(int size) : super('loop', size);
|
||||
|
||||
void run() {
|
||||
result = Uint8List(input.length);
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
result[i] = input[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class Float64ListCopyBenchmark extends BenchmarkBase {
|
||||
final int size;
|
||||
Float64List input;
|
||||
Float64List result;
|
||||
|
||||
Float64ListCopyBenchmark(String method, this.size)
|
||||
: super('TypedDataDuplicate.Float64List.$size.$method');
|
||||
|
||||
void setup() {
|
||||
input = Float64List(size);
|
||||
for (int i = 0; i < size; ++i) {
|
||||
input[i] = (i - 7).toDouble();
|
||||
}
|
||||
}
|
||||
|
||||
void teardown() {
|
||||
for (int i = 0; i < size; ++i) {
|
||||
if (result[i] != (i - 7).toDouble()) {
|
||||
throw 'Unexpected result';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Float64ListCopyViaFromListBenchmark extends Float64ListCopyBenchmark {
|
||||
Float64ListCopyViaFromListBenchmark(int size) : super('fromList', size);
|
||||
|
||||
void run() {
|
||||
result = Float64List.fromList(input);
|
||||
}
|
||||
}
|
||||
|
||||
class Float64ListCopyViaLoopBenchmark extends Float64ListCopyBenchmark {
|
||||
Float64ListCopyViaLoopBenchmark(int size) : super('loop', size);
|
||||
|
||||
void run() {
|
||||
result = Float64List(input.length);
|
||||
for (var i = 0; i < input.length; i++) {
|
||||
result[i] = input[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
main() {
|
||||
final sizes = [8, 32, 256, 16384];
|
||||
final benchmarks = [
|
||||
for (int size in sizes) ...[
|
||||
Uint8ListCopyViaLoopBenchmark(size),
|
||||
Uint8ListCopyViaFromListBenchmark(size)
|
||||
],
|
||||
for (int size in sizes) ...[
|
||||
Float64ListCopyViaLoopBenchmark(size),
|
||||
Float64ListCopyViaFromListBenchmark(size)
|
||||
]
|
||||
];
|
||||
for (var bench in benchmarks) {
|
||||
bench.report();
|
||||
}
|
||||
}
|
135
benchmarks/Utf8Decode/dart2/Utf8Decode.dart
Normal file
135
benchmarks/Utf8Decode/dart2/Utf8Decode.dart
Normal file
|
@ -0,0 +1,135 @@
|
|||
// Copyright (c) 2020, 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.
|
||||
//
|
||||
// Benchmark for UTF-8 decoding
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:benchmark_harness/benchmark_harness.dart';
|
||||
|
||||
import 'datext_latin1_10k.dart';
|
||||
import 'entext_ascii_10k.dart';
|
||||
import 'netext_3_10k.dart';
|
||||
import 'rutext_2_10k.dart';
|
||||
import 'sktext_10k.dart';
|
||||
import 'zhtext_10k.dart';
|
||||
|
||||
class Utf8Decode extends BenchmarkBase {
|
||||
final String language;
|
||||
final String text;
|
||||
final int size;
|
||||
final bool allowMalformed;
|
||||
List<Uint8List> chunks;
|
||||
int totalInputSize;
|
||||
int totalOutputSize;
|
||||
|
||||
static String _makeName(String language, int size, bool allowMalformed) {
|
||||
String name = "Utf8Decode.$language.";
|
||||
name += size >= 1000000
|
||||
? "${size ~/ 1000000}M"
|
||||
: size >= 1000 ? "${size ~/ 1000}k" : "$size";
|
||||
if (allowMalformed) name += ".malformed";
|
||||
return name;
|
||||
}
|
||||
|
||||
Utf8Decode(this.language, this.text, this.size, this.allowMalformed)
|
||||
: super(_makeName(language, size, allowMalformed));
|
||||
|
||||
@override
|
||||
void setup() {
|
||||
Uint8List data = utf8.encode(text) as Uint8List;
|
||||
if (data.length != 10000) {
|
||||
throw "Expected input data of exactly 10000 bytes.";
|
||||
}
|
||||
if (size < data.length) {
|
||||
// Split into chunks.
|
||||
chunks = <Uint8List>[];
|
||||
int startPos = 0;
|
||||
for (int pos = size; pos < data.length; pos += size) {
|
||||
int endPos = pos;
|
||||
while ((data[endPos] & 0xc0) == 0x80) {
|
||||
endPos--;
|
||||
}
|
||||
chunks.add(Uint8List.fromList(data.sublist(startPos, endPos)));
|
||||
startPos = endPos;
|
||||
}
|
||||
chunks.add(Uint8List.fromList(data.sublist(startPos, data.length)));
|
||||
totalInputSize = data.length;
|
||||
totalOutputSize = text.length;
|
||||
} else if (size > data.length) {
|
||||
// Repeat data to the desired length.
|
||||
final Uint8List expanded = Uint8List(size);
|
||||
for (int i = 0; i < size; i++) {
|
||||
expanded[i] = data[i % data.length];
|
||||
}
|
||||
chunks = <Uint8List>[expanded];
|
||||
totalInputSize = size;
|
||||
totalOutputSize = text.length * size ~/ data.length;
|
||||
} else {
|
||||
// Use data as is.
|
||||
chunks = <Uint8List>[data];
|
||||
totalInputSize = data.length;
|
||||
totalOutputSize = text.length;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void run() {
|
||||
int lengthSum = 0;
|
||||
for (int i = 0; i < chunks.length; i++) {
|
||||
final String s = utf8.decode(chunks[i], allowMalformed: allowMalformed);
|
||||
lengthSum += s.length;
|
||||
}
|
||||
if (lengthSum != totalOutputSize) {
|
||||
throw "Output length doesn't match expected.";
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void exercise() {
|
||||
// Only a single run per measurement.
|
||||
run();
|
||||
}
|
||||
|
||||
@override
|
||||
void warmup() {
|
||||
BenchmarkBase.measureFor(run, 1000);
|
||||
}
|
||||
|
||||
@override
|
||||
double measure() {
|
||||
// Report time per input byte.
|
||||
return super.measure() / totalInputSize;
|
||||
}
|
||||
|
||||
@override
|
||||
void report() {
|
||||
// Report time in nanoseconds.
|
||||
final double score = measure() * 1000.0;
|
||||
print("$name(RunTime): $score ns.");
|
||||
}
|
||||
}
|
||||
|
||||
void main(List<String> args) {
|
||||
const texts = {
|
||||
"en": en,
|
||||
"da": da,
|
||||
"sk": sk,
|
||||
"ru": ru,
|
||||
"ne": ne,
|
||||
"zh": zh,
|
||||
};
|
||||
final bool testMalformed =
|
||||
args != null && args.isNotEmpty && args.first == "malformed";
|
||||
final benchmarks = [
|
||||
// Only benchmark with allowMalformed: false unless specified otherwise.
|
||||
for (bool allowMalformed in [false, if (testMalformed) true])
|
||||
for (int size in [10, 10000, 10000000])
|
||||
for (String language in texts.keys)
|
||||
() => Utf8Decode(language, texts[language], size, allowMalformed)
|
||||
];
|
||||
|
||||
benchmarks.forEach((bm) => bm().report());
|
||||
}
|
62
benchmarks/Utf8Decode/dart2/datext_latin1_10k.dart
Normal file
62
benchmarks/Utf8Decode/dart2/datext_latin1_10k.dart
Normal file
|
@ -0,0 +1,62 @@
|
|||
// This text is an extract from the Danish Wikipedia article about Donald Duck
|
||||
// (Anders And): https://da.wikipedia.org/wiki/Anders_And
|
||||
//
|
||||
// The extract is based on the Wikipedia database dump. All markup has been
|
||||
// removed using WikiExtractor: https://github.com/attardi/wikiextractor
|
||||
//
|
||||
// The material is licensed under the Creative Commons Attribution-Share-Alike
|
||||
// License 3.0: https://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
const String da = """
|
||||
Anders And
|
||||
|
||||
Anders Fauntleroy And er en berømt figur i Disneys tegnefilm og tegneserier. Selv om han ikke er særligt populær i USA, optræder han i mange ugeblade i Europa: I Norge, Tyskland, Sverige, Danmark, Holland, Italien med flere. Hans første optræden var i tegnefilmen "The Wise Little Hen" (Den kloge lille høne) fra 9. juni 1934. Hans karakteristiske stemme blev indtalt af radiokomikeren Clarence Nash, der siden fast lagde stemme til.
|
||||
Efter Nash' død i 1985 overtog tegnefilmsdubberen Tony Anselmo. Det var i øvrigt Nash selv, der trænede Anselmo til at lyde som Anders And. I Danmark har tre lagt stemme til Anders And: Bjarne H. Hansen, Dick Kaysø og Peter Zhelder. Dick Kaysø og Peter Zhelder lægger stadigvæk stemme til ham.
|
||||
|
||||
Anders And var en bifigur til filmen Den lille kloge Høne, han havde et langt spidst næb og en utrolig kort lunte; der skulle ikke meget til for at udløse et raserianfald hos ham. Klassikerne er fra 1930'erne, hvor Mickey, Fedtmule og Anders er et trekløver, der er film som "Mickeys Campingvogn," "Tårnuret" og "Ensomme Spøgelser." Fred Spencer og Dick Lundy var animatorer. Efterhånden opnåede anden større popularitet, hvad der ikke rigtig huede Disney, der ikke brød sig om den krakilske and. Men Anders blev forfremmet til hovedfigur i en serie film, der begyndte med "Anders And og Strudsen" i 1938. Med den var det "ande-teamet" hos Disney født: instruktøren Jack King og assistenterne Carl Barks (den senere serieskaber), Chuck Couch, Harry Reeves og Jack Hannah. Han var stadigvæk en hidsigprop, men han blev gjort mere nuanceret, og nye figurer blev tilføjet som nevøerne Rip, Rap og Rup, kæresten Andersine And og i en enkelt film Fætter Guf.
|
||||
|
||||
Under 2. verdenskrig blev der lavet film om anden i militærtjeneste, som han selvfølgelig gør meget klodset. Mest kendt er "Der Fuehrers Face" fra 1943, hvor anden drømmer, at han er i diktaturstaten Nutziland. Den film blev arkiveret af Disney efter krigen, men er senere vist meget uofficielt. Desuden medvirkede han i to mindre kendte spillefilm "Saludos Amigos" 1943 og "The Three Caballeros" 1945, der gav et lystigt billede af livet i Latinamerika.
|
||||
|
||||
Efter krigen blev omgivelserne mere hjemlige, men næppe mere fredfyldte. I 1947 overtog Jack Hannah pladsen som instruktør. Filmene handlede ofte om klammeri med andre, det kunne være en bi, Chip og Chap eller bare ting han ikke kunne få til at makke ret. Anders var her ofte plageånden, hvis egne drillerier kom tilbage som en boomerang.
|
||||
|
||||
Fjernsynets fremmarch i 1950'erne satte snart stopper for de korte tegnefilm, og Jack Hannah blev sat til at redigere Disneys tv-shows og kæde de korte tegnefilm sammen i et længere forløb. Her blev Anders gjort mere sympatisk, men det var eksperten i alt, Raptus von And, der gjorde mest for tv-showet. I et andet tv-show Disney Sjov medvirkede Anders i tv-serierne Rip, Rap og Rup på eventyr, Bonkers, og Rap Sjak. I Rip, Rap og Rup på eventyr, var han sjældent med, men fik et nyt tøj. I Rap Sjak var hans matrostøj skiftet ud med en hawaii-skjorte med røde og hvide blomster. I Bonkers var han også med kort tid.
|
||||
|
||||
Anders And gik til tegneserien allerede tidligt efter sin debut i 1934, hvor han i avisstriber optrådte med sin makker fra "The Wise Little Hen," Peter Gris. Fra den 10. februar 1935 blev han en del af Mickeys avisstribe som musens kujonagtige og drillesyge ven, men fra 1936 kaprede han hovedrollen i Silly Symphony-striben. Forfatteren Ted Osborne og tegneren Al Taliaferro, der stod bag serieudgaven af The Wise Little Hen, pressede på, og da det blev en succes kunne forfatteren Bob Karp i samarbejde med Taliaferro lave andens egen avisstribe.
|
||||
|
||||
Det blev en populær serie. I de allertidligste serier var Anders en hidsig, ondskabsfuld og pueril and med langt spidst næb og hænder, der knap kunne skelnes fra vinger. Senere blev han mere voksen, da han fik ansvaret for sine tre nevøer Rip, Rap og Rup, og kæresten Andersine kom til sammen med Bedstemor And.
|
||||
|
||||
Tegneseriefiguren Anders And blev senere udviklet yderligere af Carl Barks. I de tidlige tegnefilm var han for det meste doven og hidsig; men for at gøre hans figur brugbar til en tegneserie besluttede Barks at udvide hans personlighed. Anders' mest gennemgående karaktertræk er hans notoriske uheld, som desuden har den narrative funktion at give adgang til spændende eventyr, uden at serien udvikler sig for meget. På denne måde kan Anders eksempelvis blive involveret i alverdens skattejagter, og da han altid ender med at miste den fundne gevinst, kan alle historierne starte fra samme udgangspunkt.
|
||||
|
||||
For at give Anders en verden at bo i, skabte Barks byen Andeby i den amerikanske stat Calisota (som er en blanding af de to amerikanske stater Californien og Minnesota) med indbyggere som den rige onkel Joakim von And, den heldige Fætter Højben og den dygtige opfinder Georg Gearløs.
|
||||
|
||||
Anders lægger med sit engelske navn Donald Duck i øvrigt navn til donaldismen, fankulturen omkring Disney-tegneserier og -tegnefilm, som den norske forfatter Jon Gisle udviklede med sin bog af samme navn.
|
||||
|
||||
Anders bor i Andeby på Paradisæblevej 111 med sine tre nevøer Rip, Rap og Rup. De er stort set identiske, men de kan i nogle historier identificeres på, hvilken farve kasket de har på.
|
||||
|
||||
Ifølge Disneytegneserieforfatteren Don Rosa var Anders født et eller andet sted omkring 1920 men dette er "ikke" officielt. Ifølge Carl Barks' stamtræ (senere udviklet og genbygget af Don Rosa for den danske udgiver Egmont Serieforlaget) er Anders' forældre Hortensia von And og Rapmus And. Anders har en søster ved navn Della And, men hverken hun eller Anders' forældre optræder i tegnefilmene eller tegneserierne bortset fra særlige steder som eksempelvis i Her er dit liv, Joakim. Ifølge Don Rosa er Anders og Della tvillinger.
|
||||
|
||||
Fra tegnefilmen "Mr. Duck Steps Out" har Anders' kæreste været Andersine And, men han går i stadig fare for at miste hende til den heldige Fætter Højben. I nogle italienske historier er Anders også superhelt under dæknavnet "Stålanden".
|
||||
|
||||
Anders' onkel, Joakim von And, er den rigeste and i verden.
|
||||
|
||||
Sammen med onkelen og nevøerne Rip, Rap og Rup har Anders And rejst jorden rundt til mange forskellige lande og steder. Blandt disse er landet Langtbortistan, hvis navn blev opfundet af den danske oversætter Sonja Rindom og senere er gledet ind i almindeligt, dansk sprogbrug.
|
||||
|
||||
I de tidlige historier af Carl Barks havde Anders en hale, der stak langt bagud. Han havde langt næb og lang hals, og hans matrostrøje havde fire knapper. Hans lange hals passede godt til egenskaben nysgerrighed og det lange næb til hans hidsighed, hvilket var to egenskaber, der prægede ham mest. Disse tidlige historier var også præget af en grov komik i stil med tegnefilmene.
|
||||
|
||||
Siden fik han et mere strømlinet udseende og kortere næb (dog ikke så kort som visse tegnere i 1960'erne gjorde det!) og kun to knapper på trøjen, et udseende de øvrige andetegnere også tager til sig. Her blev hans natur også mere sammensat, og han ligner mest af alt en tragisk helt. Komikken kommer mere og mere til at ligge i replikkerne.
|
||||
|
||||
Anders And optræder særligt i jumbobøgerne med en hemmelig identitet i form af superhelten Stålanden. Figuren er inspireret af Batman og er skabt af den italienske forfatter Guido Martina i samarbejde med kunstneren Giovan Battista Carpi i 1969 i hans italienske navn Paperinik. Ofte tegnet af Romano Scarpa.
|
||||
|
||||
I lighed med inspirationskilden er han maskeret og har et hav af tekniske opfindelser, som han bruger i kamp mod forbryderne og overvejende! i kamp for at redde sit andet jeg, Anders And, ud af kniben. Den eneste, som kender hans hemmelige identitet, er opfinderen Georg Gearløs, som har udstyret gamle 313 med moderne udstyr og finurligheder. Det er ikke så tit at Stålanden optræder; næsten kun i Jumbobøger, men har dog også optrådt i Anders And-bladene med en gæstehistorie, hvor Stålanden kæmper mod en tidligere fjende, som nu havde kaldt sig Lord Hvalros.
|
||||
|
||||
Efter at Stålanden i starten af 1990'erne ikke var benyttet så meget af historieskriverne, forsvandt han næsten ud af Disney-universet indtil 1996, hvor en ny serie, "Stålanden på nye eventyr", startede med Stålanden i hovedrollen (umiddelbart inspireret af Marvel universet). Denne relancering af Stålanden gav karakteren det ekstra, der skulle til for at få et godt comeback.
|
||||
|
||||
I modsætning til størstedelen af Anders And-tegneserier har denne serie en sammenhængende forløb. I serien optræder kun sjældent de andre figurer fra de normale Anders And tegneserier; oftest er det kun en kort birolle eller en reference, men til gengæld er en mængde nye figurer kommet til.
|
||||
|
||||
Serien starter med, at Anders And er ansat som vicevært i Ducklair Tower, et højhus bygget af mangemilliardæren Everett Ducklair, en fremragende videnskabsmand og opfinder, som forlod Andeby og drog til et tibetansk kloster, Dhasam-Bul. Anders finder Globus, en kunstig intelligens skabt af Ducklair, samt Ducklair's arsenal af våben og opfindelser. Dette bliver starten på Anders' liv som Stålanden.
|
||||
En mængde temaer var ofte i centrum for historierne, bl.a. tidsrejser og Tidspolitiets kamp med tidspiraterne fra Organisationen, invasion fra rummet af de magtsyge evronianere, Stålandens samarbejde med FBI og meget mere.
|
||||
|
||||
Serien fik en fornyelse i 2001, hvilket ændrede markant på historierne. Everett Ducklair vender tilbage, slukker Globus, smider Stålanden ud af Ducklair Tower og genopretter sit gamle imperium. Undervejs bliver Ducklair's to døtre afsløret. De hader begge Everett af grunde, der ikke bliver afsløret til fulde, selvom det bliver antydet, at Everett skilte sig af med deres mor på en eller anden måde.
|
||||
Denne fornyelse holdt 18 numre, før serien blev afsluttet, dog uden at historierne fik en slutning.
|
||||
|
||||
Barks-historien "Anders And og den gyldne hjelm" fra 1954 kom i 2006 med i """;
|
35
benchmarks/Utf8Decode/dart2/entext_ascii_10k.dart
Normal file
35
benchmarks/Utf8Decode/dart2/entext_ascii_10k.dart
Normal file
|
@ -0,0 +1,35 @@
|
|||
// This text is an extract from the English Wikipedia article about Anarchism:
|
||||
// https://en.wikipedia.org/wiki/Anarchism
|
||||
//
|
||||
// The extract is based on the Wikipedia database dump. All markup has been
|
||||
// removed using WikiExtractor: https://github.com/attardi/wikiextractor
|
||||
//
|
||||
// The material is licensed under the Creative Commons Attribution-Share-Alike
|
||||
// License 3.0: https://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
const String en = """
|
||||
Anarchism
|
||||
|
||||
Anarchism is a radical political movement that is highly skeptical towards authority and rejects all forms of unjust hierarchy. It calls for the abolition of the state which it holds to be undesirable, unnecessary, and harmful. Anarchism advocates for the replacement of the state with stateless societies or other forms of free associations.
|
||||
|
||||
Anarchism's timeline stretches back to prehistory when people lived in anarchistic societies long before the establishment of formal states, kingdoms or empires. With the rise of organised hierarchical bodies, skepticism towards authority also rose, but it was not until the 19th century a self-conscious political movement was formed. During the latest half of 19th and the first decades of 20th century, the anarchist movement flourished to most parts of the world, and had a significant role in worker's struggles for emancipation. Various branches of anarchism were espoused during those times. Anarchists took part in several revolutions, most notably in the Spanish Civil War, where they were crushed by the fascists forces in 1939, marking the end of the classical era of anarchism. In the latest decades of the 20th century, the anarchist movement nonetheless became relevant and vivid once more.
|
||||
|
||||
Anarchism employ various tactics in order to meet their ideal ends; these can be broadly separated in revolutionary and evolutionary tactics. There is significant overlap between the two legs which are merely descriptive. Revolutionary tactics aim to bring down authority and state, and have taken a violent turn in the past. Evolutionary tactics aim to prefigure what an anarchist society would be like. Anarchism's thought, criticism and praxis has played a part in diverse fields of human society.
|
||||
|
||||
The etymological origin of the word "anarchism" is from the ancient Greek word "anarkhia", meaning "without a ruler", composed of the prefix "an-" (i.e. "without") and the word "arkhos" (i.e. "leader" or "ruler"). The suffix -ism denotes the ideological current that favours anarchy. The word "anarchism" appears in English from 1642 as "anarchisme" and the word "anarchy" from 1539. Various factions within the French Revolution labelled their opponents as "anarchists", although few such accused shared many views with later anarchists. Many revolutionaries of the 19th century such as William Godwin (17561836) and Wilhelm Weitling (18081871) would contribute to the anarchist doctrines of the next generation, but they did not use the word "anarchist" or "anarchism" in describing themselves or their beliefs.
|
||||
|
||||
The first political philosopher to call himself an "anarchist" () was Pierre-Joseph Proudhon (18091865), marking the formal birth of anarchism in the mid-19th century. Since the 1890s and beginning in France, the term "libertarianism" has often been used as a synonym for anarchism and its use as a synonym is still common outside the United States. On the other hand, some use "libertarianism" to refer to individualistic free-market philosophy only, referring to free-market anarchism as libertarian anarchism.
|
||||
|
||||
While opposition to the state is central to anarchist thought, defining anarchism is not an easy task as there is a lot of talk among scholars and anarchists on the matter and various currents perceive anarchism slightly differently. Hence, it might be true to say that anarchism is a cluster of political philosophies opposing authority and hierarchical organization (including the state, capitalism, nationalism and all associated institutions) in the conduct of all human relations in favour of a society based on voluntary association, on freedom and on decentralisation, but this definition has the same shortcomings as the definition based on etymology (which is simply a negation of a ruler), or based on anti-statism (anarchism is much more than that) or even the anti-authoritarian (which is an "a posteriori" conclusion). Nonetheless, major elements of the definition of anarchism include the following:
|
||||
|
||||
During the prehistoric era of mankind, an established authority did not exist. It was after the creation of towns and cities that institutions of authority were established and anarchistic ideas espoused as a reaction. Most notable precursors to anarchism in the ancient world were in China and Greece. In China, philosophical anarchism (i.e the discussion on the legitimacy of the state) was delineated by Taoist philosophers Zhuangzi and Lao Tzu. Likewise, anarchic attitudes were articulated by tragedians and philosophers in Greece. Aeschylus and Sophocles used the myth of Antigone to illustrate the conflict between rules set by the state and personal autonomy. Socrates questioned Athenian authorities constantly and insisted to the right of individual freedom of consciousness. Cynics dismissed human law ("nomos") and associated authorities while trying to live according to nature ("physis"). Stoics were supportive of a society based on unofficial and friendly relations among its citizens without the presence of a state.
|
||||
|
||||
During the Middle Ages, there was no anarchistic activity except some ascetic religious movements in the Islamic world or in Christian Europe. This kind of tradition later gave birth to religious anarchism. In Persia, Mazdak called for an egalitarian society and the abolition of monarchy, only to be soon executed by the king. In Basra, religious sects preached against the state. In Europe, various sects developed anti-state and libertarian tendencies. Libertarian ideas further emerged during the Renaissance with the spread of reasoning and humanism through Europe. Novelists fictionalised ideal societies that were based not on coercion but voluntarism. The Enlightenment further pushed towards anarchism with the optimism for social progress.
|
||||
|
||||
During the French Revolution, the partisan groups of Enrags and saw a turning point in the fermentation of anti-state and federalist sentiments. The first anarchist currents developed throughout the 18th centuryWilliam Godwin espoused philosophical anarchism in England, morally delegitimizing the state, Max Stirner's thinking paved the way to individualism, and Pierre-Joseph Proudhon's theory of mutualism found fertile soil in France. This era of classical anarchism lasted until the end of the Spanish Civil War of 1936 and is considered the golden age of anarchism.
|
||||
Drawing from mutualism, Mikhail Bakunin founded collectivist anarchism and entered the International Workingmen's Association, a class worker union later known as the First International that formed in 1864 to unite diverse revolutionary currents. The International became a significant political force, and Karl Marx a leading figure and a member of its General Council. Bakunin's faction, the Jura Federation and Proudhon's followers, the mutualists, opposed Marxist state socialism, advocating political abstentionism and small property holdings. After bitter disputes the Bakuninists were expelled from the International by the Marxists at the 1872 Hague Congress. Bakunin famously predicted that if revolutionaries gained power by Marxist's terms, they would end up the new tyrants of workers. After being expelled, anarchists formed the St. Imier International. Under the influence of Peter Kropotkin, a Russian philosopher and scientist, anarcho-communism overlapped with collectivism. Anarcho-communists, who drew inspiration from the 1871 Paris Commune, advocated for free federation and distribution of goods according to one's needs.
|
||||
|
||||
At the turning of the century, anarchism had spread all over the world. In China, small groups of students imported the humanistic pro-science version of anarcho-communism. Tokyo was a hotspot for rebellious youth from countries of the far east, pouring into the Japanese capital to study. In Latin America, So Paulo was a stronghold for anarcho-syndicalism where it became the most prominent left-wing ideology. During this time, a minority of anarchists adopted tactics of revolutionary political violence. This strategy became known as propaganda of the deed. The dismemberment of the French socialist movement into many groups and the execution and exile of many Communards to penal colonies following the suppression of the Paris Commune favoured individualist political expression and acts. Even though many anarchists distanced themselves from these terrorist acts, infamy came upon the movement. Illegalism was another strategy which some anarchists adopted these same years.
|
||||
Anarchists enthusiastically participated in the Russian Revolutiondespite concernsin opposition to the Whites. However, they met harsh suppression after the Bolshevik government was stabilized. Several anarchists from Petrograd and Moscow fled to Ukraine, notably leading to the Kronstadt rebellion and Nestor Makhno's struggle in the Free Territory. With the anarchists being crushed in Russia, two new antithetical currents emerged, namely platformism and synthesis anarchism. The former sought to create a coherent group that would push for the revolution while the latter were against anything that would resemble a political party. Seeing the victories of the Bolsheviks in the October Revolution and the resulting Russian Civil War, many workers and activists turned to communist parties which grew at the expense of anarchism and other socialist movements. In France and the United States, members of major syndicalist movements, the General Confederation of Labour and Industrial Workers of the World, left their organisations and joined the Communist International.
|
||||
|
||||
In the Spanish Civil War, anarchists and syndicalists (CNT and FAI) once again allied themselves with various currents of leftists. A long tradition of Spanish anarchism led to anarchists playing a pivotal role in the war. In response to the army rebellion, an anarchist-inspired movement of peasants and workers, supported by armed militias, took control of Barcelona and of large areas of rural Spain where they collectivised """;
|
24
benchmarks/Utf8Decode/dart2/netext_3_10k.dart
Normal file
24
benchmarks/Utf8Decode/dart2/netext_3_10k.dart
Normal file
|
@ -0,0 +1,24 @@
|
|||
// This text is an extract from the Nepali Wikipedia article about Nepal
|
||||
// (नेपाल): https://ne.wikipedia.org/wiki/नेपाल
|
||||
//
|
||||
// The extract is based on the Wikipedia database dump. All markup has been
|
||||
// removed using WikiExtractor: https://github.com/attardi/wikiextractor
|
||||
//
|
||||
// The material is licensed under the Creative Commons Attribution-Share-Alike
|
||||
// License 3.0: https://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
const String ne = """
|
||||
नेपाल
|
||||
|
||||
नेपाल (आधिकारिक नाम: सङ्घीय लोकतान्त्रिक गणतन्त्र नेपाल) दक्षिण एसियाली भूपरिवेष्ठित हिमाली राष्ट्र हो । यसको भौगोलिक अक्षांश २६ डिग्री २२ मिनेटदेखि ३० डिग्री २७ मिनेट उत्तर र ८० डिग्री ४ मिनेटदेखि ८८ डिग्री १२ मिनेट पूर्वी देशान्तरसम्म फैलिएको छ । यसको कूल क्षेत्रफल १,४७,१८१ वर्ग कि.मि छ । यो क्षेत्रफल पृथ्वीको कूल क्षेत्रफलको ०.०३% र एसिया महादेशको ०.३% पर्दछ । लण्डन स्थित "ग्रीनवीच मिनटाइम" भन्दा पूर्वतर्फ रहेकोले गौरीशङ्कर हिमालको नजिक भएर जाने ८६ डिग्री १५ मिनेट पूर्वी देशान्तरलाई आधार मानी नेपालको प्रमाणिक समय ५ घण्टा ४५ मिनेट आगाडि मानिएको छ ।
|
||||
|
||||
नेपालको पूर्वी सीमाना मेची नदीदेखि पश्चिमी सीमाना महाकाली नदीसम्मको औसत लम्वाई ८८५ कि.मि. छ । उत्तरदेखि दक्षिणको चौडाई भने एकनासको छैन । पूर्वी भागभन्दा पश्चिमी भाग केही चौडा छ । त्यस्तै मध्य भाग भने केही खुम्चिएको छ । यसमा अधिकतम चौडाई २४१ कि.मि. र न्यूनतम चौडाई १४५ कि.मि. रहेको छ । यसर्थ नेपालको औसत चौडाई १९३ कि.मि. रहेको छ । नेपालको उत्तरमा चीनको स्वशासित क्षेत्र तिब्बत पर्दछ भने दक्षिण, पूर्व र पश्चिममा भारत पर्दछ । नेपालका ८०% भन्दा बढी नागरिक हिन्दू धर्म मान्दछन् जुन विश्वकै सबैभन्दा बढी प्रतिशत हिन्दू धर्मावलम्बी हुने राष्ट्र पनि हो । यसबाहेक बौद्ध, इस्लाम, किराॅत आदि धर्म मान्ने मानिसहरू पनि यहाँ बसोबास गर्दछन् । एउटा सानो क्षेत्रको लागि नेपालको भौगोलिक विविधता निकै उल्लेखनीय छ । यहाँ तराईका उष्ण फाँटदेखि चिसा हिमालयका शृंखला अवस्थित छन् । संसारका सबैभन्दा उच्च १४ हिमश्रृंखलाहरु मध्ये ८ वटा नेपालमा पर्दछन्, जसमध्ये संसारको सर्वोच्च शिखर सगरमाथा (नेपाल र चीनको सीमानामा पर्ने) पनि एक हो । नेपालको प्रमुख सहर एवं राजधानी काठमाडौं हो । काठमाडौं, ललितपुर र भक्तपुर सहरहरूलाई काठमाडौं उपत्यका भनेर चिनिन्छ । अन्य प्रमुख सहरहरूमा भरतपुर, बिराटनगर, भैरहवा, वीरगञ्ज, जनकपुर, पोखरा, नेपालगञ्ज, धनगढी र महेन्द्रनगर पर्दछन् ।
|
||||
|
||||
नेपाल शब्दको उत्त्पत्ति बारेमा ठोस प्रमाण त उपलब्ध छैन, तर एक प्रसिद्ध विश्वास अनुसार मरिची ॠषि पुत्र 'ने' मुनिले पालन गरेको ठाउँको रूपमा यहाँको नाम नेपाल रहन गएको हो । निरन्तर रूपमा राजा-रजौटाहरूको अधीनमा रहेर फुट्ने र जुट्ने लामो तथा सम्पन्न इतिहास बोकेको, अहिले नेपाल भनेर चिनिने यो खण्डले वि. सं. २०४६ सालको आन्दोलन पश्चात् संवैधानिक राजतन्त्रको नीति अवलम्बन गर्यो । तर यस पश्चात् पनि राजसंस्था एक महत्त्वपूर्ण तथा अस्पष्ट परिधि तथा शक्ति भएको संस्थाको रूपमा रहिरह्यो । यो व्यवस्थामा पहिले संसदीय अनिश्चितता तथा सन् १९९६ देखि ने.क.पा.(माओवादी)को जनयुद्धको कारणले राष्ट्रिय अनिश्चितता देखियो ।
|
||||
|
||||
माओवादीहरूले राजनीतिको मूलाधारबाट अल्लगिएर भूमिगत रूपमा राजतन्त्र तथा मूलाधारका राजनीतिक दलहरूको विरुद्धमा गुरिल्ला युद्ध सञ्चालन गरे, जसको कारण १३,००० भन्दा बढी मानिसहरूको ज्यान जान पुग्यो । यही विद्रोहलाई दमन गर्ने पृष्ठभूमिमा राजाले सन् २००२ मा संसदको विघटन गरी निर्वाचित प्रधानमन्त्रीलाई अपदस्त गरेर प्रधानमन्त्री मनोनित गर्दै शासन चलाउन थाले । सन् २००५ मा उनले एकनासै संकटकालको घोषणा गरेर सबै कार्यकारी शक्ति ग्रहण गरे। सन् २००६को लोकतान्त्रिक आन्दोलन (जनाअन्दोलन-२) पश्चात् राजाले देशको सार्वभौमसत्ता जनतालाई हस्तान्तरण गरे तथा अप्रिल २४, २००६ मा भंग गरिएको संसद पूनर्स्थापित भयो । मे १८, २००६ मा आफूले पाएको सार्वभौमसत्ताको उपयोग गर्दै नयाँ प्रतिनिधि सभाले राजाको अधिकारमा कटौती गर्यो तथा नेपाललाई एक धर्मनिरपेक्ष राष्ट्र घोषणा गर्यो । अन्तरिम व्यवस्थापिका संसदले पहिले नै घोषणा गरिसकेको "सङ्घीय लोकतान्त्रिक गणराज्य, संविधानसभा" को पहिलो बैठकबाट मे २८, २००८ मा आधिकारिक रूपमा कार्यान्वयन भयो । नेपालको भूगोल सानो भए पनी नेपालीहरु को मन ठुलो छ ।
|
||||
|
||||
हिमालय क्षेत्रमा मानिसहरू बस्न थालेको कम्तिमा पनि ९,००० वर्ष भएको कुरा काठमाडौं उपत्यकामा पाइएका प्राचीन औजारहरूबाट पुष्टि हुन्छ। सम्भवत: भोट-बर्मेली मूलका मानिसहरू नेपालमा २,५०० वर्ष अगाडि बसोबास गर्दथे।
|
||||
|
||||
ईशापूर्व १५०० तिर इन्डो-आर्यन जातिहरू उपत्यका प्रवेश गरे। ईशापूर्वको १००० तिर स-साना राज्यहरू र राज्यसङ्गठनहरू बने। सिद्धार्थ गौतम (ईशापूर्व ५६३–४८३) त्यस्तै एक वंश, शाक्यवंशका राजकुमार थिए, जसले आफ्नो राजकाज त्यागी तपस्वीको जीवन अँगाले र उनी बुद्ध भनेर विश्व प्रसिद्ध भए।
|
||||
ईशापूर्वको २५० सम्ममा, यो क्षेत्र उत्तर""";
|
44
benchmarks/Utf8Decode/dart2/rutext_2_10k.dart
Normal file
44
benchmarks/Utf8Decode/dart2/rutext_2_10k.dart
Normal file
|
@ -0,0 +1,44 @@
|
|||
// This text is an extract from the Russian Wikipedia article about Lithuania
|
||||
// (Литва): https://ru.wikipedia.org/wiki/Литва
|
||||
//
|
||||
// The extract is based on the Wikipedia database dump. All markup has been
|
||||
// removed using WikiExtractor: https://github.com/attardi/wikiextractor
|
||||
//
|
||||
// The material is licensed under the Creative Commons Attribution-Share-Alike
|
||||
// License 3.0: https://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
const String ru = """
|
||||
Литва
|
||||
|
||||
Литва́ (), официальное название Лито́вская Респу́блика () государство, расположенное в северной части Европы. Столица страны Вильнюс.
|
||||
|
||||
Площадь км². Протяжённость с севера на юг 280 км, а с запада на восток 370 км. Население составляет человек (сентябрь, 2019). Занимает 140-е место место в мире по численности населения и 121-е по территории. Имеет выход к Балтийскому морю, расположена на его восточном побережье. Береговая линия составляет всего 99 км (наименьший показатель среди государств Балтии). На севере граничит с Латвией, на юго-востоке с Белоруссией, на юго-западе с Польшей и Калининградской областью России.
|
||||
|
||||
Член ООН с 1991 года, ЕС и НАТО с 2004 года, ОЭСР с мая 2018 года. Входит в Шенгенскую зону и Еврозону.
|
||||
|
||||
Независимость страны провозглашена 11 марта 1990 года, а юридически оформлена 6 сентября 1991 года. .
|
||||
|
||||
Этимология слова «Литва» точно не известна, при этом существует множество версий, ни одна из которых не получила всеобщего признания. Корень «лит» и его варианты «лет»/«лют» допускают различные толкования как в балтских и славянских, так и в других индоевропейских языках. Так, например, существуют созвучные топонимы на территории Словакии «"Lytva"» и Румынии «"Litua"», известные с XIXII веков. По мнению Е. Поспелова, топоним образован от древнего названия реки Летава (Lietavà от «лить», русское «Летаука»). Феодальное княжество, по землям которого протекала эта река, со временем заняло ведущее положение и название было распространено на всё государство. В «Повести временных лет» (XII век) упоминается этноним «литва», полностью совпадающий с названием местности «Литва» и по смыслу (территория, где живёт литва), и по форме.
|
||||
|
||||
Поверхность равнинная со следами древнего оледенения. Поля и луга занимают 57 % территории, леса и кустарники 30 %, болота 6 %, внутренние воды 1 %.
|
||||
|
||||
Высшая точка 293,84 м над уровнем моря холм Аукштояс (или Аукштасис калнас) в юго-восточной части страны, в 23,5 км от Вильнюса.
|
||||
|
||||
Крупнейшие реки Неман и Вилия.
|
||||
Более 3 тыс. озёр (1,5 % территории): крупнейшее из них Друкшяй на границе Латвии, Литвы и Белоруссии (площадь 44,8 км²), самое глубокое Таурагнас, 61 м), самое длинное Асвея длинной в 30 км у местечка Дубингяй.
|
||||
|
||||
Климат переходный от морского к континентальному. Средняя температура зимой 5 °C, летом +17 °C. Выпадает 748 мм осадков в год.
|
||||
|
||||
Полезные ископаемые: торф, минеральные материалы, строительные материалы.
|
||||
|
||||
Территория современной Литвы была заселена людьми с конца XIX тысячелетия до н. э. Жители занимались охотой и рыболовством, использовали лук и стрелы с кремнёвыми наконечниками, скребки для обработки кожи, удочки и сети. В конце неолита (IIIII тысячелетия до н. э.) на территорию современной Литвы проникли индоевропейские племена. Они занимались земледелием и скотоводством, при этом охота и рыболовство оставались основными занятиями местных жителей вплоть до широкого распространения железных орудий труда. Индоевропейцы, заселившие земли между устьями Вислы и Западной Двины, выделились в отдельную группу, названную учёными балтами.
|
||||
|
||||
Традиционно считается, что этническая основа Литвы сформирована носителями археологической культуры восточнолитовских курганов, сложившейся в V веке н. э. на территории современных Восточной Литвы и Северо-Западной Белоруссии. Около VII века литовский язык отделился от латышского.
|
||||
|
||||
Становление государственности на территории современной Литвы относят к XIII веку, при этом само название «Литва» впервые упомянуто в Кведлинбургских анналах под 1009 годом в сообщении об убийстве язычниками миссионера Бруно на границе Руси и Литвы. По наиболее распространённой версии, топоним возник от названия небольшой реки Летаука, притока Няриса. Согласно более современной гипотезе, название страны могло произойти от этнонима «леты» или «лейти», которым жители окрестных земель называли дружинников литовских князей.
|
||||
|
||||
В начале XIII века в земли балтов-язычников с запада началось вторжение немецких рыцарей-крестоносцев. Они покорили Пруссию и Ливонию. В это же время с юга началась экспансия Галицко-Волынского княжества. К середине XIII века многие литовские земли были объединены под властью князя Миндовга, принявшего в 1251 году католическое крещение и коронованного в 1253 году. Через несколько лет Миндовг отрёкся от христианства и до начала XIV века литовские земли оставались языческими. Несмотря на то, что уже в 1263 году Миндовг был свергнут, его правление положило начало более чем пятисотлетнему существованию Великого княжества Литовского.
|
||||
|
||||
В XIV начале XV веках территория Великого княжества Литовского стремительно росла, в основном за счёт присоединения земель Западной Руси. Включение в состав государства славянских земель, многократно превышающих по площади и количеству населения собственно литовские земли, привело к перениманию литовскими князьями, получившими во владение русские земли, православной культуры и западнорусского языка. Со временем западнорусский язык стал официальным языком канцелярии великих князей. Собственно литовский язык до XVI века оставался бесписьменным, хотя и продолжал использоваться на этнически литовских землях.
|
||||
|
||||
В 1385 году великий князь литовский Ягайло заключил Кревскую унию с Королевством Польским. По условиям унии, Ягайло обязался присоединить Великое княжество Литовское к Королевству Польскому и крестить литовские земли по католическому обряду, а сам становился королём Польши и сохранял титул великого князя литовского. Однако вскоре он вынужден был уступить власть в Великом княжестве Литовском своему двоюродному брату Витовту.""";
|
46
benchmarks/Utf8Decode/dart2/sktext_10k.dart
Normal file
46
benchmarks/Utf8Decode/dart2/sktext_10k.dart
Normal file
|
@ -0,0 +1,46 @@
|
|||
// This text is an extract from the Slovak Wikipedia article about Esperanto:
|
||||
// https://sk.wikipedia.org/wiki/Esperanto
|
||||
//
|
||||
// The extract is based on the Wikipedia database dump. All markup has been
|
||||
// removed using WikiExtractor: https://github.com/attardi/wikiextractor
|
||||
//
|
||||
// The material is licensed under the Creative Commons Attribution-Share-Alike
|
||||
// License 3.0: https://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
const String sk = """
|
||||
Esperanto (pôvodne Lingvo Internacia – „medzinárodný jazyk“) je najrozšírenejší medzinárodný plánový jazyk. Názov je odvodený od pseudonymu, pod ktorým v roku 1887 zverejnil lekár L. L. Zamenhof základy tohto jazyka. Zámerom tvorcu bolo vytvoriť ľahko naučiteľný a použiteľný neutrálny jazyk, vhodný na použitie v medzinárodnej komunikácii. Cieľom nebolo nahradiť národné jazyky, čo bolo neskôr aj deklarované v Boulonskej deklarácii.
|
||||
|
||||
Hoci žiaden štát neprijal esperanto ako úradný jazyk, používa ho komunita s odhadovaným počtom hovoriacich 100 000 až 2 000 000, z čoho približne 2 000 tvoria rodení hovoriaci. V Poľsku je na zozname nemateriálneho kultúrneho dedičstva. Získalo aj isté medzinárodné uznania, napríklad dve rezolúcie UNESCO či podporu známych osobností verejného života. V súčasnosti sa esperanto využíva pri cestovaní, korešpondencii, medzinárodných stretnutiach a kultúrnych výmenách, kongresoch, vedeckých diskusiách, v pôvodnej aj prekladovej literatúre, divadle a kine, hudbe, tlačenom aj internetovom spravodajstve, rozhlasovom a televíznom vysielaní.
|
||||
|
||||
Slovná zásoba esperanta pochádza predovšetkým zo západoeurópskych jazykov, zatiaľ čo jeho skladba a tvaroslovie ukazujú na silný slovanský vplyv. Morfémy sú nemenné a je možné ich kombinovať takmer bez obmedzení do rozmanitých slov; esperanto má teda mnoho spoločného s analytickými jazykmi, ako je čínština, zatiaľ čo vnútorná stavba jeho slov pripomína jazyky aglutinačné, ako je japončina, swahilčina alebo turečtina.
|
||||
|
||||
Pri zrode esperanta stál Ludwik Lejzer Zamenhof. Vyrastal v mnohojazyčnom, vtedy ruskom, teraz poľskom meste Białystok, kde bol svedkom častých sporov medzi jednotlivými národnosťami (Rusi, Poliaci, Nemci, Židia). Pretože za jednu z hlavných príčin týchto sporov považoval neexistenciu spoločného jazyka, začal už ako školák pracovať na projekte reči, ktorá by túto funkciu mohla plniť. Mala byť, na rozdiel od národných jazykov, neutrálna a ľahko naučiteľná, teda prijateľná ako druhý jazyk pre všetkých, jazyk vyučovaný spoločne s národnými jazykmi a používaný v situáciách vyžadujúcich dorozumenie medzi národmi.
|
||||
|
||||
Zamenhof najskôr uvažoval o oživení latinčiny, ktorú sa učil v škole, ale usúdil, že je pre bežné dorozumievanie zbytočne zložitá. Keď študoval angličtinu, všimol si, že časovanie slovies podľa osoby a čísla nie je nutné; že gramatický systém jazyka môže byť oveľa jednoduchší, než sa dovtedy nazdával. Stále však zostávala prekážka v memorovaní sa veľkého množstva slov. Raz Zamenhofa zaujali dva ruské nápisy: "швейцарская" [švejcarskaja] (vrátnica, odvodené od "швейцар" [švejcar] – vrátnik) a "кондитерская" [konditerskaja] (cukráreň, odvodené od "кондитер" [konditér] – cukrár). Tieto slová rovnakého zakončenia mu vnukli myšlienku, že používanie pravidelných predpôn a prípon by mohlo významne znížiť množstvo slovných koreňov nutných na dorozumenie sa. Aby boli korene čo najmedzinárodnejšie, rozhodol sa prevziať slovnú zásobu predovšetkým z románskych a germánskych jazykov, teda tých, ktoré boli vtedy v školách po celom svete vyučované najčastejšie.
|
||||
Prvý Zamenhofov projekt, nazvaný "Lingwe uniwersala," bol viac-menej hotový už v roku 1878, ale autorov otec, učiteľ jazykov, považoval túto prácu za márnu a utopistickú, a zrejme preto rukopis, ktorý mu bol zverený, zničil. V rokoch 1879 – 1885 Zamenhof študoval medicínu v Moskve a vo Varšave. V tej dobe začal znova pracovať na medzinárodnom jazyku. Prvú obnovenú verziu vyučoval v roku 1879 pre svojich priateľov. Po niekoľkých rokoch už prekladal poéziu, aby jazyk čo najviac zdokonalil. V roku 1885 autor napísal:
|
||||
|
||||
Zamenhofovi prichádzalo veľa nadšených listov, ktoré často prinášali najrôznejšie návrhy úprav jazyka. Všetky podnety zaznamenával a neskoršie ich začal uverejňovať v časopise "Esperantisto", vychádzajúcom v Norimbergu. V tom istom časopise aj dal o úpravách dvakrát hlasovať, väčšina čitateľov však so zmenami nesúhlasila. Po týchto hlasovaniach na určitý čas utíchli hlasy volajúce po reforme a jazyk sa začal rozširovať. Najviac odberateľov mal časopis vo vtedajšom Rusku. Veľkou ranou preň bolo, keď ruská cenzúra jeho šírenie zakázala kvôli článku Leva Nikolajeviča Tolstého. Časopis kvôli tomu musel byť zrušený, krátko na to bol však vystriedaný novým, nazvaným "Lingvo Internacia." Najskôr ho redigovali vo švédskej Uppsale, neskôr v Maďarsku a nakoniec v Paríži, kde jeho vydávanie zastavila až prvá svetová vojna.
|
||||
|
||||
Nový medzinárodný jazyk začali jeho používatelia skoro používať aj na organizáciu odbornej a záujmovej činnosti na medzinárodnej úrovni. V prvých desaťročiach prebiehala komunikácia v esperante takmer výhradne písomnou formou. Ale po nečakane úspešnom prvom Svetovom kongrese esperanta, usporiadanom v roku 1905 vo francúzskom meste Boulogne-sur-Mer, na ktorom sa overili možnosti používania tejto reči v hovorenej forme, začali naberať na intenzite aj osobné kontakty.
|
||||
|
||||
Esperanto začali pre svoju činnosť používať aj rôzne organizácie a hnutia. Už na svetovom kongrese v Barcelone roku 1909 sa uskutočnilo niekoľko stretnutí prítomných katolíkov, ktorí sa nakoniec rozhodli usporiadať v nadchádzajúcom roku, 1910, samostatný kongres katolíckych esperantistov. Počas neho bolo založené Medzinárodné združenie katolíckych esperantistov (IKUE – Internacia Katolika Unuiĝo Esperantista). Časopis "Espero Katolika" ("Katolícka nádej") vychádzal už od roku 1903 a s viac ako 100 rokmi svojej existencie je dnes najdlhšie vychádzajúcim esperantským periodikom.
|
||||
|
||||
V roku 1912 sa Zamenhof pri slávnostnom prejave ôsmeho Svetového kongresu esperanta v Krakove vzdal svojej oficiálnej úlohy v hnutí. Desiaty kongres sa mal konať v roku 1914 v Paríži, prihlásilo sa naň takmer 4 000 ľudí, ale nakoniec ho zrušili pre začínajúcu vojnu, Zamenhof sa vtedy musel vrátiť domov cez škandinávske štáty.
|
||||
|
||||
Po vojne túžba po harmónii a mieri vzbudila nové nádeje, vďaka čomu sa esperanto veľmi rýchlo šírilo. Prvý povojnový kongres sa konal v roku 1920 v Haagu, 13. svetový kongres v 1921 v Prahe. V roku 1927 bolo vo viedenskom Hofburgu otvorené Medzinárodné esperantské múzeum, v roku 1929 bolo pripojené k Rakúskej národnej knižnici a dnes sídli v samostatnej budove.
|
||||
|
||||
Snahy o presadenie esperanta ako univerzálneho jazyka sa stretávali s pozitívnou odozvou: Petíciu v jeho prospech adresovanú Organizácii Spojených národov podpísalo vyše 80 miliónov ľudí, v Česko-Slovensku napríklad prof. Jaroslav Heyrovský, nositeľ Nobelovej ceny.
|
||||
|
||||
Valné zhromaždenie UNESCO prijalo podobné rezolúcie v Montevideu 10. decembra 1954 a v Sofii 8. novembra 1985. Vzalo v nich na vedomie "výsledky dosiahnuté esperantom na poli medzinárodnej duchovnej výmeny aj zblíženia národov sveta" a vyzvalo členské štáty, "aby sa chopili iniciatívy pri zavádzaní študijných programov o jazykovom probléme a esperante na svojich školách a inštitúciách vyššieho vzdelávania".
|
||||
|
||||
K esperantu sa hlásila aj rada predsedov Poľskej akadémie vied. Jubilejného 72. Svetového kongresu esperanta roku 1987 (100. výročie uverejnenia prvej učebnice jazyka) sa vo Varšave zúčastnilo takmer 6 000 ľudí zo 60 národov.
|
||||
|
||||
Pokroky dosiahli aj katolícki esperantisti – roku 1990 bol vydaný dokument "Norme per la celebrazione della Messa in esperanto", ktorým Svätá stolica povoľuje vysluhovať sväté omše v tomto jazyku bez zvláštneho povolenia. Esperanto sa tak stalo jediným schváleným umelým liturgickým jazykom katolíckej cirkvi.
|
||||
|
||||
Skutočnosť, že mnohé z cieľov esperantského hnutia sa doteraz nepodarilo naplniť, je často prisudzovaná okrem iného technologickej a kultúrnej dominancii Spojeného kráľovstva a Spojených štátov amerických, predovšetkým v období po druhej svetovej vojne, vďaka čomu je v súčasnosti dorozumievacím jazykom väčšiny medzinárodných činností angličtina.
|
||||
|
||||
Už na začiatku 20. storočia bolo na území dnešného Slovenska (vtedy severná časť Uhorska) činné esperantské hnutie. Esperantistov a kluby zastrešovala „Uhorská esperantská spoločnosť” a „Verda Standardo”. V Prahe boli činné spolky "Bohema Unio Esperantista", ktorý prijímal len organizácie a kluby, a "Bohema Asocio Esperantista", ktorý prijímal jednotlivcov. Oba spolky vydávali svoje časopisy. V roku 1907, 20 rokov po zverejnení jazyka Zamenhofom, vydal tolstojovec Albert Škarvan spolu s Rusom N. P. Evstifejevom prvú učebnicu esperanta v slovenčine, „Základy medzinárodnej reči ESPERANTO“.
|
||||
|
||||
Po prvej svetovej vojne sa oba pražské spolky zlúčili do "Československej Esperantskej Asociácie". Tá bola v roku 1936 premenovaná na "Esperantskú Asociáciu v Československej republike". V tomto období bolo hnutie veľmi aktívne, fungovalo mnoho klubov, konalo sa veľa prednášok a kurzov. Esperanto bolo vyučované na školách rôznych stupňov, rádio Bratislava od 1930 vysielalo kurzy a od 1932 aj kultúrny program v esperante. Bola vydaná "Československá antológia" predstavujúca diela 20 slovenských autorov. V rámci protifašistickej aktivity vychádzali aj preklady protifašistických článkov z esperantských časopisov z obdobia Španielskej občianskej vojny.
|
||||
|
||||
Druhá svetová vojna utlmila esperantské hnutie. Bratislavský esperantský klub požiadal o zmenu štatútu a rozšírenie poľa pôsobnosti na celú vtedajšiu Slovenskú republiku a následne sa stal strediskom esperantského hnutia na Slovensku.""";
|
73
benchmarks/Utf8Decode/dart2/zhtext_10k.dart
Normal file
73
benchmarks/Utf8Decode/dart2/zhtext_10k.dart
Normal file
|
@ -0,0 +1,73 @@
|
|||
// This text is an extract from the Chinese Wikipedia article about Kanji
|
||||
// (汉字): https://zh.wikipedia.org/wiki/汉字
|
||||
//
|
||||
// The extract is based on the Wikipedia database dump. All markup has been
|
||||
// removed using WikiExtractor: https://github.com/attardi/wikiextractor
|
||||
//
|
||||
// The material is licensed under the Creative Commons Attribution-Share-Alike
|
||||
// License 3.0: https://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
const String zh = """
|
||||
最簡單的漢字只有一笔画,但卻不止一個字:除了「一」字以外,「乙」、「〇」、「丶」、「丨」、「亅」、「丿」、「乀」、「乁」、「𠄌」、「𠃋」、「𠃉」、「𠃊」、「乚」等都是漢字,而且都有各自的讀音。
|
||||
|
||||
中文汉字中,笔画最多的汉字可能是“”,是一种面食的名称,此字至今习用,其不同写法的笔画数在54至71画之间不等。被传统辞典收录的笔画最多的汉字为《字汇补》、《汉语大字典》中由四个“-{龍}-”字组成的「」字,共64画;同樣屬於64劃的字由四個“-{興}-”字組成的“𠔻”字,收入自《中文大辭典》;之後的是由四個「雷」字組成的“䨻”字,有52劃,收錄於《說文解字》。
|
||||
|
||||
另外,日本汉字「」收录于日本的TRON计划中,但此字无法提供有效证据表明其确有使用,因此状况存疑。该字由3個「-{龍}-」字和3個「-{雲}-」個組合而成,共有84劃。该字曾提交到当时的统一码扩展C区,编号为JMK66147,后因扩展C区的时间原因被安排到了扩展D区,之后因找不到合适证据被撤销。最后提交到扩展G区并被接受。
|
||||
|
||||
現在,純漢字僅僅被用於記錄漢語。而漢字和假名一起被用於記錄日語。
|
||||
|
||||
其他一些民族在早期會將漢字單純作為表音文字來記錄他們的語言。如蒙古語最早的文獻蒙古秘史即用純漢字當做表音文字進行記錄。日語最早的文獻也是把漢字當做表音文字來記錄日語,後來演變出萬葉假名。
|
||||
|
||||
契丹文、女真文、西夏文的創製受到了漢字的影響,它們跟漢字一樣都是方塊型文字,筆畫形狀也極其類似,也採用類似六書的造字法。但這些文字除個別字與漢字外形相同外,絕大部分字形都跟漢字不同,因此在Unicode中它們都是獨立區塊編碼的。
|
||||
|
||||
古壯字(方塊壯字)、古白字(方塊白字)、古布依字(方塊布依字)、字喃等文字可以說是漢字在其他語言中的擴充,因為它們很大一部分本身就是漢字(賦予新義),另一些則是用已有漢字偏旁組合構成新字,因此,這些文字的外觀上與漢字很相似,在Unicode中與漢字一道編入漢字區。
|
||||
|
||||
女書是用於記錄漢語的另一種文字,它們的造字法與六書有部分相似之處,但字的外觀與漢字差異較大,Unicode中作為獨立區塊編碼。
|
||||
|
||||
以上文字都因各種原因而消亡,如今除專家學者外無幾人能識。
|
||||
|
||||
日語的假名()是由漢字的草體、簡筆演變而成的。諺文和日語假名一樣可以和漢字一同混寫。
|
||||
|
||||
此外如蒙古文、滿文、錫伯文等也是在漢字書寫方式和書寫工具的影響下,將從右向左書寫的源自察合台文的書寫方式改為從上到下書寫,文字的結構也隨之有所變化。
|
||||
|
||||
漢字是承載文化的重要工具,目前留有大量用漢字書寫的典籍。不同的方言、甚至語言都使用漢字作為共同書寫體系。在古代日本、朝鮮半島、越南、琉球群島,以及位於婆羅洲的蘭芳共和國,漢字都曾是該國正式文書的唯一系統,因而漢字在歷史上對文明的傳播分享有著重要作用。
|
||||
|
||||
由於漢字和發聲的聯繫不是非常密切,比較容易被其他民族所借用,如日本、朝鮮半島和越南都曾經有過不會說漢語,單純用漢字書寫的歷史階段。漢字的這個特點對於維繫一個文化圈—一個充滿各種互相不能交流的方言群體的民族——發揮了主要的作用。
|
||||
|
||||
漢字對周邊國家的文化產生過巨大的影響,形成了一個共同使用漢字的漢字文化圈,在日本、越南和朝鮮半島、琉球群島,漢字被融合成它們語言的文字「」、「」、「」。直到現在,日語中仍然把漢字認為是書寫體系的一部分。在北韓和越南,已經完全不再使用漢字;在韓國,漢字的使用在近幾十年來越來越少;但是由於朝鮮語/韓語中使用了大量的漢字詞彙,並且重音現象嚴重,所以在需要嚴謹表達的場合時仍然會使用漢字。雖然在通常情況下人名、公司機構名稱等均使用韓文書寫,不過大多數的人名、公司機構均有其對應的漢字名稱。
|
||||
|
||||
漢字於公元3世紀經朝鮮半島輾轉傳入日本。二戰後日本開始限制漢字的數量和使用,頒布了《當用漢字表》及《人名用字表》等,其中簡化了部分漢字(日本新字體),不過文學創作使用的漢字,並不在限制之列。日本除從中文中傳入的漢字外,還創造和簡化了一些漢字,如「-{辻}-」(十字路口)、「-{栃}-」、「-{峠}-」(山道)和「-{広}-」(廣)、「-{転}-」(轉)、「-{働}-」(勞動)等。
|
||||
|
||||
公元3世紀左右,漢字傳入了朝鮮半島,朝鮮語/韓語曾經完全使用漢字來書寫。相傳薛聰在當時發明了吏讀,把朝鮮語用同音或同義的漢字來表示。例如:「乙」字被用來表示韓語中的後綴「-l()」。由於有不少發音都沒有對應的漢字,所以朝鮮半島的人民又運用組字法,把兩個或多個漢字合組成為一個新的吏讀字。相傳後來的契丹文就是受到吏讀字的影響。此外尚有鄉札、口訣等以漢字表記朝鮮語的方法。
|
||||
|
||||
1443年,朝鮮世宗大王頒布《訓民正音》,發明了諺文與漢字一起使用,但當中有不少部件仍然有昔日吏讀字的痕跡。現在的大韓民國雖禁止在正式場合下使用漢字,並停止了在中小學中教授漢字(但是從2011年開始,大韓民國的李明博政府已經決定將漢字重新納入中小學的課程裡),不過漢字在民間仍在繼續使用,且可以按照個人習慣書寫,但是現在能寫一筆漂亮漢字的韓國人越來越少。朝鮮民主主義人民共和國於1948年廢除了漢字,僅保留了十幾個漢字(參見廢除漢字)。
|
||||
|
||||
公元1世紀漢字便傳入了越南,越南語也曾完全使用漢字做為書寫用文字,並在漢字的基礎上創造了喃字,但是由於書寫不便,漢字仍是主要的書寫方式。
|
||||
|
||||
1945年越南民主共和國成立後廢除漢字,使用了稱為「國語字」的拼音文字。現在的越南文已經看不出漢字的痕跡了。
|
||||
|
||||
中國許多民俗都與漢字有關,例如:
|
||||
|
||||
漢字獨特優美的結構,書寫的主要工具——毛筆有多樣的表現力,因而產生了中文獨特的造型藝術——書法。而篆刻是和書法相關的藝術,用刀在石材上雕刻出篆字作為印章,尚有勒石、山壁題字等。
|
||||
同一个汉字,可以有不同的字体。當前漢字字體主要有篆書、隷書、草書、行書、楷書等。
|
||||
|
||||
漢字歷史上是不斷在組新字的,目前的各種漢字並非同时定型于某一年代,而是應時代需要逐渐發展而来的。例如:“人”字在商朝就已出现,“凹”字和“凸”字則是在唐朝才出現的。
|
||||
|
||||
此外不同的行業也会因用字需求而造字。例如:中国的傳統音乐在記譜上會使用減字譜、工尺譜。
|
||||
|
||||
自十九世紀中葉後,亞洲和西方都發佈了很多漢字拉丁化方案,如:
|
||||
|
||||
現在,漢語拼音方案是使用最廣且被聯合國接受的汉字拉丁化方案。而威妥瑪拼音歷史悠久,至今仍用於臺灣的人名、地名拼寫。
|
||||
汉字中存在许多异体字,它们的意义和读音完全相同,只是写法不同。异体字的产生部分是由于历史原因,有的则是人为造字,如「和、咊、-{龢}-」、「秋、-{秌}-、龝」等。
|
||||
|
||||
臺灣也有使用所謂的異體字,例如“-{臺}-”與“-{台}-”、“-{體}-”與“-{体}-”以及“-{學}-”與“-{学}-”等等。
|
||||
|
||||
中国大陆於1956年公布整理异体字表,废除了大量异体字,但後來因為各種原因恢復了部分異體字。如“-{於}-”曾被當作“-{于}-”的異體字廢除掉,但在1988年發表的《現代漢語通用字表》中又恢復成為規範字,因爲姓氏中「-{于}-」和「-{於}-」同時存在,不宜合併。另外,不同地區對異體字的取捨有所不同,例如:韓國就以漢字各種異體字中最早出現的樣式為標準寫法。所以,在韓語漢字的標準中,取“甛”而不取“甜”、取“-{幇}-”而不取“-{幫}-”、取“-{畵}-”而不取“-{畫}-”。
|
||||
|
||||
由于英文文字是由26个字母排列组合而成的文字,因此可以简化输入步骤;相比较之下汉字则不能如此,从字形上汉字虽然可以拆解成不同的部分,但是被分成的部首或偏旁数量过多,这样不但不能达到简化输入的目的,反而显得更为繁琐。于是从汉字字音上去考虑,汉字输入被分成少量的语音元素组合排列,反而可以达到简化输入的步骤。因为是语音输入对汉字的读音必须清楚,某些生僻字或不知道汉字发音的则会很困难,这在一定程度上限制了汉字的输入。
|
||||
|
||||
由于打字機鍵盤是為歐美文字設計的,在設計時本身沒有考慮汉字輸入的問題,輸入漢字往往比輸入拼音文字困難。汉字没有经过中文打字機的普及,直接进入了電腦中文信息处理阶段。在電腦發明初期曾引起漢字能否適應電腦時代的問題,支持漢字拉丁化的學者甚至以此為理據。
|
||||
|
||||
随着各种中文输入法的出现,汉字的计算机输入、存储、输出技术得到了基本解决,大大提高了中文写作、出版、信息检索等的效率。目前中文输入法有上千种之多,主要包括表音输入和表形输入两类,也有两者兼之的。汉字的语音输入、手写识别和光学字符识别(OCR)技术也已得到广泛应用。
|
||||
|
||||
如收录数千字的GB 2312(中國大陸)、B""";
|
Loading…
Reference in a new issue