Raku
start-up times are notoriously huge.
On my machine (old one but the most modern I own):
time raku -e 'put "Hello, World!"'
495.67 – 567.02 millis
(1.53 secs for the 1st time launch)
time perl -E 'say "Hello, World!"'
13.63 – 19.51 millis
time sh -c 'echo "Hello, World!"'
4.31 – 6.88 millis
c
:
#include <stdio.h>
int main(void) {
puts("Hello, World!");
return 0;
}
time ./hello
5.03 – 7.67 millis
time clang hello.c -o hello
(compile)
126.39 – 195.66 millis
Raku
does boast many nifties built-in which make it heavy.
Yet sometimes you need a rather simple script, and you would prefer Raku
for its elegance, but launch-time spent waiting brings dissatisfaction and you don’t pick Raku
for your next simple script.
Is there a way to help it?
Like starting Raku
once and keeping it running, and making it handle all Raku
scripts, with and without modules, big and small, and raku -e ...
as well?
Something like FastCGI
but for local shell?
Even sacrificing permanently huge amount of memory is better for me than wasting time waiting.
In KiloBytes, according to ps
:
Virtual size Resident set size
raku -e 'loop {sleep 1}' 146704 107200
perl -e 'while (1) {sleep 1}' 1252 4036
sh -c 'while true; do sleep 1; done' 892 900
#include <unistd.h>
void main(void) { for(;;) {sleep(1);} } 172 780
Update:
Elaborating upon
raku -e 'while prompt "Enter filename: " -> $filename { EVALFILE $filename }'
from https://stackoverflow.com/a/73873471/14812514
Concocted from https://docs.raku.org/type/IO::Socket::INET and https://www.tutorialspoint.com/perl/perl_socket_programming.htm
raku-persistent
, heavy server:
#! /usr/bin/env raku
use MONKEY-SEE-NO-EVAL;
my $listen = IO::Socket::INET.new( :listen,
:localhost<localhost>,
:localport(3333) );
loop {
my $conn = $listen.accept;
try {
while my $buf = $conn.recv() {
put now - (EVAL $buf)[1] - 37; # also subtract leap seconds
EVALFILE (EVAL $buf)[0];
}
}
$conn.close;
CATCH { default {.payload.say} }
}
ra
, light client:
#! /usr/bin/env perl
use strict;
use warnings;
use Time::HiRes;
use Socket;
my $file = shift || '/tmp/test.raku';
my $arg1 = shift || Time::HiRes::time;
# initialize host and port
my $host = shift || 'localhost';
my $port = shift || 3333;
my $server = "localhost"; # Host IP running the server
# create the socket, connect to the port
socket(SOCKET,PF_INET,SOCK_STREAM,(getprotobyname('tcp'))[2])
or die "Can't create a socket $!n";
connect(SOCKET, pack_sockaddr_in($port, inet_aton($server)))
or die "Can't connect to port $port! n";
printf SOCKET '["%s", %f]', $file, $arg1;
close SOCKET or die "close: $!";
/tmp/test.raku
:
put "Hello, World!";
run 'notify-send', 'Hello, World!'
raku-persistent
in one terminal, once;
ra [script.raku]
in another terminal, how many times you want.
Delay ranges 0.008848472 – 1.322056732; in most cases being below 0.07.
0.008848472 must be some kind of a mistake – it is less than perl startup time, which is impossible.
This is still a proof of concept since arguments don’t get into the target script.
Update 2:
As a reaction to https://stackoverflow.com/a/73918912/14812514
Suggested bench suite doesn’t work via Makefile
on my machines, even on Debian-based antiX 21, and possible solution was found after I had done all measurements semi-manually. Moreover, on OpenBSD run.c
doesn’t even compile. Being zero at c
, I resorted to shell time for i in $(seq 1000); do ./<file> >/dev/null; done
.
All 4 machines with HDD, no SSD.
My initial configuration is i5-3320M OpenBSD and marked with ^
.
Results in seconds for 1K invocations, also for single invocation for Raku only:
CPU cores GHz OS via Raku K Raku 1 Perl K C K
Pentium 4-M i686 1 2.2 OpenBSD i386 shell seq 36m32.479 2.192479 22.368 6.408
Core 2 Duo T5800 2 2 antiX c run 10m34.460 0.63446 2.224 0.535
Core 2 Duo T5800 2 2 antiX shell seq 7m48.153 0.468153 3.878 1.509
^i5-3320M 2 2.6 OpenBSD shell seq 8m 0.011 0.480011 8.150 2.258
i5-3320M 2 2.6 antiX live c run 4m53.469 0.293469 1.157 0.276
i5-3320M 2 2.6 antiX live shell seq 3m37.042 0.217042 1.688 0.615
i7-3770S 4 3.1 OpenBSD shell seq 6m44.920 0.40492 7.026 2.340
i7-3770S 4 3.1 antiX live c run 4m 5.571 0.245571 0.872 0.268
Some highlights:
- Raku startup times are unwelcomely regardless of CPU and OS
- shell
for i in $(seq...
is slower than custom Crun
for Perl & C but outperformed it for Raku – 7m+ vs 10m+ on Core 2 Duo and 3m+ vs 4m+ on i5 - OpenBSD doesn’t prioritize speed
- 3rd-party software doesn’t prioritize optimization for OpenBSD
2
Answers
Assuming you have a recent version of Rakudo, I can say that you have indeed a very slow machine:
are the numbers I see on a 2-year M1 MacMini. On a 10+ year old MacMini running Debian, I see:
Now to get back to your question: yes, you can have something like FastCGI but for a local shell:
run this, enter the name of the script to execute and ENTER, and it will run. Now, this is the principle: this only runs scripts without arguments. You can build on this allowing for arguments and such.
The client-server model you describes seems like a really good idea to me! In fact, I’ve been toying with a similar idea myself; I actually gave a lightning talk on the subject at the 2022 Perl and Raku Conference, A Nailgun for Raku.
As that talk mentions, this is the basic idea behind Nailgun – which implements the same idea but for Java. As you note, it gets a bit more complex than the proof of concept, but definitely seems like a doable Raku project.
On a different note, I agree with Raiph [edit: with Liz, oops!] that the timings you posted are pretty close to a worst-case scenario in terms of launch speed. My go-to reference for language startup time benchmarks is bdrung/startup-time, which has both a benchmarking framework and the author’s own data. For comparison, that author clocks a "Hello, World!" C program between 0.26 ms (for a 2018 laptop) and 2.19 ms (for a Raspberry Pi 3). Those times might provide some context for the 5.03 to 7.67ms time you measured for a "Hello, World!" C programs.