Saturday, January 7, 2012

Classes, Names and Stopwatches in Dart

‹prev | My Chain | next›

I wrote some fairly ridiculous, process heavy Dart code last night. I am pretty sure that it was actually slower than trying to do the same thing in a single process (because of bad code, not a deficiency in Dart). "Pretty sure" is the realm of coding buffoonery. As my friend and mentor Mike Barry is fond of saying, show me the numbers!

Although Dart is still quite new, it does come with a built-in timer—the Stopwatch class. Best of all, it is a very high precision timer, capable of measuring in CPU clock cycles or microseconds. It is also fairly easy to use:
main() {
  final doom_finder = new DoomsDayFinder()

  var timer = new Stopwatch.start();
  doom_finder.spawn().then((port) {
    port.call(2000).receive((message, replyTo) {
      for (var year in message) print(year);
      timer.stop();
    });
  });

  print("Elapsed time: " + timer.elapsedInMs());
}
The start() constructor allows me to instantiate and start the timer in a single line. Then, I just need to stop() the timer when the spawned isolate/process completes—which it does when it replies to the receive() callback.

Of course, that does not work:
➜  command_line git:(master) ✗ dart timer01.dart
Elapsed time: 36
2001
2007
2012
2018
2029
2035
2040
2046
2057
2063
2068
2074
2085
2091
2096
In fact, the elapsed time that I am printing is the the number of milliseconds that the timer runs as the isolates are spawned. I need to print the timing information just after the stopwatch is stopped:
main() {
  final doom_finder = new DoomsDayFinder();

  var timer = new Stopwatch.start();
  doom_finder.spawn().then((port) {
    port.call(2000).receive((message, replyTo) {
      for (var year in message) print(year);
      timer.stop();
      print("Elapsed time: " + timer.elapsedInMs() + "ms");
    });
  });
}
(try.dartlang.org)

With that, I get actual timing information:
➜  command_line git:(master) ✗ dart timer01.dart
2001
2007
2012
2018
2029
2035
2040
2046
2057
2063
2068
2074
2085
2091
2096
Elapsed time: 2797ms
I am slightly put off being required to stop and print the results on two different lines. It would not be so bad if both were not buried deep in the same callback (somehow it was not quite as bad when the print() came afterwards).

Since I have yet to really get the hang of classes in Dart, let's see if I can write one from scratch to time and print the results of things:
class PrettyStopwatch {
  var timer = new Stopwatch();

  start() {
    timer.start();
  }

  stop() {
    timer.stop();
    print("Elapsed time: " + timer.elapsedInMs() + "ms");
  }
}
Instead of extending the behavior of the built-in Stopwatch, my PrettyStopwatch class has a stopwatch. Why not sub-class Stopwatch? Because I hate inheritance. Unless I am sure that I want inheritance, I avoid it.

As written, I must instantiate and start my watch on separate lines:
main() {
  final doom_finder = new DoomsDayFinder();

  var timer = new PrettyStopwatch();
  timer.start();
  doom_finder.spawn().then((port) {
    port.call(2000).receive((message, replyTo) {
      for (var year in message) print(year);
      timer.stop();
    });
  });
}
I will address the separate lines in a bit. First, I have more pressing issues. Specifically, my code does not compile:
➜  command_line git:(master) ✗ dart timer02.dart
'/home/cstrom/repos/dart-site/examples/command_line/timer02.dart': Error: line 57 pos 15: expression must be a compile time constant
  var timer = new Stopwatch();
              ^
Compile time constant? Interesting. I will have to research that particular requirement. For now, it is easy enough to comply by declaring the timer instance variable, but not assigning it until the constructor is invoked:
class PrettyStopwatch {
  Stopwatch timer;

  PrettyStopwatch() {
    timer = new Stopwatch();
  }

  start() {
    timer.start();
  }

  stop() {
    timer.stop();
    print("Elapsed time: " + timer.elapsedInMs() + "ms");
  }
}
With that, I am back in business (code compiles and runs as before). The last thing that I would like to try tonight is making a "named constructor". That is, I hope to be able to instantiate and start my "pretty" stopwatch on a single line. That turns out to be quite easy—I simply define a PrettyStopwatch.start() method in addition to the regular PrettyStopwatch() constructor:
class PrettyStopwatch {
  Stopwatch timer;

  PrettyStopwatch() {
    timer = new Stopwatch();
  }

  PrettyStopwatch.start() {
    timer = new Stopwatch.start();
  }

  start() {
    timer.start();
  }

  stop() {
    timer.stop();
    print("Elapsed time: " + timer.elapsedInMs() + "ms");
  }
}
With that, I am down to two lines of code required to start, stop, and print out the results of my timer:
main() {
  final doom_finder = new DoomsDayFinder();

  var timer = new PrettyStopwatch.start();
  doom_finder.spawn().then((port) {
    port.call(2000).receive((message, replyTo) {
      for (var year in message) print(year);
      timer.stop();
    });
  });
}
(try.dartlang.org)

And it all works.

I was unsure of the named constructors—both how to write them and if I cared for the aesthetics. Writing them turns out to be fairly easy. More importantly, I must confess that I do like using them. They cut down on the parenthesis madness that Javascript chains might require:
var timer = (new PrettyStopwatch()).start();
Named constructors seem an especially big win when there are arguments—those get unreadable quickly in Javascript.

It is a bit ugly to define both a start() named constructor and method. Perhaps that is something that be cleaned up when a method_missing or reflection capability is added to the language.


Day #258

No comments:

Post a Comment