02.16.07

Initial Impressions of D

Posted in boggle, programming at 12:07 am by danvk

I got curious about the programming language D after noticing it outperform C++ in the somewhat-silly Computer Language Shootout and reading Steve Yegge’s thoughts on it. I’ve played around it for the past few days. Here’s some thoughts.

First of all, D is what you might call a “low PageRank language.” If you search for ‘D’, there’s nothing related to the language until result #15. The next result isn’t until #160, and it’s pretty random. Contrast that with C. This makes searching for help on D almost impossible. Try “D slow file I/O” for a taste. This isn’t necessarily Google’s fault, either. There’s just not that many good D resources online yet.

So why’d I search for that? Here’s a program in C++:

// ... headers ...
int main(int argc, char** argv) {
  ifstream f(argv[1]);
  string word;
  vector<string> words;
  while (f >> word) {
    words.push_back(word);
  }
  cout << "Words: " << words.size() << endl;
  return 0;
}

It loads the 173,528-word ENABLE2K word list in 0.277 seconds. Here’s the same program in D:

import std.stdio;
import std.stream;

int main(char[][] args) {
  auto f = new File(args[1]);
  char[][] words;
  while (!f.eof()) {
    char[] word;
    word = f.readLine(word);
    words ~= word;
  }
  writefln("Words: ", words.length);
  return 0;
}

This takes 7.590 seconds to run. Why is file I/O in D so slow? Turns out that we need to turn on buffered I/O, but it took stumbling on this forum post to find that out. Replace “File” with “BufferedFile” in that code and the runtime drops to 0.338 seconds.

Even the official documentation is pretty bad. Here’s a real gem: std.c.time. Even better is what I found when I tried to time a section of code. I was happy to find this tutorial about HighPerformanceCounter in the std.perf library. Of course, the official documentation doesn’t even acknowledge the existence of std.perf, but that’s OK. I couldn’t get the example to compile, though. After some more scrounging, I discovered that this was a Windows-only class. What I wanted on my Mac was PerformanceCounter, but I had to manually peruse the /usr/include/d/4.0.1/std/perf.d file to find that out. What gives?

I’ll give you one guess what my standard test program for new languages is. (hint) This was surprisingly easy once I got the hang of things. I’m a big fan of the debug keyword. If you write something like

debug(2) writefln("Dan was here");

Then the line will execute only if you compile with --fdebug=2 or higher. This is a nice way to avoid commenting out debug statements. When you compile with --frelease, the statement disappears entirely. No runtime cost.

Some other things I found interesting:

  • A real module system. No more .h header and .cc implementation silliness.
  • There’s a unittest keyword. When you write code in a unittest block, it automatically runs before your code when you pass the -funittest flag to the gdc. Very cool!
  • The writefln function is way smarter than printf. You can do things like writefln("x: ", x) and avoid having to guess whether x needs a “%d” or a “%lu”. You can also pass just about anything into a “%s” argument and expect a reasonable conversion to a string.
  • The private keyword makes variables module-private, not class-private like in C++. Every class in the same module is effectively your friend. Not sure what I think of this.
  • Arrays are finally declared like this: char[] string.
  • The auto keyword does type inference, just like it will in C++0x.
  • The ~ operator is cool. It appends to arrays. It's going to be hard for me to get used to ~= not meaning "apply regex" like it does in Perl.
  • Array accesses are bounds-checked in debug binaries. This is fantastic, since it means you'll get meaningful errors in more meaningful locations.
  • There's no -> arrow operator. You just use . instead. This has taken a lot of getting used to.

Anyways, here's the perf for my boggle program:

$ gdc -O3 -fno-bounds-check -finline-functions -frelease -Wall -o test trie.d btest.d boggler.d
$ ./test
Loaded 172203 words
Loaded 50000 test boards
Elapsed time: 4666252 us
Perf: 10715.2 bds/sec
Avg. Score: 141.173 pts/bd

That's pretty damn good! Slightly better than the best C++ program I've published on this site, though not better than the best C++ Boggler I've ever written. It's interesting what a huge difference the compiler flags make. If you drop the "-frelease" flag, perf drops to 7500 bds/sec. Without the "-fno-bounds-check" flag, we're down to 6200 bds/sec. That makes reviewing the terse GDC manual essential.

So, conclusions. D is a very young language with lots of promise. I'd hesitate to call it the "next big thing," though. While it's leaps and bounds higher level than C, I've found that the expressiveness is about on par with code written with the C++ STL. The main differences are the module system and the presence of garbage collection, important features that didn't really come out in re-implementing Boggle. My biggest complaint is the lack of documentation. There's no English books on D, it's practically unsearchable, and the official documentation is pathetic. Digital Mars would do their language a great service by writing some tutorials and a more complete reference.

3 Comments

  1. Andrew Briscoe said,

    July 18, 2007 at 3:22 am

    Well actually steve was talking about javascript 2

  2. leonardo said,

    August 23, 2009 at 3:35 am

    Where can I find the D code? I may be able to tune it a little.
    I also suggest you to try the LDC D compiler, if you want to try it I can give you compilation flags, that are tricky.

  3. leonardo said,

    August 23, 2009 at 11:03 am

    My D version:
    http://codepad.org/uubWD3zF

    In D array appending is slow, so if you need to append a lot you can use an ArrayBuilder struct or similar.