[racket] Is racket suitable for such a project?

From: Yuhao Dong (ithisa at outlook.com)
Date: Mon Feb 10 18:49:59 EST 2014


I'm trying to decide between Racket and Go on writing my onion-routing
system inspired by Tor. Basically, a network server, involving lots of
long-lived connections that often pass large amounts of data. The thing
needs to be super scalable; I often find that these servers, although
network servers, often become CPU-bound doing encryption and
encapsulation of protocols, so I do have experience that this is not
"premature optimization"!

Go seems to be the go-to (pun intended) for scalable network things. It
has super cheap threads, is statically compiled (easy to deploy to lots
of machines), and apparently uses little memory and runs fast. However,
after writing some low-level transports in Go, I found a few things I
gripe about but seem to be unsolvable:

 - Go doesn't have exceptions. This means checking errors over and over.
 - Go uses lightweight user threads like Racket ("goroutines"), but
isn't fully non-blocking under the hood for I/O. Thus some I/O
operations actually spawn a kernel thread, and then kill it when the
operation is done. This is unacceptable for my use case. 
 - Go isn't as fast as I imagined. My program is still CPU-bound at
around 40 MB/s, and that's just counting pushing zeroes through the
lowest-level transport protocol.
 - Go's garbage collector is naive stop-the-world mark-and-sweep. My
program keeps a large amount of state, and constantly pushes data and
accepts connections. The mark-and-sweep process can take as much as 20
seconds on large heaps, severly impacting user experience. Since the GC
is nonmoving, heap fragmentation is also an issue. I searched around a
bit, and it seems that "buffer reuse" is a thing in Go. Ugh.

So I decided to take Racket, namely Typed Racket, a spin. I like it a
lot, it's my favorite language after all, and especially I like the fact
that I can use macros to simplify verbose patterns of code. DrRacket's
REPL is also very helpful. I also find that Racket is much more
performant than I imagined (I used to think of it as a Python-type slow
scripting language). However, there are a few gripes I have, but seeing
how flexible Racket is, I think they might be solvable:

 - Racket threads don't seem to be able to utilize multiple CPU cores.
Is there an idiomatic way to, say, form a pool of places and push
threads to them? I imagine sharing data is very messy?
 - The Racket VM is huuuuuuge, both on disk and in memory.
Infuriatingly, using #lang racket seems to import every single #lang
racket library into the memory space, and when I raco dist my program,
every single library gets packaged. I know this sounds like whining, but
why can't some sort of analysis phase be applied where only the
libraries actually required for the program to run are loaded?
 - Racket doesn't seem to be able to call raw C code or machine code in
static libraries, instead requiring the code be compiled into a library.
Is this related to the fact that Racket is run in a VM rather than
compiled to machine code?
 - Some things in racket are pathologically slow. As an example, try
implementing a cipher with loops and array indices and bytestrings. It
will end up orders of magnitude slower than, say, C or Go or Java, or
sometimes even Python.

Are there solutions to these problems? These aren't showstoppers by any
means, but could finally end my endless dilemma between the two langs :)

Yuhao Dong
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 213 bytes
Desc: This is a digitally signed message part
URL: <http://lists.racket-lang.org/users/archive/attachments/20140210/0771bd15/attachment.sig>

Posted on the users mailing list.