diff --git a/accepted/future-releases/spread-collections/benchmarks/bin/lrhn_benchmark.dart b/accepted/future-releases/spread-collections/benchmarks/bin/lrhn_benchmark.dart new file mode 100644 index 0000000000..a254e5f5fe --- /dev/null +++ b/accepted/future-releases/spread-collections/benchmarks/bin/lrhn_benchmark.dart @@ -0,0 +1,53 @@ +import"dart:collection"; +void copy1(Map from, Map to) { + for (var entry in from.entries) { + to[entry.key] = entry.value; + } +} +void copy2(Map from, Map to) { + for (var key in from.keys) { + to[key] = from[key]; + } +} +void copy3(Map from, Map to) { + var tmp = to; + from.forEach((key, value) { + tmp[key] = value; + }); + tmp = null; +} +void copy4(Map from, Map to) { + to.addAll(from); +} + +main() { + for (int i = 0; i < 5; i++) { + bench("entries", copy1); + bench("keys", copy2); + bench("forEach", copy3); + bench("addAll", copy4); + } +} + +int id(int x)=>x; +var maps = List.generate(100, (n) { + var map = Map.fromIterable(Iterable.generate(n * 10), key: (n) =>"#$n"); + if (n % 4 == 1) map = SplayTreeMap.from(map); + if (n % 4 == 2) map = HashMap.from(map); + return map; +}); + +void bench(String name, void Function(Map, Map) action) { + var e = 0; + var c = 0; + var sw = Stopwatch()..start(); + do { + for (var from in maps) { + var to = {}; + action(from, to); + c += from.length; + } + e = sw.elapsedMilliseconds; + } while (e < 2000); + print("$name: ${c/e} entries/ms"); +} diff --git a/accepted/future-releases/spread-collections/benchmarks/bin/profile_map.dart b/accepted/future-releases/spread-collections/benchmarks/bin/profile_map.dart new file mode 100644 index 0000000000..e569ac0762 --- /dev/null +++ b/accepted/future-releases/spread-collections/benchmarks/bin/profile_map.dart @@ -0,0 +1,302 @@ +import 'dart:collection'; + +const trials = 2000; +const rounds = 4; + +var csv = StringBuffer(); + +bool warmup = true; + +//class CustomMap extends MapBase { +// final Map _inner; +// +// CustomMap(this._inner); +// +// operator [](Object key) => _inner[key]; +// +// void operator []=(key, value) => _inner[key] = value; +// +// void clear() => throw "not implemented"; +// +// Iterable get keys => _inner.keys; +// +// remove(Object key) => throw "not implemented"; +//} + +void main() { + benchmarks(); +} + +void benchmarks() { + final maps = makeMaps([0, 1, 2, 5, 10, 20, 50, 100], (map) { + return [ + map, + SplayTreeMap.of(map), + HashMap.of(map), + ]; + }); + + for (var i = 1; i <= rounds; i++) { + warmup = i < rounds; + + var baseline = bench("addEntries", maps, addEntries); + var entriesMap = + bench("iterate entries into map", maps, iterateEntriesIntoNewMap, baseline); + var keysMap = bench("iterate keys into map", maps, iterateKeysIntoNewMap, baseline); + var forEachMap = bench("forEach into map", maps, forEachIntoNewMap, baseline); + + log(); + + var justEntries = bench("just iterate entries", maps, iterateEntries); + var justKeys = bench("just iterate keys", maps, iterateKeys); + var justForEach = bench("just forEach", maps, forEach); + + log(); + + log("(entries insertion overhead)", entriesMap - justEntries); + log("(keys insertion overhead)", keysMap - justKeys); + log("(forEach insertion overhead)", forEachMap - justForEach); + } +} + +Map makeMap(int size) { + var result = {}; + for (var i = 0; i < size; i++) { + var key = ""; + var n = i + 1; + while (n > 0) { + key += String.fromCharCode((n % 26) + 65); + n ~/= 26; + } + + result[key] = i; + } + + return result; +} + +List> makeMaps(List sizes, + List> Function(Map) callback) { + var maps = >[]; + for (var size in sizes) { + var map = makeMap(size); + maps.addAll(callback(map)); + } + + return maps; +} + +double bench(String label, List> maps, + void Function(Map) action, [double baseline]) { + var watch = Stopwatch()..start(); + for (var i = 0; i < trials; i++) { + for (var i = 0; i < maps.length; i++) { + action(maps[i]); + } + } + + watch.stop(); + var microPerTrial = watch.elapsedMicroseconds / (trials * maps.length); + log(label, microPerTrial, baseline); + return microPerTrial; +} + +void log([String label, double microseconds, double baseline]) { + if (warmup) return; + + if (microseconds != null) { + var perMs = (1000 / microseconds).toStringAsFixed(2); + var output = "${label.padLeft(30)} ${perMs.padLeft(10)} spreads/ms"; + if (baseline != null) { + var relative = baseline / microseconds; + output += " ${relative.toStringAsFixed(2).padLeft(5)}x"; + } + print(output); + } else if (label != null) { + print("--- $label ---"); + } else { + print(""); + } +} + +void iterateEntries(Map from) { + var sum = 0; + for (var entry in from.entries) { + sum += entry.value; + } + + preventOptimization(sum); +} + +void iterateEntriesIntoNewMap(Map from) { + var to = {"a": 1, "b": 2}; + + for (var entry in from.entries) { + to[entry.key] = entry.value; + } + + to["b"] = 3; + to["c"] = 4; + + preventOptimization(to.length); +} + +void iterateKeys(Map from) { + var sum = 0; + for (var key in from.keys) { + sum += from[key]; + } + + preventOptimization(sum); +} + +void iterateKeysIntoNewMap(Map from) { + var to = {"a": 1, "b": 2}; + + for (var key in from.keys) { + to[key] = from[key]; + } + + to["b"] = 3; + to["c"] = 4; + + preventOptimization(to.length); +} + +void forEach(Map from) { + var sum = 0; + from.forEach((key, value) { + sum += value; + }); + + preventOptimization(sum); +} + +void forEachIntoNewMap(Map from) { + var to = {"a": 1, "b": 2}; + + var temp = to; + from.forEach((key, value) { + temp[key] = value; + }); + temp = null; + + to["b"] = 3; + to["c"] = 4; + + preventOptimization(to.length); +} + +void addEntries(Map from) { + var to = {"a": 1, "b": 2}; + + to.addEntries(from.entries); + + to["b"] = 3; + to["c"] = 4; + + preventOptimization(to.length); +} + +/// Ensure [obj] is used in some way so that the optimizer doesn't eliminate +/// the code that produces it. +void preventOptimization(Object obj) { + if (obj == "it will never be this") print("!"); +} + +//double runBench(String name, int length, +// void Function(Map, Map) action, +// [double baseline]) { +// var from = {}; +// for (var i = 0; i < length; i++) { +// from[String.fromCharCode(i % 26 + 65)] = i; +// } +// +// var froms = [from, CustomMap(from)]; +// +// var rate = benchBest(froms, action); +// +// if (baseline == null) { +// print("${length.toString().padLeft(4)} ${name.padRight(15)} " +// "${rate.toStringAsFixed(2).padLeft(10)} spreads/ms " +// " ${'-' * 20}"); +// } else { +// var comparison = rate / baseline; +// var bar = "=" * (comparison * 20).toInt(); +// if (comparison > 4.0) bar = "!!!"; +// print("${length.toString().padLeft(4)} ${name.padRight(15)} " +// "${rate.toStringAsFixed(2).padLeft(10)} spreads/ms " +// "${comparison.toStringAsFixed(2).padLeft(6)}x baseline $bar"); +// } +// +// csv.writeln("$length,$name,$rate"); +// return rate; +//} +// +///// Runs [bench] a number of times and returns the best (highest) result. +//double benchBest(List> froms, +// void Function(Map, Map) action) { +// var best = 0.0; +// for (var i = 0; i < 4; i++) { +// var result = bench(froms, action); +// if (result > best) best = result; +// } +// +// return best; +//} +// +//double bench(List> froms, +// void Function(Map, Map) action) { +// var elapsed = 0; +// var count = 0; +// var watch = Stopwatch()..start(); +// do { +// for (var i = 0; i < froms.length; i++) { +// var from = froms[i]; +// var to = {"a": 1, "b": 2}; +// +// action(from, to); +// to["b"] = 3; +// to["c"] = 4; +// +// count++; +// } +// elapsed = watch.elapsedMilliseconds; +// } while (elapsed < trialMs); +// +// return count / elapsed; +//} +// +//void iterateEntries(Map from, Map to) { +// for (var entry in from.entries) { +// to[entry.key] = entry.value; +// } +//} + +//void addList(Map from, Map to) { +// var length = from.length; +// for (var i = 0; i < length; i++) { +// to.add(from[i]); +// } +//} +// +//void resizeAndSet(Map from, Map to) { +// var length = from.length; +// var j = to.length; +// to.length = to.length + length; +// for (var i = 0; i < length; i++) { +// to[j] = from[i]; +// } +//} +// +//void addAll(Map from, Map to) { +// to.addAll(from); +//} +// +//void forEach(Map from, Map to) { +// var temp = to; +// from.forEach((s) { +// temp.add(s); +// }); +// temp = null; +//}