[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:
Jonas Termansen 2020-06-02 12:53:24 +00:00
parent 91e4856905
commit 6f66f82625
40 changed files with 5992 additions and 2 deletions

14
DEPS
View file

@ -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}}",

View 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());
}

View 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);
}

View 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');
}

View 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;

View 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.");
}
}

View 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();
}

View 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());
}

View 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');

View 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);
}

View 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();
}

File diff suppressed because it is too large Load diff

View 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);
}

View 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/

View 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

View 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;
}

View 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());
}

View 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;
}

View 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();
}
}

View 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();
}
}
}

View file

@ -0,0 +1 @@
export 'dart:_internal' show sendAndExit;

File diff suppressed because one or more lines are too long

View 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();
}

View file

@ -0,0 +1,3 @@
main() {
print('Hello, world!');
}

View 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();
}

View file

@ -0,0 +1,3 @@
main() {
print('Hello, world!');
}

View 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();
}
}

View 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());
}

View 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
```

View 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}';
}
}

View 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");
}

View 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, '{', '}');
}

View 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();
}
}

View 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());
}

View 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 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 , 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 Paradisæblevej 111 med sine tre nevøer Rip, Rap og Rup. De er stort set identiske, men de kan i nogle historier identificeres , hvilken farve kasket de har .
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 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 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 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 """;

View 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 """;

View 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 = """
(िि : ि ) ि ि िि ि ि २६ ि २२ िि ३० ि २७ ि ८० ि िि ८८ ि १२ ि ि ,४७,१८१ ि.ि .०३% ि .३% ि "ग्रीनवीच मिनटाइम" ि ि ८६ ि १५ ि ि ४५ ि ि ि
ि ि ८८५ ि.ि. ि ि ि ि ि २४१ ि.ि. १४५ ि.ि. १९३ ि.ि. ि ि ि, ि ८०% ि ि ि ि ि ि , , ि ि ि ि ि ि िि ि ि ि ि ि १४ ि , ि ( ) ि , ि िि , ि, , , , , ,
ि , ि ि ि ि 'ने' ि ि - ि , ि िि ि. . २०४६ ि ि ि िि ि ि ि िि १९९६ ि ...() ि िि ि
ि ि ि ि ि ि , १३, ि ि ि २००२ ि िि ि २००५ ि २००६ ि (-) ि २४, २००६ ि ि १८, २००६ ििि ि ि ि ि ि ि "सङ्घीय लोकतान्त्रिक गणराज्य, संविधानसभा" ि २८, २००८ िि
ि ि ि ि , ि : - ि ,५०० ि
१५०० ि - ि १००० ि - ि ( ५६३४८३) , ि, ि ि
२५० , """;

View 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 году великий князь литовский Ягайло заключил Кревскую унию с Королевством Польским. По условиям унии, Ягайло обязался присоединить Великое княжество Литовское к Королевству Польскому и крестить литовские земли по католическому обряду, а сам становился королём Польши и сохранял титул великого князя литовского. Однако вскоре он вынужден был уступить власть в Великом княжестве Литовском своему двоюродному брату Витовту.""";

View 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 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 nemenné a je možné ich kombinovať takmer bez obmedzení do rozmanitých slov; esperanto 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 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ý 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 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 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. 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 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.
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". 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.""";

View 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 = """
丿𠄌𠃋𠃉𠃊
5471-{}-6464-{}-𠔻52
TRON计划中使3-{}-3-{}-84C区JMK66147C区的时间原因被安排到了扩展D区G区并被接受
西Unicode中它們都是獨立區塊編碼的
Unicode中與漢字一道編入漢字區
Unicode中作為獨立區塊編碼
滿
使
滿
使使使使使使
3使使-{}--{}--{}--{}--{}--{}-
3使-l
1443使使2011使1948
1便使便
1945使
使
西
使
-{}--{}-
使-{}--{}--{}--{}--{}--{}-
1956-{}--{}-1988-{}--{}--{}--{}--{}--{}-
26
OCR广
GB 2312B""";