20 April 2006

JavaScript speed test

After fiddling around with several JavaScript performance thingies online, I finally caved in and wrote my own.

14 April 2006

Miscellany

I'm searching for a way to ask the Visual Studio debugger to attach and break as soon as a given process starts. I don't care which version of Visual Studio, or if I have to attach to the parent process first, or how much scripting I have to do, as long as it's possible to get to a point where I can do this conveniently. For the world's most complicated debugger, this seems like a really obvious feature, but I can't find it.

Update: No luck yet, but I'm slightly warmer. VS.NET has macros; the EnvDTE namespace provides a tiny amount of programmatic access to the debugger; VS2005 allows you to set breakpoints in DLLs that are not loaded.

Firefox has a menu option, View → Page Style → No Style, that is perfect for pages like this one.

My side project is going well. Some 300 tests are passing. I need to pause a moment and figure out specifically what to do next. There are a lot of little things I'm eager to work on.

For one thing, it's slow. I've been using the Venkman JavaScript debugger to find the worst bits. It actually has a usable profiler. Fanstastic.

I have never (in maybe 6 tries) succeeded in getting any version of the Visual Studio debugger to work with a symbol server. The error messages are totally useless.

12 April 2006

Generator syntax for state machines

I have a real-world interface that looks like this:

interface ITableScanner {
    void open(Path filename);
    void onRecord(TableRecord record);
    void onEndOfFile(int status, Exception error);
    void close();
}

It's actually a state machine. The methods are only to be called in a very specific order: open(), then zero or more calls to onRecord(), then onEndOfFile(), then close().

Here's the simplest nontrivial implementation of this interface I can come up with:

class DebugTableScanner implements ITableScanner {
    public DebugTableScanner(Writer out) {
        m_out = out;
    }

    public void open(Path filename) {
        m_out.writeln("contents of file " + filename);
    }

    public void onRecord(TableRecord record) {
        if (!isNoise(record))
            m_out.writeln("  - " + record);
    }

    public void onEndOfFile(int status, Exception error) {
        if (status != OK)
            m_out.writeln("  * End of file status: " + status);
        if (error != null)
            error.printStackTrace(m_out);
    }

    public void close() {
    }

    private Writer m_out;
}

I'm going to propose a different syntax for writing that class. Just as you can use generator syntax instead of explicitly writing an Iterator class, you could use this special syntax instead of explicitly implementing any state machine interface.

The special syntax is receive methodName(parameters). It means roughly “yield, then resume here when the appropriate method call is received”. But if this syntax occurs in an if or while test-expression, it also means “skip to the next resume statement if a different method is called instead”. Informal, I know, but I think formal semantics would be easy to hash out.

The above class, implemented using this syntax, looks like this:

ITableScanner debugTableScanner(Writer out) {
    receive open(Path filename) {
        out.writeln("contents of file " + filename);
    }

    while (receive onRecord(TableRecord record)) {
        if (!isNoise(record))
            out.writeln("  - " + record);
    }

    receive onEndOfFile(int status, Exception error) {
        if (status != OK)
            out.writeln("  * End of file status: " + status);
        if (error != null)
            error.printStackTrace(out);
    }

    receive close();
}

You would call debugTableScanner(myOut) instead of new DebugTableScanner(myOut) to create an instance.

Nice things about this:

  • The code appears in the same order that it'll execute at run time.
  • The code and its caller look the same. You could put the code side by side and see the method calls and receive statements line up.
  • No need to explicitly check the object state and throw IllegalStateException (or whatever) if stuff hasn't been called in the right order; the compiler can generate the checks.
  • You could create variables anywhere in the body, and they'll be accessible only to methods called later.
  • Similarly, there's no need to stick the Writer into a member variable. It's lexically scoped. (Yeah, lexical scoping is frequently the highlight of my day...)

Here's a funny real-world example of why my proposed syntax is nice. I had to modify the real-world version of this example to cope with errors. Here's what I ended up with:

class DebugTableScanner implements ITableScanner {
    public DebugTableScanner(Writer out) {
        m_out = out;
    }

    public void open(Path filename) {
        m_out.writeln("contents of file " + filename);
    }

    public void onRecord(TableRecord record) {
        try {
            if (!isNoise(record))
                m_out.writeln("  - " + record);
        } catch (Exception exc) {
            onError(exc);
            throw;
        }
    }

    public void onEndOfFile(int status, Exception error) {
        try {
            if (status != OK)
                m_out.writeln("  * End of file status: " + status);
            if (error != null)
                error.printStackTrace(m_out);
        } catch (Exception exc) {
            onError(exc);
            throw;
        }
    }

    public void close() {
    }

    private void onError(Exception exc) {
        reportError(
            new Exception(INTERNAL_ERROR_IN_SCANNER, exc));
    }

    private Writer m_out;
}

...and actually considerably worse, since I had code in close() which also had to be wrapped. In my proposed syntax, it would look like this, instead:

ITableScanner debugTableScanner(Writer out) {
    receive open(Path filename) {
        out.writeln("contents of file " + filename);
    }

    try {
        while (receive onRecord(TableRecord record)) {
            if (!isNoise(record))
                out.writeln("  - " + record);
        }

        receive onEndOfFile(int status, Exception error) {
            if (status != OK)
                out.writeln("  * End of file status: " + status);
            if (error != null)
                error.printStackTrace(out);
        }

        receive close();
    } catch (Exception exc) {
        reportError(
            new Exception(INTERNAL_ERROR_IN_SCANNER, exc));
        throw;
    }
}

11 April 2006

Worksheet IDEs

I've always liked using worksheet-style programs. Excel is the best-known one, but Mathcad is what I'm really talking about. They're document-oriented, yet interactive. The emphasis is clearly on making something a human can read; but it's also executable.

Hmmm.

I think a worksheet-style programming environment would be interesting. For one thing, it would make it really, really easy to accumulate test cases. I think most developers do informal testing of the variety "I just finished coding X; let's run it and see if it works." If the interactive environment in which you did this kind of testing were a worksheet, you could very easily save the thing and run it again later. It would almost be hard not to build up test cases.

04 April 2006

JavaScript, threading, and continuations

I've got a few bits and pieces of my side project working now, and I ran into something unexpected.

The annoying problem

In a simple GUI app, events (like mouse clicks and keystrokes) are all handled in a single, dedicated thread. If event-handling code takes too long to run, the application becomes unresponsive. It works like this everywhere: Java, Windows, XWin—and client-side JavaScript. But as always, JavaScript is... different:

  1. The whole browser doesn't become unresponsive, just your scripts. This is scripts don't run in the browser's main GUI thread. Rather, the browser gives you a separate thread dedicated to scripting events. But don't think this lets you off the hook, because...

  2. The browser detects unresponsive scripts, and believe me, you don't want this to happen. Firefox, for instance, pops up a warning message and offers the user the option to kill your script. If the user says no, the same warning message pops up again a few seconds later.

  3. The usual solution to this problem won't work in a browser. In any other system, you would just take the long-running code and move it into its own thread. But scripts can't spawn threads. Now what?

The annoying solution

I found a workaround using window.setTimeout(). You have to change your code to do its work in chunks and use setTimeout() as a substitute for preemptive thread-switching. It sounds like green threads. Unfortunately, it's much worse than that. Consider this:

    function hog() {
        // code that does three time-consuming things
        spellCheckWikipedia();
        findPrimesInDigitsOfPi();
        buildConcensusOnUsenet();
    }

In a language with green threads, you'd just call sleep(1) (or whatever) between steps.

In JavaScript, you have to write this:

    function hog_start() {
        setTimeout(hog_step1, 10);
    }

    function hog_step1() {
        spellCheckWikipedia();
        setTimeout(hog_step2, 10);
    }

    function hog_step2() {
        findPrimesInDigitsOfPi();
        setTimeout(hog_step3, 10);
    }

    function hog_step3() {
        buildConcensusOnUsenet();
        // phew-- done.
    }

Ugly. The really annoying case is when individual steps are too time-consuming. Suppose buildConcensusOnUsenet() takes an unbounded amount of time to run. Then the function needs to be modified to be "setTimeout()-aware"—a huge pain.

Funny thing is: this is just continuation-passing style. You use setTimeout to "jump to" a continuation. I thought this was cute, like a pun is cute. I've seen continuations simulated via exceptions, but never via event queues.

I guess theoretically I could source-transform JavaScript code written in the green-threads style into JavaScript written in continuation-passing style. Not sure I want to go that far.

To do

I have to get back to my current side project, about which more in a bit. But a definite possible future side project is:

  • Create a "foundations of math" wiki combining MediaWiki and the Metamath proof-checking engine.