--- name: fast start-up with the pperl daemon --- # Fast start-up: the daemon Every `pperl` invocation normally pays the full start-up bill: process creation, interpreter initialisation, and - the expensive part for real programs - compiling your script and every module it `use`s. The daemon lets repeated invocations of the same script skip that bill: a resident process keeps a fully compiled image of the script, and each new run is a fork of that image that goes straight to execution. The feature is strictly **opt-in**, per invocation. Nothing changes for plain `pperl script.pl`, and whenever the daemon cannot serve a request exactly, pperl silently falls back to a normal cold run. Correctness never depends on the daemon; only speed does. ## Quick start ```bash pperl --daemon=start # start the daemon (background) PPERL_DAEMON=2 pperl report.pl # first run: compiles, caches PPERL_DAEMON=2 pperl report.pl # every further run: fork + run pperl --daemon=status # what is cached, what is live pperl --daemon=stop # shut it down ``` The first opted-in run of a script builds its *template*: the daemon compiles the script (including all `use`d modules and `BEGIN` blocks) and keeps the result resident. Every later run of the same script forks the template and only executes. ## When it pays The saving is your script's compile time: parsing plus module loading plus `BEGIN` work. For a script that loads a handful of modules this is routinely the dominant part of start-up, and a template run can be several times faster end to end. For a trivial script that loads nothing, the saving roughly equals the daemon's own overhead and the result is a wash - never a meaningful loss. As everywhere in this guide: measure your own script, on your own machine, before drawing conclusions - see [Measuring](measuring). ## What running from a template means A template run executes your script's *run phase* only. Compilation already happened, once, when the template was built. pperl guarantees that the following are fresh and correct for every run: - `@ARGV`, `%ENV`, the current directory, umask - standard input, output and error (your redirections and pipes) - the process id `$$` - exit codes, and death by signal (a killed script kills the invoking `pperl` the same way) - the `__DATA__` handle - each run reads it from the start, independently of other runs What is **not** re-done is anything your code did at compile time. `BEGIN` blocks, and module code executed at `use` time, ran once when the template was built; their side effects are part of the image every run inherits. Consequences you own when you opt a script in: - `$^T` (the script start time) and the random-seed state date from template creation. If your script cares, re-stamp them at the start of the run phase: ```perl INIT { $^T = time; srand(); } ``` - Values read from `%ENV` *at compile time* (inside `BEGIN` or at `use` time) were the values of the run that built the template. Read configuration at run time, or in an `INIT` block, if it must track the invoking environment. - File handles, sockets or database connections opened at compile time are shared with every run. Open connections at run time - the same rule long established for preloading application servers. If a script cannot satisfy this contract, simply do not opt it in; `PPERL_DAEMON=2` is per invocation, not global. ## Editing the script, and the cache The daemon notices when the script file itself changes and rebuilds the template automatically on the next run - editing your script never serves you a stale image. Edits to *modules* the script loads are not detected automatically. After changing a module, drop the affected template by hand: ```bash pperl --daemon-reset=report.pl # forget this script's template pperl --daemon-reset # forget all templates ``` Templates are a bounded cache: at most `--daemon-max-scripts` of them (default 100), evicted least-recently-used, and each expires after the daemon's idle timeout (`--daemon-idle`, default 900 seconds). ## When pperl falls back to a cold run The daemon refuses - and the client falls back to a normal run, silently and correctly - whenever it could not reproduce the invocation exactly: - no daemon is running for this pperl binary - environment variables that shape interpreter start-up differ from the daemon's (`PERL_HASH_SEED`, `PERL_PERTURB_KEYS`, `PERL_RAND_SEED`, `PERL_INTERNAL_RAND_SEED`, `PERL_UNICODE`, `LANG`, any `LC_*`, any `MALLOC_*`) - the command-line flags differ from the ones the template was built with (a template stores one configuration) - the template is still being built by a concurrent first run - the invocation is a `-e` one-liner (templates cache script files) - `--interactive` or `--stats` is in effect ## Security and identity The daemon serves exactly one user: its socket lives in a directory with owner-only permissions and every connection's peer user id is verified. It also serves exactly one *build* of pperl: the socket is keyed to the binary's identity (`pperl --build-id` prints it), so after a pperl upgrade the old daemon is simply never contacted again and exits on its idle timeout. ## Limitations - Job control is approximate: the running script is a child of the daemon, not of your shell. Signals you send to `pperl` (including `Ctrl-C`) are forwarded to the script, but `Ctrl-Z` suspends only the foreground `pperl`, not the script process. - One template per script path, holding one flag configuration. Invoking the same script with different flags falls back to a cold run rather than rebuilding the template. - `PPERL_DAEMON=1` (or `--via-daemon`) exists as a reduced mode that forks a pre-initialised interpreter but still compiles the script every run. It saves almost nothing and is mainly useful for testing the daemon itself; use `PPERL_DAEMON=2` for the real benefit.