| // Copyright (c) 2014, 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. |
| |
| /** |
| * The track map is a simple wrapper around a map that keeps track |
| * of the 'final' size of maps grouped by description. It allows |
| * determining the distribution of sizes for a specific allocation |
| * site and it can be used like this: |
| * |
| * Map<String, int> map = new TrackMap<String, int>("my-map"); |
| * |
| * After finishing the compilaton, the histogram of track map sizes |
| * is printed but only when running in verbose mode. |
| */ |
| class TrackMap<K, V> implements Map<K, V> { |
| final Map<K, V> _map; |
| final List _counts; |
| static final Map<String, List<int>> _countsMap = {}; |
| |
| TrackMap._internal(this._counts) : _map = new Map<K, V>(); |
| |
| factory TrackMap(String description) { |
| List counts = _countsMap.putIfAbsent(description, () => [0]); |
| Map result = new TrackMap<K, V>._internal(counts); |
| counts[0]++; |
| return result; |
| } |
| |
| static void printHistogram() { |
| _countsMap.forEach((description, counts) { |
| print('$description -- ${counts.length} maps'); |
| |
| // Count the total number of maps. |
| int sum = 0; |
| for (int i = 0; i < counts.length; i++) { |
| sum += counts[i]; |
| } |
| int increment = sum ~/ 10; |
| int target = increment; |
| int accumulated = 0; |
| for (int i = 0; i < counts.length; i++) { |
| accumulated += counts[i]; |
| if (accumulated >= target) { |
| String percent = (accumulated / sum * 100).toStringAsFixed(1); |
| print(' -- $percent%: length <= $i'); |
| target += increment; |
| } |
| } |
| }); |
| } |
| |
| int get length => _map.length; |
| bool get isEmpty => _map.isEmpty; |
| bool get isNotEmpty => _map.isNotEmpty; |
| |
| Iterable<K> get keys => _map.keys; |
| Iterable<V> get values => _map.values; |
| |
| bool containsKey(Object key) => _map.containsKey(key); |
| bool containsValue(Object value) => _map.containsValue(value); |
| |
| V operator [](Object key) => _map[key]; |
| String toString() => _map.toString(); |
| |
| void forEach(void action(K key, V value)) { |
| _map.forEach(action); |
| } |
| |
| void operator []=(K key, V value) { |
| if (!_map.containsKey(key)) { |
| _notifyLengthChanged(1); |
| _map[key] = value; |
| } |
| } |
| |
| V putIfAbsent(K key, V ifAbsent()) { |
| if (containsKey(key)) return this[key]; |
| V value = ifAbsent(); |
| this[key] = value; |
| return value; |
| } |
| |
| V remove(Object key) { |
| if (_map.containsKey(key)) { |
| _notifyLengthChanged(-1); |
| } |
| return _map.remove(key); |
| } |
| |
| void addAll(Map<K, V> other) { |
| other.forEach((key, value) => this[key] = value); |
| } |
| |
| void clear() { |
| _notifyLengthChanged(-_map.length); |
| _map.clear(); |
| } |
| |
| Map<KR, VR> cast<KR, VR>() => _map.cast<KR, VR>(); |
| |
| Map<KR, VR> retype<KR, VR>() => _map.retype<KR, VR>(); |
| |
| Iterable<MapEntry<K, V>> get entries => _map.entries; |
| |
| void addEntries(Iterable<MapEntry<K, V>> entries) { |
| for (var entry in entries) this[entry.key] = entry.value; |
| } |
| |
| Map<KR, VR> map<KR, VR>(MapEntry<KR, VR> transform(K key, V value)) => |
| _map.map(transform); |
| |
| V update(K key, V update(V value), {V ifAbsent()}) => |
| _map.update(key, update, ifAbsent: ifAbsent); |
| |
| void updateAll(V update(K key, V value)) { |
| _map.updateAll(update); |
| } |
| |
| void removeWhere(bool test(K key, V value)) { |
| int before = _map.length; |
| _map.removeWhere(test); |
| _notifyLengthChanged(_map.length - before); |
| } |
| |
| void _notifyLengthChanged(int delta) { |
| int oldLength = _map.length; |
| int newLength = oldLength + delta; |
| _counts[oldLength]--; |
| if (newLength < _counts.length) { |
| _counts[newLength]++; |
| } else { |
| _counts.add(1); |
| assert(newLength == _counts.length - 1); |
| } |
| } |
| } |