Getting started with XS

eXtendable Subroutines (XS) are subroutines written in C that are callable from
Perl code. There are two common reasons you’d want to use XS: there is a C
library you’d like to use with Perl, or you want to make a subroutine faster
by processing it in C instead of Perl.

This tutorial will walk you through all the components needed to get up and
running with a basic XS example. There will be a lot of new terms and concepts:

If you want to write XS, you have to learn it. Learning XS is very difficult

Steven W. McDougall

Try not to get discouraged if things don’t click right away: I promise you that
learning XS can be hugely rewarding: you’ll develop the power to write lightning
fast code; get a better understanding of how Perl internals work, and be able to
integrate any C library you choose and use it from Perl.

Components

There are a few basic components needed to write an xsub. The first is a Perl
module that will provide the namespace for any XS functions. This is all that’s
needed:

package XS::Tutorial::One;
require XSLoader;
XSLoader::load();
1;

This file should be saved as lib/XS/Tutorial/One.pm. XSLoader::load by
default searches for XS code that matches the package name it is called from*.

This file should be saved as lib/XS/Tutorial/One.xs. The top half of the file
is pure C code. The line beginning MODULE = XS::Tutorial::One indicates the
start of the XS code. This section will be parsed and compiled into C code by
xsubpp.

The MODULE and PACKAGE directives define the Perl module and package which
will load any xsubs we define. The line PROTOTYPES: ENABLE tells xsubpp
to define subroutine prototypes for any xsubs we create. This is usually what
you want: prototypes can help Perl catch compile time errors.

The last two lines of the file are an xsub:

unsigned int
rand()

The first line defines the return type. The second line does two things: it
indicates the name of the C function to be called and it defines the
signature of the xsub.

In this case we’re calling rand and accepting no parameters. This isn’t
Perl’s built-in rand function, this rand comes from stdlib.h.

The final thing we need is a Makefile.PL script - as XS code is compiled, we
need a tool to build it before we can use it:

But let’s talk about XSMULTI. This is a relatively new feature which allows
you to have separate .xs files for modules. By default EUMM assumes the xs
file matches the distribution name. In this case that would mean having a single
Tutorial.xs file, with multiple xs MODULE and PACKAGE declarations in it.
By using XSMULTI, we can have multiple XS files, one for each module in the
distribution instead.

Building

A minor essay to understand xsubpp generated C

make creates a bunch of files, but take a look at lib/XS/Tutorial/One.c.
This is the output of xsubpp. If you look closely enough, you can find the
lines of C code from lib/XS/Tutorial/One.xs in there. But checkout what
happened to our rand xsub:

xsubpp has replaced our XS code with some rather ugly C macros! These macros
are part of the Perl interpreter’s C API. Many are documented in perlapi
and they are usually defined in XSUB.h or perl.h in the Perl source code.

So what are these macros doing? At a high level, dVAR and dXSARGS setup
the global pointer stack and some local variables. items is a count of the
arguments supplied to the xsub. As rand is a void function, if this isn’t
zero, it croaks. croak_xs_usage takes a coderef and an args string. In this
context cv is the xsub, and there are no args so the string is empty.

Next the code declares RETVAL, the return value of the xsub. dXTARG
initializes the TARG pointer. Next rand() is called its return value
assigned to RETVAL. XSprePUSH moves the stack pointer back one,
and PUSHu copies RETVAL into TARG and pushes it onto the global stack
pointer. XSRETURN returns from the xsub, indicating how many arguments it
added to the stack, which in this case, is one.

Writing XS, you usually don’t need to study the generated C code, but it’s
helpful to have an awareness of the process.

Installing

Now the code is compiled, install it with:

$ make install

If you’re using system Perl, you may need to use sudo to install. Now we can
test the module using a one liner:

We get the same pseudorandom sequence each time… We need to call srand to
seed the sequence. That function is already provided by stdlib.h, so all we
need to do is append the following text to lib/XS/Tutorial/One.xs:

void
srand(seed)
unsigned int seed

This xsub is different to the first one: its return type is void which
means it returns nothing. It also includes a parameter called seed in its
signature, and the last line defines it as an unsigned int.

Rebuild and install the distribution:

$ make && make install

Now we can seed the pseudorandom sequence by calling srand before rand:

We used a lucky (777) seed number, and rand emitted a different number, yay!

Did we beat Perl?

As you know by now, xsubs are often faster than pure Perl code. We’ve built two
xsubs for rand and srand, which are also available as built-in functions
in Perl. Do you think the xsubs are faster? Here’s a benchmark from my machine:

Oh no! Despite our rand xsub directly calling the C stdlib function, it’s
miles slower than Perl’s built-in rand. This isn’t because xsubs are slow,
rather that Perl’s built-in functions are really fast. There is an overhead
associated with calling xsubs which built-in functions do not pay.

Tests

Instead of running one liners to check our code works, we can write unit tests.
Here’s a basic script:

Site Map

Contact Us

License

Legal

Perl.com and the authors make no representations with respect to the accuracy or completeness of the contents of all work on this website and specifically disclaim all warranties, including without limitation warranties of fitness for a particular purpose. The information published on this website may not be suitable for every situation. All work on this website is provided with the understanding that Perl.com and the authors are not engaged in rendering professional services. Neither Perl.com nor the authors shall be liable for damages arising herefrom.