Home > Software engineering >  Way to minimize `Raku` scripts start-up time via persistence?
Way to minimize `Raku` scripts start-up time via persistence?

Time:10-02

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.

This is still a proof of concept since arguments don't get into the target script (and I don't know how except slurping manually, substituting and EVALuating - dirty, brittle, bad).

CodePudding user response:

Assuming you have a recent version of Rakudo, I can say that you have indeed a very slow machine:

% time perl -E 'say "Hello, World!"'
Hello, World!
real    0.05s
user    0.00s
sys 0.03s

% time raku -e 'put "Hello, World!"'
Hello, World!
real    0.12s
user    0.12s
sys 0.02s

are the numbers I see on a 2-year M1 MacMini. On a 10 year old MacMini running Debian, I see:

$ time perl -E 'say "Hello, World!"'
Hello, World!

real    0m0.009s
user    0m0.004s
sys 0m0.005s

$ time raku -e 'put "Hello, World!"'
Hello, World!

real    0m0.241s
user    0m0.287s
sys 0m0.041s

Now to get back to your question: yes, you can have something like FastCGI but for a local shell:

% raku -e 'while prompt "Enter filename: " -> $filename { EVALFILE $filename }

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.

CodePudding user response:

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 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.

  • Related