Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library object_graph; | 5 library object_graph; |
| 6 | 6 |
| 7 import 'dart:async'; | |
| 7 import 'dart:typed_data'; | 8 import 'dart:typed_data'; |
| 8 | 9 import 'dart:collection'; |
| 9 import 'dominator_tree.dart'; | |
| 10 | 10 |
| 11 // Port of dart::ReadStream from vm/datastream.h. | 11 // Port of dart::ReadStream from vm/datastream.h. |
| 12 class ReadStream { | 12 class _ReadStream { |
| 13 int _cur = 0; | 13 int position = 0; |
| 14 final ByteData _data; | 14 int _size = 0; |
| 15 | 15 final List<ByteData> _chunks; |
| 16 ReadStream(this._data); | 16 |
| 17 | 17 _ReadStream(this._chunks) { |
| 18 int get pendingBytes => _data.lengthInBytes - _cur; | 18 int n = _chunks.length; |
| 19 | 19 for (var i = 0; i < n; i++) { |
| 20 var chunk = _chunks[i]; | |
| 21 if (i + 1 != n) { | |
| 22 assert(chunk.lengthInBytes == (1 << 20)); | |
| 23 } | |
| 24 _size += chunk.lengthInBytes; | |
| 25 } | |
| 26 } | |
| 27 | |
| 28 int get pendingBytes => _size - position; | |
| 29 | |
| 30 int getUint8(i) { | |
| 31 return _chunks[i >> 20].getUint8(i & 0xFFFFF); | |
| 32 } | |
| 33 | |
| 20 int readUnsigned() { | 34 int readUnsigned() { |
| 21 int result = 0; | 35 int result = 0; |
| 22 int shift = 0; | 36 int shift = 0; |
| 23 while (_data.getUint8(_cur) <= maxUnsignedDataPerByte) { | 37 while (getUint8(position) <= maxUnsignedDataPerByte) { |
| 24 result |= _data.getUint8(_cur) << shift; | 38 result |= getUint8(position) << shift; |
| 25 shift += dataBitsPerByte; | 39 shift += dataBitsPerByte; |
| 26 ++_cur; | 40 ++position; |
| 27 } | 41 } |
| 28 result |= (_data.getUint8(_cur) & byteMask) << shift; | 42 result |= (getUint8(position) & byteMask) << shift; |
| 29 ++_cur; | 43 ++position; |
| 30 return result; | 44 return result; |
| 31 } | 45 } |
| 32 | 46 |
| 33 static const int dataBitsPerByte = 7; | 47 static const int dataBitsPerByte = 7; |
| 34 static const int byteMask = (1 << dataBitsPerByte) - 1; | 48 static const int byteMask = (1 << dataBitsPerByte) - 1; |
| 35 static const int maxUnsignedDataPerByte = byteMask; | 49 static const int maxUnsignedDataPerByte = byteMask; |
| 36 } | 50 } |
| 37 | 51 |
| 38 class ObjectVertex { | 52 class ObjectVertex { |
| 39 // Never null. The isolate root has id 0. | |
| 40 final int _id; | 53 final int _id; |
|
koda
2015/05/19 20:12:35
The root now has id 1?
rmacnak
2015/05/19 22:16:07
Yes, documented below by the arrays this indexes i
| |
| 41 bool get isRoot => _id == 0; | 54 final ObjectGraph _graph; |
| 42 // TODO(koda): Include units in object graph metadata. | 55 |
| 43 int addressForWordSize(int bytesPerWord) => _id * 2 * bytesPerWord; | 56 ObjectVertex._(this._id, this._graph); |
| 44 // null for VM-heap objects. | 57 |
| 45 int _shallowSize; | 58 bool operator ==(other) => _id == other._id && _graph == other._graph; |
| 46 int get shallowSize => _shallowSize; | 59 int get hashCode => _id; |
| 47 int _retainedSize; | 60 |
| 48 int get retainedSize => _retainedSize; | 61 int get retainedSize => _graph._retainedSizes[_id]; |
| 49 // null for VM-heap objects. | 62 ObjectVertex get dominator => new ObjectVertex._(_graph._doms[_id], _graph); |
| 50 int _classId; | 63 |
| 51 int get classId => _classId; | 64 int get shallowSize { |
| 52 final List<ObjectVertex> succ = new List<ObjectVertex>(); | 65 var stream = new _ReadStream(_graph._chunks); |
| 53 ObjectVertex(this._id) : _retainedSize = 0; | 66 stream.position = _graph._positions[_id]; |
| 54 String toString() => '$_id,$_shallowSize,$succ'; | 67 stream.readUnsigned(); // addr |
| 68 return stream.readUnsigned(); // shallowSize | |
| 69 } | |
| 70 | |
| 71 int get vmCid { | |
| 72 var stream = new _ReadStream(_graph._chunks); | |
| 73 stream.position = _graph._positions[_id]; | |
| 74 stream.readUnsigned(); // addr | |
| 75 stream.readUnsigned(); // shallowSize | |
| 76 return stream.readUnsigned(); // cid | |
| 77 } | |
| 78 | |
| 79 get successors => new _SuccessorsIterable(_graph, _id); | |
| 80 | |
| 81 int get address { | |
| 82 // Note that everywhere else in this file, "address" really means an address | |
| 83 // scaled down by kObjectAlignment. They were scaled down so they would fit | |
| 84 // into Smis on client. | |
| 85 var stream = new _ReadStream(_graph._chunks); | |
| 86 stream.position = _graph._positions[_id]; | |
| 87 var scaledAddr = stream.readUnsigned(); | |
| 88 return scaledAddr * _graph._kObjectAlignment; | |
| 89 } | |
| 90 | |
| 91 List<ObjectVertex> get dominatorTreeChildren { | |
|
koda
2015/05/19 20:12:35
Is this really fast/simple enough to be a getter?
rmacnak
2015/05/19 22:16:07
No, this is O(2 million) for a dart2js heap.
| |
| 92 var N = _graph._N; | |
| 93 var doms = _graph._doms; | |
| 94 | |
| 95 var parentId = _id; | |
| 96 var domChildren = []; | |
| 97 | |
| 98 for (var childId = 1; childId <= N; childId++) { | |
| 99 if (doms[childId] == parentId) { | |
| 100 domChildren.add(new ObjectVertex._(childId, _graph)); | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 return domChildren; | |
| 105 } | |
| 55 } | 106 } |
| 56 | 107 |
| 57 // See implementation of ObjectGraph::Serialize for format. | 108 class _SuccessorsIterable extends IterableBase<ObjectVertex> { |
| 58 class ObjectGraph { | 109 final ObjectGraph _graph; |
| 59 final Map<int, ObjectVertex> _idToVertex = new Map<int, ObjectVertex>(); | 110 final int _id; |
| 60 | 111 |
| 61 ObjectVertex _asVertex(int id) { | 112 _SuccessorsIterable(this._graph, this._id); |
| 62 return _idToVertex.putIfAbsent(id, () => new ObjectVertex(id)); | 113 |
| 114 Iterator<ObjectVertex> get iterator => new _SuccessorsIterator(_graph, _id); | |
| 115 } | |
| 116 | |
| 117 class _SuccessorsIterator implements Iterator<ObjectVertex> { | |
| 118 final ObjectGraph _graph; | |
| 119 _ReadStream _stream; | |
| 120 | |
| 121 ObjectVertex current; | |
| 122 | |
| 123 _SuccessorsIterator(this._graph, int id) { | |
| 124 _stream = new _ReadStream(this._graph._chunks); | |
| 125 _stream.position = _graph._positions[id]; | |
| 126 _stream.readUnsigned(); // addr | |
| 127 _stream.readUnsigned(); // shallowSize | |
| 128 _stream.readUnsigned(); // cid | |
| 63 } | 129 } |
| 64 | 130 |
| 65 void _addFrom(ReadStream stream) { | 131 bool moveNext() { |
| 66 ObjectVertex obj = _asVertex(stream.readUnsigned()); | 132 while (true) { |
| 67 obj._shallowSize = stream.readUnsigned(); | 133 var nextAddr = _stream.readUnsigned(); |
| 68 obj._classId = stream.readUnsigned(); | 134 if (nextAddr == 0) return false; |
| 69 int last = stream.readUnsigned(); | 135 var nextId = _graph._addrToId[nextAddr]; |
| 70 while (last != 0) { | 136 if (nextId == null) continue; |
| 71 obj.succ.add(_asVertex(last)); | 137 current = new ObjectVertex._(nextId, _graph); |
| 72 last = stream.readUnsigned(); | 138 return true; |
| 73 } | 139 } |
| 74 } | 140 } |
| 141 } | |
| 75 | 142 |
| 76 ObjectGraph(ReadStream reader) { | 143 class _VerticesIterable extends IterableBase<ObjectVertex> { |
| 77 while (reader.pendingBytes > 0) { | 144 final ObjectGraph _graph; |
| 78 _addFrom(reader); | 145 |
| 79 } | 146 _VerticesIterable(this._graph); |
| 80 _computeRetainedSizes(); | 147 |
| 81 _mostRetained = new List<ObjectVertex>.from( | 148 Iterator<ObjectVertex> get iterator => new _VerticesIterator(_graph); |
| 82 vertices.where((u) => !u.isRoot)); | 149 } |
| 150 | |
| 151 class _VerticesIterator implements Iterator<ObjectVertex> { | |
| 152 final ObjectGraph _graph; | |
| 153 | |
| 154 int _nextId = 0; | |
| 155 ObjectVertex current; | |
| 156 | |
| 157 _VerticesIterator(this._graph); | |
| 158 | |
| 159 bool moveNext() { | |
| 160 if (_nextId == _graph._N) return false; | |
| 161 current = new ObjectVertex._(_nextId++, _graph); | |
| 162 return true; | |
| 163 } | |
| 164 } | |
| 165 | |
| 166 class ObjectGraph { | |
| 167 ObjectGraph(List<ByteData> chunks, int nodeCount) | |
| 168 : this._chunks = chunks | |
| 169 , this._N = nodeCount; | |
| 170 | |
| 171 int get size => _size; | |
| 172 int get vertexCount => _N; | |
| 173 int get edgeCount => _E; | |
| 174 | |
| 175 ObjectVertex get root => new ObjectVertex._(1, this); | |
| 176 Iterable<ObjectVertex> get vertices => new _VerticesIterable(this); | |
| 177 | |
| 178 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { | |
| 179 List<ObjectVertex> _mostRetained = | |
| 180 new List<ObjectVertex>.from(vertices.where((u) => !u.isRoot)); | |
| 83 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); | 181 _mostRetained.sort((u, v) => v.retainedSize - u.retainedSize); |
| 84 } | |
| 85 | 182 |
| 86 Iterable<ObjectVertex> get vertices => _idToVertex.values; | |
| 87 List<ObjectVertex> _mostRetained; | |
| 88 | |
| 89 ObjectVertex get root => _asVertex(0); | |
| 90 | |
| 91 Iterable<ObjectVertex> getMostRetained({int classId, int limit}) { | |
| 92 var result = _mostRetained; | 183 var result = _mostRetained; |
| 93 if (classId != null) { | 184 if (classId != null) { |
| 94 result = result.where((u) => u.classId == classId); | 185 result = result.where((u) => u.classId == classId); |
| 95 } | 186 } |
| 96 if (limit != null) { | 187 if (limit != null) { |
| 97 result = result.take(limit); | 188 result = result.take(limit); |
| 98 } | 189 } |
| 99 return result; | 190 return result; |
| 100 } | 191 } |
| 101 | 192 |
| 102 void _computeRetainedSizes() { | 193 Future process(statusReporter) async { |
| 103 // The retained size for an object is the sum of the shallow sizes of | 194 // We build futures here instead of marking the steps as async to avoid the |
| 104 // all its descendants in the dominator tree (including itself). | 195 // heavy lifting being inside a tranformed method. |
| 105 var d = new Dominator(); | 196 |
| 106 for (ObjectVertex u in vertices) { | 197 statusReporter.add("Finding node positions..."); |
| 107 if (u.shallowSize != null) { | 198 await new Future(() => _buildPositions()); |
| 108 u._retainedSize = u.shallowSize; | 199 |
| 109 d.addEdges(u, u.succ.where((ObjectVertex v) => v.shallowSize != null)); | 200 statusReporter.add("Finding post order..."); |
| 110 } | 201 await new Future(() => _buildPostOrder()); |
| 111 } | 202 |
| 112 d.computeDominatorTree(root); | 203 statusReporter.add("Finding predecessors..."); |
| 113 // Compute all retained sizes "bottom up", starting from the leaves. | 204 await new Future(() => _buildPredecessors()); |
| 114 // Keep track of number of remaining children of each vertex. | 205 |
| 115 var degree = new Map<ObjectVertex, int>(); | 206 statusReporter.add("Finding dominators..."); |
| 116 for (ObjectVertex u in vertices) { | 207 await new Future(() => _buildDominators()); |
| 117 var v = d.dominator(u); | 208 |
| 118 if (v != null) { | 209 _firstPreds = null; |
| 119 degree[v] = 1 + degree.putIfAbsent(v, () => 0); | 210 _preds = null; |
| 120 } | 211 _postOrderIndex = null; |
| 121 } | 212 |
| 122 var leaves = new List<ObjectVertex>(); | 213 statusReporter.add("Finding retained sizes..."); |
| 123 for (ObjectVertex u in vertices) { | 214 await new Future(() => _calculateRetainedSizes()); |
| 124 if (!degree.containsKey(u)) { | 215 |
| 125 leaves.add(u); | 216 _postOrder = null; |
| 126 } | 217 |
| 127 } | 218 statusReporter.add("Done"); |
| 128 while (!leaves.isEmpty) { | 219 return this; |
| 129 var v = leaves.removeLast(); | 220 } |
| 130 var u = d.dominator(v); | 221 |
| 131 if (u == null) continue; | 222 final List<ByteData> _chunks; |
| 132 u._retainedSize += v._retainedSize; | 223 |
| 133 if (--degree[u] == 0) { | 224 int _kObjectAlignment; |
| 134 leaves.add(u); | 225 int _N; |
| 135 } | 226 int _E; |
| 136 } | 227 int _size; |
| 137 } | 228 |
| 138 } | 229 Map<int, int> _addrToId = new Map<int, int>(); |
| 230 | |
| 231 // Indexed by node id, with id 0 representing invalid/uninitialized. | |
| 232 Uint32List _positions; // Position of the node in the snapshot. | |
| 233 Uint32List _postOrder; // post-order index -> id | |
| 234 Uint32List _postOrderIndex; // id -> post-order index | |
| 235 Uint32List _firstPreds; // Offset into preds. | |
| 236 Uint32List _preds; | |
| 237 Uint32List _doms; | |
| 238 Uint32List _retainedSizes; | |
| 239 | |
| 240 void _buildPositions() { | |
| 241 var N = _N; | |
| 242 var addrToId = _addrToId; | |
| 243 | |
| 244 var positions = new Uint32List(N + 1); | |
| 245 | |
| 246 var stream = new _ReadStream(_chunks); | |
| 247 _kObjectAlignment = stream.readUnsigned(); | |
| 248 | |
| 249 var id = 1; | |
| 250 while (stream.pendingBytes > 0) { | |
| 251 positions[id] = stream.position; | |
| 252 var addr = stream.readUnsigned(); | |
| 253 var shallowSize = stream.readUnsigned(); | |
| 254 var cid = stream.readUnsigned(); | |
| 255 addrToId[addr] = id; | |
| 256 | |
| 257 var succAddr = stream.readUnsigned(); | |
| 258 while (succAddr != 0) { | |
| 259 succAddr = stream.readUnsigned(); | |
| 260 } | |
| 261 id++; | |
| 262 } | |
| 263 assert(id == (N + 1)); | |
| 264 | |
| 265 var root = addrToId[0]; | |
| 266 assert(root == 1); | |
| 267 | |
| 268 _positions = positions; | |
| 269 } | |
| 270 | |
| 271 void _buildPostOrder() { | |
| 272 var N = _N; | |
| 273 var E = 0; | |
| 274 var addrToId = _addrToId; | |
| 275 var positions = _positions; | |
| 276 | |
| 277 var postOrder = new Uint32List(N); | |
| 278 var postOrderIndexx = new Uint32List(N + 1); | |
|
koda
2015/05/19 20:12:35
xx?
rmacnak
2015/05/19 22:16:07
Renamed to postOrderIndices.
| |
| 279 var stackNodes = new Uint32List(N); | |
| 280 var stackCurrentEdgePos = new Uint32List(N); | |
| 281 | |
| 282 var visited = new Uint8List(N + 1); | |
| 283 var postOrderIndex = 0; | |
| 284 var stackTop = 0; | |
| 285 var root = 1; | |
| 286 | |
| 287 stackNodes[0] = root; | |
| 288 | |
| 289 var stream = new _ReadStream(_chunks); | |
| 290 stream.position = positions[root]; | |
| 291 stream.readUnsigned(); // addr | |
| 292 stream.readUnsigned(); // shallowSize | |
| 293 stream.readUnsigned(); // cid | |
| 294 stackCurrentEdgePos[0] = stream.position; | |
| 295 visited[root] = 1; | |
| 296 | |
| 297 while (stackTop >= 0) { | |
| 298 var n = stackNodes[stackTop]; | |
| 299 var edgePos = stackCurrentEdgePos[stackTop]; | |
| 300 | |
| 301 stream.position = edgePos; | |
| 302 var childAddr = stream.readUnsigned(); | |
| 303 if (childAddr != 0) { | |
| 304 stackCurrentEdgePos[stackTop] = stream.position; | |
| 305 var childId = addrToId[childAddr]; | |
| 306 if (childId == null) continue; | |
| 307 ++E; | |
| 308 if (visited[childId] == 1) continue; | |
| 309 | |
| 310 ++stackTop; | |
| 311 stackNodes[stackTop] = childId; | |
| 312 | |
| 313 stream.position = positions[childId]; | |
| 314 stream.readUnsigned(); // addr | |
| 315 stream.readUnsigned(); // shallowSize | |
| 316 stream.readUnsigned(); // cid | |
| 317 stackCurrentEdgePos[stackTop] = stream.position; // i.e., first edge | |
| 318 visited[childId] = 1; | |
| 319 } else { | |
| 320 // Done with all children. | |
| 321 postOrderIndexx[n] = postOrderIndex; | |
| 322 postOrder[postOrderIndex++] = n; | |
| 323 --stackTop; | |
| 324 } | |
| 325 } | |
| 326 | |
| 327 assert(postOrderIndex == N); | |
| 328 assert(postOrder[N - 1] == root); | |
| 329 | |
| 330 _postOrder = postOrder; | |
| 331 _postOrderIndex = postOrderIndexx; | |
| 332 _E = E; | |
| 333 } | |
| 334 | |
| 335 void _buildPredecessors() { | |
| 336 var N = _N; | |
| 337 var E = _E; | |
| 338 var addrToId = _addrToId; | |
| 339 var positions = _positions; | |
| 340 | |
| 341 var numPreds = new Uint32List(N + 2); | |
|
koda
2015/05/19 20:12:35
Explain the +2 (space for sentinel(s)?)
rmacnak
2015/05/19 22:16:07
Added comment. +1 for 0 sentinel and +1 for derivi
| |
| 342 var preds = new Uint32List(E); | |
| 343 | |
| 344 // Count predecessors of each node. | |
| 345 var stream = new _ReadStream(_chunks); | |
| 346 for (var i = 1; i <= N; i++) { | |
| 347 stream.position = positions[i]; | |
| 348 stream.readUnsigned(); // addr | |
| 349 stream.readUnsigned(); // shallowSize | |
| 350 stream.readUnsigned(); // cid | |
| 351 var succAddr = stream.readUnsigned(); | |
| 352 while (succAddr != 0) { | |
| 353 var succId = addrToId[succAddr]; | |
| 354 if (succId != null) { | |
| 355 numPreds[succId]++; | |
| 356 } | |
| 357 succAddr = stream.readUnsigned(); | |
| 358 } | |
| 359 } | |
| 360 | |
| 361 // Assign indices into predecessors array. | |
| 362 var firstPreds = numPreds; // Alias. | |
| 363 var nextPreds = new Uint32List(N + 1); | |
| 364 var predIndex = 0; | |
| 365 for (var i = 1; i <= N; i++) { | |
| 366 var thisPredIndex = predIndex; | |
| 367 predIndex += numPreds[i]; | |
| 368 firstPreds[i] = thisPredIndex; | |
| 369 nextPreds[i] = thisPredIndex; | |
| 370 } | |
| 371 assert(predIndex == E); | |
| 372 firstPreds[N + 1] = E; // Extra entry for cheap boundry detection. | |
| 373 | |
| 374 // Fill predecessors array. | |
| 375 for (var i = 1; i <= N; i++) { | |
| 376 stream.position = positions[i]; | |
| 377 stream.readUnsigned(); // addr | |
| 378 stream.readUnsigned(); // shallowSize | |
| 379 stream.readUnsigned(); // cid | |
| 380 var succAddr = stream.readUnsigned(); | |
| 381 while (succAddr != 0) { | |
| 382 var succId = addrToId[succAddr]; | |
| 383 if (succId != null) { | |
| 384 var predIndex = nextPreds[succId]++; | |
| 385 preds[predIndex] = i; | |
| 386 } | |
| 387 succAddr = stream.readUnsigned(); | |
| 388 } | |
| 389 } | |
| 390 | |
| 391 _firstPreds = firstPreds; | |
| 392 _preds = preds; | |
| 393 } | |
| 394 | |
| 395 // "A Simple, Fast Dominance Algorithm" | |
| 396 // Keith D. Cooper, Timothy J. Harvey, and Ken Kennedy | |
| 397 void _buildDominators() { | |
| 398 var N = _N; | |
| 399 var E = _E; | |
| 400 var addrToId = _addrToId; | |
| 401 var postOrder = _postOrder; | |
| 402 var postOrderIndex = _postOrderIndex; | |
| 403 var firstPreds = _firstPreds; | |
| 404 var preds = _preds; | |
| 405 | |
| 406 var root = 1; | |
| 407 var rootPostOrderIndex = postOrderIndex[root]; | |
| 408 var domByPOI = new Uint32List(N + 1); | |
| 409 | |
| 410 domByPOI[rootPostOrderIndex] = rootPostOrderIndex; | |
| 411 | |
| 412 var round = 0; | |
| 413 var changed = true; | |
| 414 while (changed) { | |
| 415 changed = false; | |
| 416 print("round $round"); | |
|
koda
2015/05/19 20:12:35
Remove or change to debug Log with default off.
rmacnak
2015/05/19 22:16:08
Done.
| |
| 417 round++; | |
| 418 | |
| 419 // Visit the nodes, except the root, in reverse post order (top down). | |
| 420 for (var curPostOrderIndex = rootPostOrderIndex - 1; | |
| 421 curPostOrderIndex > 1; | |
| 422 --curPostOrderIndex) { | |
| 423 if (domByPOI[curPostOrderIndex] == rootPostOrderIndex) | |
| 424 continue; | |
| 425 | |
| 426 var nodeOrdinal = postOrder[curPostOrderIndex]; | |
| 427 var newDomIndex = 0; // 0 = undefined | |
| 428 | |
| 429 // Intersect the DOM sets of the node's precedessors. | |
| 430 var beginPredIndex = firstPreds[nodeOrdinal]; | |
| 431 var endPredIndex = firstPreds[nodeOrdinal + 1]; | |
| 432 for (var predIndex = beginPredIndex; | |
| 433 predIndex < endPredIndex; | |
| 434 predIndex++) { | |
| 435 var predOrdinal = preds[predIndex]; | |
| 436 var predPostOrderIndex = postOrderIndex[predOrdinal]; | |
| 437 if (domByPOI[predPostOrderIndex] != 0) { | |
| 438 if (newDomIndex == 0) { | |
| 439 newDomIndex = predPostOrderIndex; | |
| 440 } else { | |
| 441 // Note this two finger algorithm to find the DOM intersection | |
| 442 // relies on comparing nodes by their post order index. | |
| 443 while (predPostOrderIndex != newDomIndex) { | |
| 444 while(predPostOrderIndex < newDomIndex) | |
| 445 predPostOrderIndex = domByPOI[predPostOrderIndex]; | |
| 446 while (newDomIndex < predPostOrderIndex) | |
| 447 newDomIndex = domByPOI[newDomIndex]; | |
| 448 } | |
| 449 } | |
| 450 if (newDomIndex == rootPostOrderIndex) { | |
| 451 break; | |
| 452 } | |
| 453 } | |
| 454 } | |
| 455 if (newDomIndex != 0 && domByPOI[curPostOrderIndex] != newDomIndex) { | |
| 456 domByPOI[curPostOrderIndex] = newDomIndex; | |
| 457 changed = true; | |
| 458 } | |
| 459 } | |
| 460 } | |
| 461 | |
| 462 // Reindex doms by id instead of post order index so we can throw away | |
| 463 // the post order arrays. | |
| 464 var domById = new Uint32List(N + 1); | |
| 465 for (var id = 1; id <= N; id++) { | |
| 466 domById[id] = postOrder[domByPOI[postOrderIndex[id]]]; | |
| 467 } | |
| 468 | |
| 469 domById[root] = 0; | |
| 470 | |
| 471 _doms = domById; | |
| 472 } | |
| 473 | |
| 474 void _calculateRetainedSizes() { | |
| 475 final N = _N; | |
| 476 final E = _E; | |
| 477 | |
| 478 var size = 0; | |
| 479 var positions = _positions; | |
| 480 var postOrder = _postOrder; | |
| 481 var doms = _doms; | |
| 482 var retainedSizes = new Uint32List(N + 1); | |
| 483 | |
| 484 // Start with retained size as shallow size. | |
| 485 var reader = new _ReadStream(_chunks); | |
| 486 for (var i = 1; i <= N; i++) { | |
| 487 reader.position = positions[i]; | |
| 488 reader.readUnsigned(); // addr | |
| 489 var shallowSize = reader.readUnsigned(); | |
| 490 retainedSizes[i] = shallowSize; | |
| 491 size += shallowSize; | |
| 492 } | |
| 493 | |
| 494 // In post order (bottom up), add retained size to dominator's retained | |
| 495 // size, skipping root. | |
| 496 for (var o = 0; o < (N - 1); o++) { | |
| 497 var i = postOrder[o]; | |
| 498 assert(i != 1); | |
| 499 retainedSizes[doms[i]] += retainedSizes[i]; | |
| 500 } | |
| 501 | |
| 502 _retainedSizes = retainedSizes; | |
| 503 _size = size; | |
| 504 } | |
| 505 } | |
| OLD | NEW |