You could write a non-shell launcher using fork+exec+wait and present the results with finer granularity than time(1). There may be something adequate available off-the-shelf but I’m not familiar with it.
$ git clone https://github.com/jart/cosmopolitan && cd cosmopolitan
$ make -j8 o//examples/rusage.com o//examples/hello.com
$ o//examples/rusage.com o//examples/hello.com
hello world 123
RL: took 90µs wall time
RL: ballooned to 108kb in size
RL: needed 106µs cpu (0% kernel)
RL: caused 13 page faults (100% memcpy)
This one has a tiny bit more overhead than the practical minimum you saw earlier in the benchmark. Note that APE binaries start off as shell scripts so the first run is going to be slower. It's something I'm working towards improving in a variety of ways. Here's the executive summary of the upcoming changes: int ws, pid;
CHECK_NE(-1, (pid = fork()));
if (!pid) {
execve("ape.com", (char *const[]){"ape.com", 0}, environ);
perror("execve");
_Exit(127);
}
CHECK_EQ(pid, wait(&ws));
CHECK_TRUE(WIFEXITED(ws));
CHECK_EQ(0, WEXITSTATUS(ws));
Here's the latency of the various execution strategies: FORK+EXEC+EXIT+WAIT │ APE │ APE-LOADER │ BINFMT_MISC
───────────────────────────────────────────────────────────────
fork() + execve() │ 55µs │ 66µs │ 56µs
vfork() + execve() │ 25µs │ 446µs │ 35µs
/bin/sh -c ./ape (1st) │ 485µs │ 457µs │ 170µs
/bin/sh -c ./ape (avg) │ 148µs │ 466µs │ 159µsThen for my musl-gcc hello world, I did, "for i in {1..10}; do o//examples/rusage.com ../hello" and the lowest wall time I got was 120us. But most of the runs were closer to 350us.
So, my attempt to answer your, "Why does hello world take 1.8ms to run?" question is: a) they timed it with "time" which measures lots of overhead, b) there's something wrong with their setup that means they are mostly timing the time taken for a CPU core to wake up.