Sep152008

Io, where I think I want to be today

Filed under: io 

So I think I've settled on a replacement for Python: Io. I find several things highly appealing about it:

  1. expression-based
  2. almost no syntax
  3. actor-based concurrency
  4. prototype-based object model
  5. lazy evaluation

When I say Io has almost no syntax, I mean it. Here's the complete BNF that defines Io's grammar:

exp        ::= { message | terminator }
message    ::= symbol [arguments]
arguments  ::= "(" [exp [ { "," exp } ]] ")"
symbol     ::= identifier | number | string
terminator ::= "\n" | ";"

Contrast this with (for example) the BNF defining Python's grammar or Ruby's (Perl apparently cannot even be described in a BNF). Having a complicated grammar isn't necessarily a bad thing, but I find smaller grammars and less syntax highly appealing (especially when they actually result in a more expressive language).

Io's guiding design principle is simplicity and power through conceptual unification:

blocks with assignable scope => functions, methods, closures
prototypes => objects, classes, namespaces, locals
messages => operators, calls, assignment, variable accesses

Io still lacks in many areas outside the language itself. The documentation leaves a lot to be desired, libraries are few, the community is small and as such it's a long way away from being a productive language. But, these are things that can be addressed and fixed by people outside the core devs, which I find promising.



3 comments Leave a comment


Oct32008

Io for .NET

Filed under: io 

I'm encouraged to see an alternate implementation of Io for .NET



1 comments Leave a comment


Oct62008

Serving simple dynamic content with Io

Filed under: io breve 

Io happens to include a nice little HTTP server. The HTTP protocol parsing is handled by Zed Shaw's Ragel-generated C parser (same as Mongrel uses), but the rest is in Io. On top of that, I've used an improved version of the Breve-like DSL I outlined a couple of days ago for generating dynamic content (which I am dubbing Iota for the time being).

Io is not a particularly fast language, but it excels at concurrency, as I'll demonstrate here. First, the complete code:

Iota := Object clone do (
    stack := nil
    output := nil

    init := method (
        stack = List clone
        output = List clone
    )

    squareBrackets := method (
        for ( arg, 0, call argCount - 1,
            output append ( call evalArgAt ( arg ) )
        )
        output append ( stack pop )
        ""
    )

    forward := method (
        name := call message name
        attrs := call message arguments map ( arguments ) map ( pair,
            k := pair at ( 0 ) asString slice ( 1, -1 )
            v := pair at ( 1 )
            " " .. k .. "=" .. v
        ) join

        if ( call message next ?name == "squareBrackets",
            output append ( "<" .. name .. attrs .. ">" )
            stack push ( "</" .. name .. ">" )
            self

        , // else
            output append ( "<" .. name .. attrs .. " />" )
            ""
        )
    )
)

server := HTTPServer do (
    setPort ( 5000 )
    setHost ( "localhost" )

    streamResponse := method ( socket, request,
        HTTPResponse withSocket ( socket ) setBody (
            createBody
        ) send
    )

    createBody := method (
        Iota clone do (
            html [
                head [
                    title [ "the title" ]
                ]

                body [
                    div ( class="foo", id="bar" ) [
                        "hello, world" , br
                        "this is a test"
                    ]
                ]
            ]
        ) output join
    )
)

server start

First I'll make clear I'm benchmarking on my laptop (1.5GHz Core2 Duo, 2.5GB of RAM, Fedora 9, 32 bit) which isn't the fastest thing around. Also ab is running on the same system, so we have a fast pipe but some of the CPU is going to ab. Not an ideal setup for benchmarking, but adequate for my purposes.

Now, the above code isn't particularly fast (the HTTPServer on its own gets around 339req/s serving a the same amount of static HTML). However, the requests per second barely dip as the concurrency increases (ab output is trimmed):

$ ab -c 10 -n 10000 http://localhost:5000/

Document Length:        149 bytes
Concurrency Level:      10
Time taken for tests:   49.915 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Non-2xx responses:      10000
Total transferred:      1870000 bytes
HTML transferred:       1490000 bytes
Requests per second:    200.34 [#/sec] (mean)
Time per request:       49.915 [ms] (mean)
Time per request:       4.992 [ms] (mean, across all concurrent requests)
Transfer rate:          36.59 [Kbytes/sec] received

So we're seeing around 200req/s at a concurrency level of 10.

Now let's push it:

$ ab -c 1000 -n 10000 http://localhost:5000/

Document Length:        149 bytes
Concurrency Level:      1000
Time taken for tests:   63.247 seconds
Complete requests:      10000
Failed requests:        0
Write errors:           0
Non-2xx responses:      10000
Total transferred:      1870000 bytes
HTML transferred:       1490000 bytes
Requests per second:    158.11 [#/sec] (mean)
Time per request:       6324.657 [ms] (mean)
Time per request:       6.325 [ms] (mean, across all concurrent requests)
Transfer rate:          28.87 [Kbytes/sec] received

At 1000 concurrent connections, we drop to 158req/s. That's a 100-fold increase in concurrency with only a 21% drop in performance. Not too shabby.

Next up I'll be visiting Io's PostgreSQL library so we can verify that we have the tools for a full web framework stack.



9 comments Leave a comment


Oct192008

Komodo Edit

Filed under: komodo io 

I've know about Komodo IDE for a long time, but I only recently discovered Komodo Edit which is a free (as in beer) programmer's editor. I also just discovered that there's an Io language plugin for it (which is actually how I stumbed upon Edit).

I'm not a big IDE fan, but I've been wanting something a bit more modern than my usual editor Emacs for a while. Komodo Edit provides that plus features such as remote opening of files over SFTP.



5 comments Leave a comment


Oct42008

Small Breve-a-like in Io

Filed under: io breve 

[Update: made a few improvements to the code]

I'm trying to understand the mechanisms for creating internal DSLs (embedded languages) in Io. Here's my first attempt at a Breve-like template language:

Tag := Object clone do (
    output := nil
    stack := nil

    init := method (
        output = List clone
        stack = List clone
    )

    squareBrackets := method (
        attrs := stack pop map ( pair,
            k := pair at ( 0 ) asString slice ( 1, -1 )
            v := pair at ( 1 )
            "#{k}=#{v}" interpolate
        ) join ( " " )
        name := stack pop

        output append ( "<#{name} #{attrs}>" interpolate )
        output append (
            call evalArgs select (
                result, result isKindOf ( List ) not
            ) join ( "\n" )
        )
        output append ( "</#{name}>" interpolate )
    )

    forward := method (
        stack push ( call message name )
        stack push ( call message arguments map ( v, v arguments ) )
        self
    )
)

Tag clone do (

    html [
        head [
            title [ "the title" ]
        ]

        body [
            div ( class="foo", id="bar" ) [
                "hello, world",
                "this is a test"
            ]
        ]
    ]

) output join println

Note that it's not very Breve-like except in general appearance (it's not a directed graph of objects that can be traversed). It's also not feature-complete (although many Breve features would be redundant in Io). Still, it's a nice proof-of-concept and not bad for a couple hours of floundering around by a complete Io novice.

In any case, Breve's syntax was largely bound by Python's and I'm thinking that I'll be ditching this syntax in favor of something even better.



0 comments Leave a comment


Oct82008

Running multiple Io VM's under OpenMP

Filed under: io openmp 

Io's concurrency is implemented as async cooperative coroutines. This gives great performance, but doesn't take advantage of multiple cores. Luckily the Io VM is threadsafe, so it shouldn't be too hard to write a program that can run multiple VM's, each in its own thread.

I decided to try OpenMP to implement this as it provides a nice way to do this sort of thing.

This example doesn't quite work just yet. It does sometimes, and other times it segfaults. I'm not sure if the problem is my code, Io, or OpenMP at this point (although I'm suspecting OpenMP due to some messages I've seen in forums).

[UPDATE: It turns out it was a static variable introduced into Io's coroutine library. It's fixed in git - thanks Steve!]

In any case, I'm posting it here in the hopes that it might inspire someone or maybe solicit some assistance.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <omp.h>
#include "io/IoState.h"

void usage ( char *program ) {
  fprintf ( stderr, "Usage: %s [-n threads] <script>\n", program );
  exit ( EXIT_FAILURE );
}

void do_io_state ( char *scriptname ) {
#define CMD "doFile(\"%s\")"
  char *cmd;
  IoState *self;

  cmd = (char *) alloca ( strlen ( scriptname ) + strlen ( CMD ) - 1 );
  sprintf ( cmd, CMD, scriptname );

  self = IoState_new ( );
  IoState_init ( self );
  IoState_doCString_ ( self, cmd );
  IoState_free ( self );
}

int main ( int argc, char **argv ) {
  int threadcount = 1;
  char *scriptname = NULL;
  int opt;

  while ( ( opt = getopt ( argc, argv, "n:" ) ) != -1 ) {
    switch ( opt ) {
    case 'n':
       threadcount = atoi ( optarg );
       break;
    default:
      usage ( argv [ 0 ] );
    }
  }

  if ( ! ( scriptname = argv [ optind ] ) )
    usage ( argv [ 0 ] );

  omp_set_num_threads ( threadcount );

#pragma omp parallel
  do_io_state ( scriptname );

  return 0;
}

To compile it, run this:

$ gcc -fopenmp test.c -o test -I/usr/local/include/io -lgomp -liovmall

You'll need to have the Io source headers in /usr/local/include/io (you have to copy them by hand from the source tree).

Then you can run it like so:

$ ./test -n 4 test.io

where test.io has a really useful bit of code such as:

"hello, world" println

You should then get the amazing output:

hello, world
hello, world
hello, world
hello, world

I haven't programmed in C for many years, so don't be too disappointed =)



0 comments Leave a comment


Oct102008

Update to Io HttpServer

Filed under: io httpserver 

I upgraded to the bleeding edge from git of Io, and plenty of stuff has changed, notably the Volcano addon, which is the HTTP server I used in my previous post. It's changed enough that the old code won't work under the latest version, so here's an update.

server := HttpServer clone do (
    setPort ( 5000 )
    setHost ( "localhost" )

    renderResponse := method ( request, response,
        response body = (
            """<html><body>hello, world</body></html>"""
        )
    )
)

server start

It's left as an exercise for the reader to update the above to use something besides static HTML (such as Iota).

As a side-note, slice has been deprecated in the latest version of Io and Volcano needs a small fix (just change all occurrences of "slice" with "exclusiveSlice" in addons/Volcano/io/StatusCodes - there's two of them on the very last line).

[UPDATE: this is fixed in latest git ]



0 comments Leave a comment


Oct102008

Welcome your new Io underling

Filed under: io 

I've just committed my first changes to Io. Nothing spectacular but it's nice to be on board.



0 comments Leave a comment


Dec292008

Multiple Io VM's under OpenMP revisited

Filed under: io openmp 

It came up the other day on the Io mailing list that managing multiple Io VM's (each with an HTTP server) was a bit too complicated. Since I'd done a little testing in this area, I decided to see what I could come up with.

First off, we need a bootstrap to launch our VM's. Again I'm utilizing OpenMP to this end:

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <string.h>
#include <getopt.h>
#include <omp.h>
#include "IoState.h"

void usage ( char *program ) {
  fprintf ( stderr, "Usage: %s [-n threads] <script>\n", program );
  exit ( EXIT_FAILURE );
}

char *mmap_src ( char *script ) {
  int src;
  struct stat finfo;
  char *buffer;

  src = open ( script, O_RDONLY );
  fstat ( src, &finfo );
  buffer = mmap ( 0, finfo.st_size, PROT_READ, MAP_SHARED, src, 0 );
  close ( src );

  return buffer;
}

void spawn_vm ( char *buffer ) {
  IoState *self;

  self = IoState_new ( );
  IoState_init ( self );
  IoState_doCString_ ( self, buffer );
  IoState_free ( self );
}

int main ( int argc, char **argv ) {
  int threadcount = 0;
  char *script = NULL, *script_src;
  int opt;

  while ( ( opt = getopt ( argc, argv, "n:" ) ) != -1 ) {
    switch ( opt ) {
    case 'n':
      threadcount = atoi ( optarg );
      break;
    default:
      usage ( argv [ 0 ] );
    }
  }

  if ( ! ( script = argv [ optind ] ) )
    usage ( argv [ 0 ] );

  script_src = mmap_src ( script );

  if ( threadcount < 1 )
    threadcount = omp_get_num_procs ( );
  threadcount++;
  omp_set_num_threads ( threadcount );

#pragma omp parallel
  {
#pragma omp single nowait
    spawn_vm ( mmap_src ( "do_server.io" ) );

    spawn_vm ( script_src );
  }

  return 0;
}

This is called mio.c and is compiled with:

gcc -fopenmp -Wall -I/usr/local/include/io \
      -L/usr/local/lib/io -liovmall -lgomp -o mio mio.c

Next, we need a DistributedObjects server so we can centralize our configuration (and maybe use it for other stuff later on). We'll name this file do_server.io:

Shared := Object clone do (
    vars := Map clone
    ip_addr := "127.0.0.1"
    ip_port := 5000

    put := method ( identifier, value,
        vars atPut ( identifier, value )
        return value
    )

    get := method ( identifier,
        return vars at ( identifier )
    )

    addr := method ( return ip_addr )

    port := method (
        ip_port = ip_port + 1
        return ip_port
    )
)

doServer := DOServer clone do (
    setRootObject ( Shared clone )
    setPort ( 8456 )
    writeln ( "Starting DistributedObject Server at 8456" )
)

doServer start

And finally, our HTTP servers:

loop ( // until we have a connection to the DOServer
    System sleep ( 0.5 )
    shared := nil
    do_conn := DOConnection clone setHost ( "127.0.0.1" ) setPort ( 8456 ) connect
    e := try (
        shared = do_conn serverObject
    )
    if ( shared type == "DOProxy", break )
)

server := HttpServer clone do (
    addr := shared addr
    port := shared port
    setHost ( addr )
    setPort ( port )

    writeln ( "Starting HttpServer at #{addr}:#{port}" interpolate )

    renderResponse := method ( request, response,
        response body = (
            "<html><body>hello, world from #{addr}:#{port}</body></html>" interpolate
        )
    )
)

server start

We'll name this file runserver.io.

Now we can run our servers with something like:

./mio -n 4 runserver.io

Of course, it's not much use to run four separate servers on four separate ports, so we need a proxy. As usual, I'll utilize Nginx, which is extremely fast and lightweight and has built-in load-balancing features:

upstream io_server {
    server 127.0.0.1:5001;
    server 127.0.0.1:5002;
    server 127.0.0.1:5003;
    server 127.0.0.1:5004;
}

server {
    listen       80;
    server_name  _;

    location / {
        proxy_pass http://io_server;
    }
}

(obviously this config is just showing the relevant bits to our application).



0 comments Leave a comment




Copyright © 2007, Cliff Wells