Files
sdk/runtime
Jens Johansen 06a785bf1e Speedup calculation of toplist by retained memory in Observatory
When clicking the "toplist by retained memory" button in Observatory
it always asks to find 10 objects of a specific size.
The previous implementation did this by taking all objects (millions if
using a lot of memory), sorting it (O(n log n)), filtering it (O(n)),
and ".take"ing the 10 we wanted. Total runtime should be O(n log n)
where n is the number of objects in the heap.

This CL optimizes this for the (to my knowledge) only case there exists,
and instead, while iterationg over all elements (O(n)) keeps a sorted
working list of the top m objects (this is 10 for all intents and purpose
as far as I can see; I've if-cased it to be at most 20). Total runtime
should be O(n log m) where n is the number of objects in the heap and m
is the requested number, which - as said - is at most 20, i.e. a constant,
maying that the runtime is more like O(n).

Instrumenting the code slightly to measure how long it takes:
```
diff --git a/runtime/observatory/lib/src/repositories/top_retaining_instances.dart b/runtime/observatory/lib/src/repositories/top_retaining_instances.dart
index 400ac1427ed..b7e5c429fac 100644
--- a/runtime/observatory/lib/src/repositories/top_retaining_instances.dart
+++ b/runtime/observatory/lib/src/repositories/top_retaining_instances.dart
@@ -7,6 +7,8 @@ part of repositories;
 class TopRetainingInstancesRepository
     implements M.TopRetainingInstancesRepository {
   Future<Iterable<M.RetainingObject>> get(M.IsolateRef i, M.ClassRef c) async {
+    Stopwatch stopwatch = new Stopwatch()..start();
+    try {
       S.Isolate isolate = i as S.Isolate;
       S.Class cls = c as S.Class;
       assert(isolate != null);
@@ -18,5 +20,9 @@ class TopRetainingInstancesRepository
       return (await Future.wait(
               snapshot.getMostRetained(isolate, classId: cls.vmCid, limit: 10)))
           .map((object) => new S.RetainingObject(object));
+    } finally {
+      int ms = stopwatch.elapsedMilliseconds;
+      print("TopRetainingInstancesRepository took $ms ms.");
+    }
   }
 }
```

I ran this program:
```
import "dart:isolate";

List<Object> data = [];

main() {
  RawReceivePort preventClose = new RawReceivePort();
  for(int i = 0; i < 10000000; i++) {
    data.add(new List<int>()..add(i));
  }
  for(int i = 0; i < 100000; i++) {
    data.add(new Foo(i));
  }
}

class Foo {
  final int i;
  Foo(this.i);
}
```

and requested the toplist by retained memory for class `Foo`.
What before took 121778 ms now takes 81026 ms.

(For smaller numbers in the same test it went from ~10 seconds to ~8 seconds).

Change-Id: I6aee4b0c91af8a9ade116b79e56d1e6cbbee5d09
Reviewed-on: https://dart-review.googlesource.com/70502
Reviewed-by: Ryan Macnak <rmacnak@google.com>
Commit-Queue: Jens Johansen <jensj@google.com>
2018-08-20 07:07:15 +00:00
..