
In my experience, there’s a modest reduction in startup time. Still, the minimum startup time even for a “Hello world” program using #lang racket/base
, compiled with raco exe
, is 110 ms on my system (a five-year-old Core i5).

In comparison, compiled binaries for other languages typically have a startup time of 3 to 5 ms on my system.

I think the reduction that you noticed is not due to compiling to executable, but is due to compiling to “bytecode”

I.e., raco make
would suffice to make you see the speedup.

When I use raco make
, I get between about 100 or 120 ms startup time. I don’t know what causes the bimodal(?) distribution.
Last time I tried actual programs with raco make
vs. raco exe
, there was little difference.

Often, I want to generate standalone executables (with raco exe --orig-exe
), so raco make
doesn’t help here. But I agree, if you don’t need the standalone functionality, raco make
can make startup a bit faster (but not much).

Unfortunately, the startup times can be much higher if you require a few more modules. So “typical” startup times for me are over 200 ms. A “Hello world” program with #lang racket
, compiled with raco make
, takes about 260 ms here.

(all measurements with Racket 8.2 cs)

That said, execution time beyond startup is pretty fast in my opinion, which I find impressive for code without static typing (not considering typed Racket here). :+1:
But still, if you rather need/want faster startup times, Racket is rather slow.

@sehoongim has joined the channel

raco make
fully expands programs. Which can be quite slow when your program (or any not-yet-raco-made modules it requires, transitively) have non-trivial macros.

If you had a project using say typed/racket
and your main.rkt
required foo.rkt
which required bar.rkt
— you’d probably see a bigger difference between startup having to fully expand all 3 every time you run, vs. reusing that work that raco make
had done/saved.

A “fun” experiment is to delete all .zo
files in the entire Racket installation, enter racket
at the command line, and start your timer…. :slightly_smiling_face:

Most of that time will be spent fully-expanding modules, not “compiling” to bytecode or machine code, IIUC.

I was still curious about the fun experiment. Starting racket
that way takes about 30 s on my computer. :smile:

To simulate that without making permanent changes, you can use the -c command line argument

@samth You should have told me that a minute ago. :wink: Seriously though, I had made a copy of the installation directory, so it took me only a few seconds to restore the directory with the zo files.

I’m actually surprised it took “only” 30 seconds. Maybe instead I should have suggested something like starting Dr Racket. :slightly_smiling_face:

btw above I forgot about the raco expand
command. Maybe a TL;DR would have been, “For some programs, raco make
takes hardly longer than raco expand
”

does racket do any dead code elimination? e.g importing a library and only using a few functions will have it so it only pulls in those functions and not the entire lib and bloating your runtime?

what do you mates think of commonlisp and why do you prefer racket over it(assuming you do)

In general dead code elimination is not sound in racket

But there are tools to do this, eg raco demod

• I prefer (foo arg ...)
over (funcall foo arg ...)
• I prefer the requirement to support tail call optimization • I feel Racket has better supported libraries • I prefer Racket’s macro system • The lack of the standard, and associated bureaucracy allows Racket to evolve more easily • etc.

I just tried raco demod
on my <https://git.sr.ht/~sschwarzer/sudoku-solver|Sudoku solver>.
racket games/sudoku-solver.rkt --help
shows the help text as expected, however raco demod -o sudoku-solver-demod.zo games/sudoku-solver.rkt
racket sudoku-solver-demod.zo --help
shows nothing despite the https://docs.racket-lang.org/raco/demod.html\|documentation saying > The demodularized ".zo" file can be run by passing it as an argument to the racket command-line program, or it can be turned into an executable with <https://docs.racket-lang.org/raco/exe.html|raco exe>. I also tried raco exe -o sudoku-solver sudoku-solver-demod.zo
and running sudoku-solver
neither gives any output.

Racket is built for and by people who want to explore new things, while still keeping practical (fast implementation, and all that…). CL is much more geared towards people who “just want to make <thing>”.

To all that, I’d add that I prefer Racket’s more functional flavor.

Also, the ability to evolve a macro dsl into a lang.

I’m also having roughly ~0.15s (0.10user + 0.05sys) startup time on my Mac 2015, this is roughly on par with Racket BC (!) which is implemented in C.
In comparison, python3 takes about ~0.04s.
I think this partly has to do with the architecture of Racket. In Python, Python is the language implemented by the compiler/interpreter. However, racket/base
is still many layers of languages on top of the core. The bytecode loading time and all the module name resolution time may affect performance.
If I directly load racket/kernel/init
, the startup time becomes ~0.10s (0.05user + 0.04sys). Half of the time spent in the user space is gone.

However, starting the GUI system takes way more time. Loading racket/gui/base
alone takes ~1.20s (0.91user + 0.18sys)

But I suspect it’s probably never going to be comparable to a compiled C/C++ binary, though. After all, compiled C/C++ binaries don’t carry a copy of GCC in it but eval
and expand
are in Racket kernel.

> but eval
and expand
are in Racket kernel. I’d say the problem isn’t to have them in the kernel, but how much they’re used/needed to execute during startup. (For comparison, Python also can load new code at runtime but still has a shorter startup time.) Ideally (from a startup perspective) a raco exe
d executable should do already as much as possible of what’s done when starting a Racket file from source code. If I understand correctly, there may be some expansions that need to be deferred to startup time, maybe depending on the runtime environment, but I guess there shouldn’t be too many of these.

By the way, here’s some information I recorded from an earlier discussion of startup time: https://gist.github.com/sschwarzer/5aaf9f203c552f1bf13a167dd08ded11#racket-startup-time

Yes, I agree is that. Currently there isn’t much staging done in those eval and expand thing. It ought to be possible — though not an easy task because of the current design — but requires someone to work on it.

But then, I’m unsure by how much the startup time can be reduced and if it’s worth it. Depending on the application, even something like 50 ms may still be too long. But I think for a general perception of “snappyness” (if that’s a goal) it would probably be something like 50 ms for a “normal” command line program, maybe something that currently takes 250 ms to start. Of course, these times depend on the hardware, so something that might be “snappy” for me may still be quite slow for someone else.

For the record, here are some other circumstances where a fast startup time is or may be important: • Programs that are run repeatedly, for each keypress. For example, if you press a volume down/up key on the keyboard, you want an immediate reaction. You don’t want repeated keypresses to be delayed and the sound volume to “overshoot” (in neither direction, but especially if you turn the volume up). • Programs that are called repeatedly from shell skripts in a loop. • Similarly, programs that are called on several nesting levels, like in makefiles. • Programs that are run as part of shell prompt rendering. If the program is too slow (and you may even have several), repeated presses of <enter> would cause newlines between prompt lines. That’s probably not a big deal but it just looks “wrong.” Actually this is an application I had to deal with recently, and I coded the program in Nim; even Python was a bit too slow here.

The biggest difference between Racket and Python startup is that the compiler and core library is not already machine code that can just be mmapped in. Even the parts that are part of the executable, like expand, are in a form that requires relocation upon loading.

Yeah, for video games especially…. the golden standard is 60fps or whatever the refresh rate of the monitor is… that’s 16MS to simulate your world, test and resolve collisions, physics, etc, and on top of that you have to render pretty graphics while syncing and playing sound ques

we’re quite lucky that modern hardware is super fast and somewhat spoiled at the same time lol

That seems different, in that you probably wouldn’t start a new instance of the runtime in your display loop