Wednesday, July 16, 2014

Fun with Dart Iterable


I am going to take a brief break from my Visitor Pattern benchmarks for Design Patterns in Dart, but not really. Instead of working with the actual numbers, I want to write a simple Dart script that reads a CSV file of runs totals so that they can be grouped and transposed.

In other words, I want to have a little Iterable fun.

After 5 runs of my Visitor Pattern implementation benchmarks, I have 15 rows of results. There are three implementations being benchmarked. For each of the three implementations, I try executing them inside loops of sizes increasing by a power of ten from 10 to 100,000. The results look like:
Classic Visitor Pattern, 1224, 10, 122.4
Nodes iterate w/ single dispatch, 1106, 10, 110.6
Visitor Traverses, 1175, 10, 117.5
Classic Visitor Pattern, 1.185e+4, 100, 118.5
Nodes iterate w/ single dispatch, 1.124e+4, 100, 112.4
Visitor Traverses, 1.176e+4, 100, 117.6
Classic Visitor Pattern, 1.204e+5, 1000, 120.4
Nodes iterate w/ single dispatch, 1.124e+5, 1000, 112.4
Visitor Traverses, 1.168e+5, 1000, 116.8
Classic Visitor Pattern, 1.486e+6, 10000, 148.6
Nodes iterate w/ single dispatch, 1.482e+6, 10000, 148.2
Visitor Traverses, 1.179e+6, 10000, 117.9
Classic Visitor Pattern, 1.481e+7, 100000, 148.1
Nodes iterate w/ single dispatch, 1.487e+7, 100000, 148.7
Visitor Traverses, 1.185e+7, 100000, 118.5
To make pretty graphs from those numbers, I would like to group the results by loop size (the 3rd column in the CSV) and include the results of each implementation (Classic, Single Dispatch, Visitor Traverses). So it ought to look like:
10,     122.4, 110.6, 117.5
100,    118.5, 112.4, 117.6
1000,   120.4, 112.4, 116.8
10000,  148.6, 148.2, 117.9
100000, 148.1, 148.7, 118.5
I create the script and make it executable:
$ touch tool/group_totals.dart 
$ chmod 755 tool/group_totals.dart
I read the totals with a little synchronous help (I just prefer synchronous reads for small stuff) from dart:io:
_readTotals() {
  var file = new File('totals.csv');
  var lines = file.readAsLinesSync();

  return lines.map((line){
    var fields = line.split(', ');
    return {
      'name': fields[0],
      'score': fields[1],
      'loopSize': fields[2],
      'averageScore': fields[3]
    };
  }).toList();
}
I have to toList() on the result of the map()—otherwise I would return an Iterable.

The problem at this point is that Dart lacks a groupBy() method on Lists and Iterables. There seems to be a groupBy package available, but I am curious how I might implement this on my own. And the best that I can come up with is to find all values for loopSize and convert them to a Set (to eliminate duplicates):
main() {
  List<Map> totals = _readTotals();
  var loopSizes = totals.map((r)=> r['loopSize']).toSet();

  loopSizes.forEach((loopSize) {
    // Print results for each loop size
  }
}
That is already kind of ugly, but lacking better ideas, I forge ahead. I find all records where the loop size is the same as the current value:
  loopSizes.forEach((loopSize) {
    var records = totals.where((r)=> r['loopSize'] == loopSize);
    // ...
  }
From that list of records, I then extract the individual records that I seek (Classic Visitor Pattern, Single Dispatch Optimized, and Visitor Traverses the Node Structure):
  loopSizes.forEach((loopSize) {
    var records = totals.where((r)=> r['loopSize'] == loopSize);

    var classic = records.firstWhere((r)=> r['name'].contains('Classic'));
    var single = records.firstWhere((r)=> r['name'].contains('single'));
    var vTraverses = records.firstWhere((r)=> r['name'].contains('Traverses'));

    print(
      '${loopSize}, '
      '${classic["averageScore"]}, '
      '${single["averageScore"]}, '
      '${vTraverses["averageScore"]}'
    );
  });
And then I print the results (adjacent strings in Dart are concatenated). The prettiness of the code has not improved much, but it works:
$ ./tool/group_totals.dart
10, 122.4, 110.6, 117.5
100, 118.5, 112.4, 117.6
1000, 120.4, 112.4, 116.8
10000, 148.6, 148.2, 117.9
100000, 148.1, 148.7, 118.5
And, pasted into a spreadsheet, it is easy to obtain pretty graphs:



Still, iterables in Dart would be a lot more fun with a group-by method.

The code is located in the book's public code repository. Tonight's script was group_totals.dart (81d0ed10ff).


Day #124

1 comment: