Compiling & Running

Flags, optimization levels, the compile-then-run loop, when to use -M vs a full compile.

__C__ Blocks

Drop into raw C from inside Perl. Variable name mapping, runtime helpers, memory rules.

Module Search Paths

How use and require resolve — compile-time vs runtime, dedup, auto-build.

Installing CPAN Modules

perla-cpan — fetch from MetaCPAN, resolve deps, compile XS, install into a prefix.

Debugging & Diagnostics

Reading --debug output, common failure modes, keeping the generated C.

Perl Compatibility

What works, what doesn't, where perla's semantics differ from real Perl.

Memory Management

Automatic cycle collection (no weaken needed) and the request arena, exposed to Perl via Strada::*.

Cannoli Integration

Running a perla-compiled web app under Cannoli's preforking HTTP server.

Compiling & Running

perla has four main modes of invocation:

CommandWhat it does
perla script.plCompile and run. Cleans up the binary after.
perla -o prog script.plCompile to ./prog, don't run.
perla -c -o out.c script.plEmit C code only; skip the cc step.
perla -M Module.pmPrecompile the module to Module.pm.o.
perla -M dir/Recursively precompile every .pm under dir/.
perla -e 'code'Run a one-liner.

All command-line flags

FlagEffect
-o FILEOutput path (executable in normal mode, C file with -c).
-cGenerate C only; don't invoke the C compiler.
-e 'CODE'Execute an inline one-liner.
-M FILEPrecompile module/directory.
-I PATHAdd to compile-time @INC.
--cc BINUse a specific C compiler (default gcc, alternative tcc).
--debugVerbose compile-time tracing. Prints module search, cache events, cc command.
--keepDon't delete the intermediate .c after compile.
-O0 .. -O3, -Os, -Ofastgcc optimization (default -O0; see below).
--precompile-depsAuto-build .pm.o+.pm.so for every module encountered.
--sharedPrefer .pm.so (runtime dlopen) over .pm.o (static link).
--force-rebuildperla -M rebuilds every module, even cached ones.
--vmRun via Strada codegen interpreter path (no cc step).
--stradaEmit Strada source instead of C.

Optimization levels

Defaults to -O0 — optimized for compile speed during dev. For release builds, use -O2 or -O3.

FlagAdded gcc argsUse case
(default)-O0 -wDev iteration (fastest compile)
-O2-O2 -w -fltoStandard release
-O3-O3 -w -flto -march=nativeMax performance, native CPU
-Os-Os -w -fltoSize-optimized
-Ofast-Ofast -w -flto -march=nativeAggressive (may violate strict IEEE)

On a medium-sized web app, the gcc phase goes from ~15s at -O0 to ~100s at -O2. Worth it for release; not worth it during iteration.

__C__ Blocks

__C__ { ... } drops raw C into the generated output. perla doesn't parse or rewrite the block — it's emitted verbatim. Three valid positions:

File scope

Place outside any sub. Use for #includes, static state, and helper functions that you'll call from multiple __C__ blocks.

__C__ {
    #include <sys/time.h>
    #include <unistd.h>
    static int call_counter = 0;
}

Inside a sub

Emitted as a { ... } block inside the generated C function body.

sub add {
    my ($a, $b) = @_;
    my $sum = 0;
    __C__ {
        int64_t __a = strada_to_int(v_a);
        int64_t __b = strada_to_int(v_b);
        strada_decref(v_sum);
        v_sum = strada_new_int(__a + __b);
    }
    return $sum;
}

Expression context (GCC statement-expression)

my $pid = __C__ {
    ({ StradaValue *__r = strada_new_int((int64_t)getpid()); __r; })
};

Variable name mapping

Critical gotcha. Perl $foo becomes C v_foo — perla does not substitute $x → v_x inside __C__ blocks. You must use the C names directly. Use --keep to inspect the generated C if unsure.

PerlC identifier
$foov_foo
@arrv_arr
%hashv_hash
@_perla_at_
$_perla_dollar_underscore
$1..$9strada_capture_var(N)
$!strerror(errno) read / errno = N write
$/ $\ $,perla_irs perla_ors perla_ofs
$|__perla_autoflush (C int)
$$getpid()
$0perla_dollar_zero
$^W $^O $^V $^Xv__caret_W, v__caret_O, etc.
$]v__rbrack_
$Pkg::scalar (fully-qualified)perla_scalar_get("Pkg", "scalar")
@Pkg::arrperla_array_get("Pkg", "arr")
%Pkg::hashperla_hash_get("Pkg", "hash")

Runtime helpers available

All strada_* and perla_* functions are in scope. The most useful:

HelperUse
strada_to_int(sv)int64_t, handles tagged ints transparently
strada_to_num(sv)double
strada_to_str(sv)malloc'd char*caller must free()
strada_to_str_buf(sv, buf, sz)writes to caller's buffer; no alloc
strada_new_int(n) strada_new_num(d) strada_new_str(s)construct boxed values
strada_new_array() strada_new_hash()empty containers
strada_incref(sv) strada_decref(sv)refcount bookkeeping
strada_hv_store_take(hv, key, val)hash insert, takes ownership
strada_array_push_take(av, elem)array append, takes ownership
STRADA_IS_TAGGED_INT(sv)Always check before touching sv->type or sv->value
perla_bless(ref, "Pkg") perla_blessed(sv)Perl blessing
perla_method_dispatch(obj, method, args)Full Perl method dispatch
perla_scalar_get/set(pkg, name)Package-scoped scalar via stash
perla_dbi_connect, perla_dbi_prepare, perla_dbi_executeDBI bridge

Memory rules

  1. Tagged-int guard. Integers in range -2^62..2^62-1 are encoded directly in pointers. Any code that reads sv->type/sv->value/sv->meta/sv->refcount must check STRADA_IS_TAGGED_INT(sv) first. The high-level helpers handle this for you.
  2. strada_to_str allocates. Always free() the result. For no-alloc, use strada_to_str_buf with a stack buffer.
  3. Decref before reassign. strada_decref(v_result); v_result = strada_new_int(42); — forgetting leaks; double-free is worse.
  4. _take vs plain for container inserts. strada_hv_store_take(hv, "k", strada_new_str("v")) keeps refcount at 1; strada_hv_store increfs. Use _take for freshly-allocated values, plain store for existing variables.

Complete worked example

use strict;

__C__ {
    #include <sys/resource.h>
}

sub rss_bytes {
    my $r = 0;
    __C__ {
        struct rusage __u;
        if (getrusage(RUSAGE_SELF, &__u) == 0) {
            /* ru_maxrss is KB on Linux, bytes on macOS */
            strada_decref(v_r);
            v_r = strada_new_int((int64_t)__u.ru_maxrss * 1024);
        }
    }
    return $r;
}

printf "RSS: %d MB\n", int(rss_bytes() / 1024 / 1024);

For the complete __C__ reference including FFI examples, see the Markdown version on GitHub.

perla has two separate search paths for use and require.

Compile-time path (consulted when parsing use X)

Built by _build_lib_paths, in search order:

  1. Directory of the input file
  2. Current directory (.)
  3. $PERLA5LIB env var
  4. $PERL5LIB (Perl compatibility)
  5. $HOME/perla/lib (if it exists)
  6. -I path CLI flags (prepended)
  7. use lib "..." statements during parse

Not included by default: /opt/bzperl/... or any large CPAN tree. Including it would trigger transitive source parsing of every module in the tree, blowing compile time. Add it via use lib "..." in your script if you need it.

Runtime path (consulted for require and lazy-loads)

Baked into the executable at compile time. Plus at startup: $PERLA_LIB, ~/.perla.conf, /etc/perla.conf. Also probes archlib subdirs (x86_64-linux/) for each path.

Dedup

Two layers prevent re-parsing:

The precompile cache

perla -M Foo/Bar.pm writes:

Subsequent compiles that use this module pick up the cached .pm.o instead of re-parsing. Useful for warming a module tree:

$ perla -M lib/          # recursively precompile every .pm under lib/

Auto-build heuristic

By default, a module used in your program is only auto-precompiled if its source matches an import-installing pattern (sub import, our @EXPORT, *{$M.import}). Everything else is left for runtime require. This prevents runaway cascade compiles.

To precompile every dependency, use --precompile-deps.

Installing CPAN Modules

perla ships native implementations of the most-used modules (JSON, DBI, Carp, Data::Dumper, List::Util, Scalar::Util, Storable, Encode, File::Temp, Digest::*, MIME::Base64, Time::HiRes, and more). For everything else — pure-Perl and XS — there's perla-cpan.

ToolPurpose
perla-cpanEnd-user installer. Fetches a module by name from MetaCPAN, resolves transitive deps, compiles XS, installs .pm + .so into a prefix.
perla-xs-buildLow-level XS compiler: turns a .xs into .o/.so against perla's perl.h shim. Called for you by perla-cpan.

Quick start

# One-time: point perla at an install prefix
$ export PERLA_LIB=$HOME/.perla/local/lib/perla5
$ export PERLA5LIB=$PERLA_LIB

# Install what your program needs — dependencies resolved automatically
$ perla-cpan Try::Tiny Path::Tiny JSON::MaybeXS

# Compile and run as usual
$ perla myprog.pl

Sources

perla-cpan accepts a module name (fetched from MetaCPAN), a pre-unpacked directory, or a local tarball:

$ perla-cpan Try::Tiny                   # by name, with transitive deps
$ perla-cpan ~/build/My-Dist-1.0/         # pre-unpacked directory
$ perla-cpan --deps ~/dl/Foo-Bar.tar.gz   # local tarball, recurse deps

Useful flags

FlagEffect
--prefix DIRInstall root (default $HOME/.perla/local).
--no-depsSkip the transitive-dependency recursion.
--no-xsPure-Perl only; skip XS compilation entirely.
--precompileAlso run perla -M on each installed .pm to build .pm.o/.pm.so sidecars.
--dry-runShow what would happen; install nothing.

How it differs from cpanm. There's no live perl in the loop and no DynaLoader at runtime — XS is compiled ahead of time against perla's perl.h shim and linked into your binary. Most distributions build cleanly. A complex XS dist may reference a Perl-internals macro the shim doesn't define yet; that's usually a one-line addition. perla-cpan installs the current release and doesn't run a dist's Makefile.PL configure logic or satisfy version-range constraints.

Debugging & Diagnostics

--debug output vocabulary

Trace lineMeaning
use FooStarting to load module
use Foo -> already loaded (skip)Name-keyed dedup hit
Foo: resolved to already-loaded path ...Path-keyed dedup hit
Foo: cache HIT /path [.pm.o (static)]Found precompiled artifact, will link
Foo: cache MISS /path — auto-buildingNarrow heuristic matched, spawning perla -M
Foo: source /path (non-tree, will inline)Project-local module, inlining source
Foo: XS module detected — skippingFound auto/.../*.so, avoiding DynaLoader
[cc] gcc ...Full C compiler invocation (grep for this!)
[cc] done in XX.XXs (ok)cc finished with timing
[require] ...Runtime require activity (inside the compiled binary)

Common failure modes

sh: 1: foo: not found after compile

The compiled binary wasn't on $PATH. Recent perla prefixes ./ automatically; if you see this, rebuild perla.

redefinition of 'perla_sub_Foo_bar' at link

Either the source defines the same sub twice (perla now matches Perl's last-wins semantic — rebuild perla), or a dependency cycle re-parses the file (caught by path-keyed dedup).

undefined reference to perla_mod_init_Xxx at link

A precompiled .pm.o was built with the wrong module name. Re-run perla -M /full/path/to/module.pm or set PERLA_MODULE_NAME=Foo::Bar explicitly.

Can't locate object method X via package "REF(0x0x...)"

A blessed ref was stringified for use as a class name. perla_bless should unwrap this via perla_class_name — rebuild perla if you see this today.

Compile works but program behaves weirdly

Run with --keep, grep the generated .c for the affected sub. Usually a codegen issue with a specific Perl idiom.

Environment variables

VarEffect
PERLA_DEBUG=1Same as --debug
PERLA_PERLPath to a real perl binary (for @INC fallback probe)
PERL5LIBCompile-time search path (colon-separated)
PERLA_LIBRuntime-require search path
PERLA_DBI_DEBUG=1Trace DBI method calls
PERLA_METHOD_DEBUG=1Trace method dispatch
PERLA_BUILD_PM_SO=1perla -M also builds .pm.so
PERLA_SHARED=1Same as --shared
PERLA_FORCE_REBUILD=1Same as --force-rebuild
PERLA_NO_AUTO_BUILD=1Disable narrow auto-build heuristic

Perl Compatibility

What works

What doesn't work (yet)

Memory Management

perla links Strada's shared runtime, which means Perl programs compiled with perla get two opt-in memory features that mainline Perl does not have. Both are compiled in by default and are no-ops in source if the runtime was built --without-cycle-gc or --without-arena.

Automatic cycle collection

A Bacon–Rajan synchronous cycle collector reclaims indirect reference cycles (array/hash/ref graphs) without Scalar::Util::weaken. It runs at a candidate-root threshold and once more at exit. Acyclic data is still freed deterministically via refcounting; only genuine cycles wait for the collector.

# Indirect cycle -- normal Perl leaks this; perla collects it automatically.
my $a = { name => "a" };
my $b = { name => "b" };
$a->{peer} = $b;
$b->{peer} = $a;        # no weaken needed

Scalar::Util::weaken still works as before and is the right choice when you need deterministic teardown or are targeting a runtime built --without-cycle-gc. Cycles that close only through a closure capture or a tied container fall back to weaken; those aren't handled by the collector. Cyclic blessed objects reclaimed by the collector skip DESTROY — this matches Perl's own global-destruction behaviour for unreachable cycles.

Explicit controls are exposed under Strada:::

FunctionDescription
Strada::gc_collect()Force a cycle collection now
Strada::gc_disable() / Strada::gc_enable()Toggle automatic collection
Strada::gc_threshold($n)Candidate-root count that triggers auto-collect (default 10000)
Strada::gc_collections()Stats: collections run
Strada::gc_freed()Stats: cyclic objects reclaimed

Request arena

For request-scoped work (most obviously Cannoli’s preforking workers), the arena bump-allocates StradaValue structs into a region freed wholesale at arena_end — skipping per-object allocation and refcount teardown for the request's churn. Dormant until Strada::arena_begin() is called; safe to ship enabled.

Strada::arena_begin();
handle_request($req);     # allocate freely; nothing freed individually
Strada::arena_end();        # reclaims everything allocated since begin
FunctionDescription
Strada::arena_begin()Start a request arena (may be nested)
Strada::arena_end()Free the current arena wholesale
Strada::arena_active()1 if an arena is open, 0 otherwise
Arena rules — read these before adopting:

See the Strada memory documentation for the deeper design notes (side-table cycle collector, ARENA_OWNS skip in strada_free_value, the cycle collector's cc_free_shell).

Cannoli Integration

Cannoli is the Strada-native preforking HTTP server. perla-compiled apps load into it via the cannoli_perla.so bridge.

$ cd /path/to/cannoli/lib/perla
$ make                            # builds cannoli_perla.so
$ make demo                       # builds Demo.pm.so
$ /path/to/cannoli/cannoli \
    --library "./cannoli_perla.so:\
        so=$(pwd)/Demo.pm.so;\
        init=perla_mod_init_Demo;\
        handler=perla_sub_Demo_hello" \
    --dev -p 8080

$ curl http://127.0.0.1:8080/
Hello from a perla-compiled handler!

The bridge parses three config options:

See the cannoli_perla source for full detail. Cannoli ships with a parallel cannoli_perl.so for running real Perl (via libperl) if you'd rather stay in that world.

Tip: wrap each request in Strada::arena_begin() / Strada::arena_end() (see Memory Management) for one-shot wholesale reclamation of the request's allocations. Cannoli's preforking model (single-threaded per worker) is the intended use case; nothing in the bridge prevents it from working out of the box.

More

Markdown docs on GitHub →

The source-tree documentation has longer prose, internal notes, and memory-rule details.

Test suite →

119 tests covering the features perla guarantees. Good reference for what definitely works.

Strada reference →

The Strada language reference that underlies perla.

Back to Getting Started →

The 60-second install and first-program guide.