"Is Affix faster than... everything else?"
Let's find out!
The Perl ecosystem offers several ways to call C code. How does Affix stack up?
We will benchmark four approaches:
-
Compiles C code into a custom XS module. This is historically the fastest way to call C from Perl, as it eliminates the "FFI" layer entirely, but it requires a C compiler at installation time (or runtime).
-
The current dominant FFI module on CPAN. It uses
libffi(a portable, general-purpose Foreign Function Interface library) to handle argument shuffling. -
Our pride and joy. Wraps our custom JIT engine: infix.
The Recipe
We'll compile a shared library performing a simple integer addition, then bind it using all three libraries.
use v5.40;
use Benchmark qw[cmpthese];
use Affix qw[:all];
use Affix::Build;
use FFI::Platypus 2.00;
# All tests run on the same C.
my $c_code;
BEGIN { $c_code = <<~'' }
int add_ints(int a, int b) {
return a + b;
}
# Inline::C setup
use Inline C => Config => ( ccflags => '-O3' );
use Inline C => $c_code;
# We must compile the library for Affix and Platypus
my $c = Affix::Build->new( flags => { cflags => '-O3' } );
$c->add( \$c_code, lang => 'c' );
my $lib = $c->link;
# FFI::Platypus setup
my $ffi_plat = FFI::Platypus->new( api => 2 );
$ffi_plat->lib($lib);
$ffi_plat->attach( [ add_ints => 'add_platypus' ], [ 'int', 'int' ], 'int' );
# Affix setup
affix $lib, [ add_ints => 'add_affix' ], [ Int, Int ] => Int;
# The race
say 'Benchmarking for 5 seconds...';
my $x = 100;
my $y = 200;
cmpthese - 5, {
Inline => sub { add_ints( $x, $y ) },
Platypus => sub { add_platypus( $x, $y ) },
Affix => sub { add_affix( $x, $y ) }
}
The Results
On my system, I see results similar to this:
Benchmarking for 5 seconds...
Rate Platypus Inline Affix
Platypus 5026460/s -- -57% -82%
Inline 11641793/s 132% -- -58%
Affix 28016636/s 457% 141% --
Analysis
-
Affix vs. XS (Inline::C)
To the reader, the most startling result is that Affix is over 120% faster than Inline::C. Traditionally, XS (which Inline::C generates) was considered the speed limit for Perl. Affix beats this limit by generating a custom JIT trampoline that reads Perl's internal SVs directly, skipping the overhead of the Perl stack macros (
dXSARGS,ST(n)) that XS relies on. It is effectively "Inline Assembly" for Perl. -
Affix vs. Platypus
Affix runs over 4x faster than Platypus in this test. While Platypus relies on
libffi(a stable, generic library),libffimust handle platform logic at runtime. Affix generates specific machine code for the exact call signature, allowing for drastic optimizations.
Summary
- Use Affix for flexibility, safety, and complex types (Structs, Arrays). As the benchmarks show, you sacrifice absolutely nothing in speed; in fact, you'll likely gain performance over hand-written XS.
- Use Inline::C if you are already comfortable writing XS macros and want static compilation, though you may actually lose performance compared to Affix unless you really focus on optimizing your own hot path.
- Use FFI::Platypus if you already already have code written and don't feel like moving to Affix?