Sunday, January 8, 2012

Libraries in Dart

‹prev | My Chain | next›

I built myself a sweet little Dart code timer class last night. Tonight, I am going to make use of it to see how over-isolating things in Dart affects process speed. But first up, I need to move my class timer out into a library that can be included in subsequent iterations as I try to improve my time.

Reading through the spec, it seems like a Dart library is a simple enough thing. I only need create a separate file with a top level class (or similar declaration) and then #import() it. So, the separate file is:
// pretty_stop_watch.dart
class PrettyStopwatch {
  // last night's code here...
}
Then, in my main dart file:
// timer03.dart
#import('pretty_stop_watch.dart');
However, when I run this, I am greeted with:
➜  command_line git:(master) ✗ dart timer03.dart
'/home/cstrom/repos/dart-site/examples/command_line/timer03.dart': Error: line 1 pos 1: library handler failed: '/home/cstrom/repos/dart-site/examples/command_line/pretty_stop_watch.dart': Error: line 1 pos 1: '#library' expected
class PrettyStopwatch {
^

#import('pretty_stop_watch.dart');
^
OK. It seems that I need a bit more than just a class declaration. Let's try a #library(); declaration at the top of the library file. I am not sure what to put in there, so I try the filename, minus the extension:
#library('pretty_stop_watch');

class PrettyStopwatch {
  // last night's code here...
}
Now, when I try to run the main dart code, I get:
➜  command_line git:(master) ✗ dart timer03.dart
Unable to find entrypoint: static .main()
Ew.

Well, if you want a static main(), I can oblige:
#library('pretty_stop_watch');

main() {}

class PrettyStopwatch {
  // last night's code here...
}
When I run the code now, I get now errors. So that is progress.

To actually use the PrettyStopwatch class, I add the isolate-based doomsday solver into timer03.dart:
// timer03.dart
#import('pretty_stop_watch.dart');

main() {
  // spawn the isolate solver
}

class DoomsDay extends Isolate { /* ... */ }

class DoomsDayFinder extends Isolate { /* ... */ }
Unfortunately, now I get the following error when I try to time with my library:
➜  command_line git:(master) ✗ dart timer03.dart
'/home/cstrom/repos/dart-site/examples/command_line/timer03.dart': Error: line 3 pos 1: 'main' is already defined
main() {
^
Well, of course main is already defined. You insisted that I define it you stupid compiler! Oh wait...

Maybe the compiler was telling me that I needed a main() method in my primary file, not in my library. Duh.

I leave the main timer03.dart script as-is and remove the main() method from pretty_stop_watch.dart:
#library('pretty_stop_watch');

class PrettyStopwatch {
  // last night's code here...
}
And that does it. Now when I run the code, I am returned a list of all the years in the 21st century in which the doomsday is a Wednesday, followed by timing information:
➜  command_line git:(master) ✗ dart timer03.dart
2001
2007
2012
2018
2029
2035
2040
2046
2057
2063
2068
2074
2085
2091
2096
Elapsed time: 2779ms
I have already broken Dart importing in a couple of different ways. I wonder if there are other things I can break. For instance, that string that I supplied to the #library() call—what happens when I change that to something other than the filename?
#library('asdf');

class PrettyStopwatch {
 // ...
}
As it turns out, nothing. The code still runs just fine.

In the end, I opt for a somewhat descriptive title for the library:
#library('A very pretty stop watch class');
// ...
It seems silly to duplicate the classname or the filename here. I was able to find #library() in the spec (scrolling helps), but it offers no suggestions for what to use here.

One other thing that I try is uploading my pretty stop watch library to github in the hopes of importing it over the network. This, however does not work from the command line:
➜  command_line git:(master) ✗ dart timer04.dart
'/home/cstrom/repos/dart-site/examples/command_line/timer04.dart': Error: line 1 pos 1: library handler failed: Unable to open file: /home/cstrom/repos/dart-site/examples/command_line/https://raw.github.com/dart4hipsters/dart4hipsters.github.com/master/examples/lib/pretty_stopwatch.dart
#import('https://raw.github.com/dart4hipsters/dart4hipsters.github.com/master/examples/lib/pretty_stopwatch.dart');
^
Nor does it work on try.dartlang.org. I was none too sure about either (although it would have been cool if it had worked). Hopefully it will work when I try this out in Dartium another day.

Anyhow, I have my very excellent stop watch class factored out into a class library. I also see that my isolate-intensive doomsday solver is taking 2.8 seconds. Let's use my stopwatch class to time a non-isolate solution:
#import('pretty_stopwatch.dart');

main() {
  var century = 2000
    , matching_years = [];

  var timer = new PrettyStopwatch.start();
  for (var i=0; i <100; i++) {
    var year = century + i
      , doomsday = find_doomsday_for(year);

    if (doomsday == 'Wed') matching_years.add(year);
  }
  for (var year in matching_years) print(year);
  timer.stop();
}

find_doomsday_for(year) {
  var march1 = new Date(year, 3, 1, 0, 0, 0, 0)
    , doom = march1.subtract(new Duration(1)).weekday
    , dow = ['Mon', 'Tues', 'Wed', 'Thurs', 'Fri', 'Sat', 'Sun'];

  return dow[doom];
}
(try.dartlang.org with PrettyStopwatch embedded)

The result? A complete denouncement of my isolate approach:
➜  command_line git:(master) ✗ dart timer05.dart
2001
2007
2012
...
2091
2096
Elapsed time: 49ms
Admittedly, 49 milliseconds is slightly better than 2.8 seconds, but this is not an indictment of isolates, just an indictment of the appropriateness of using them to solve a fairly simple problem like this.

This will do for a stopping point today. Up tomorrow: hopefully finding a more appropriate use of Isolates.


Day #259

2 comments:

  1. Great write up. Another thing you can do with a #library() is #source() a dart files to compose the library. http://goo.gl/m3anI If your using the DartEditor it has some bugs with creating libraries. I just close and reopen or wipe out workspace directory.

    ReplyDelete
  2. @Adam Hah! I had no idea that includes were done with #source(). I read through the "Includes" section of the spec a couple of times yesterday but mentally assumed that the directive would be #include(). I couldn't figure out what you could be talking about until I re-read the spec :)

    Whatever it's called, I'll definitely check out includes in the near future. Thanks!

    ReplyDelete