MicroECC
view release on metacpan or search on metacpan
perlmulticore.h view on Meta::CPAN
do_your_non_thread_safe_thing ();
pthread_mutex_unlock (&my_mutex);
perlinterp_acquire ();
=item I<Don't> get confused by having to release first.
In many real world scenarios, you acquire a resource, do something, then
release it again. Don't let this confuse you, with this, you already own
the resource (the perl interpreter) so you have to I<release> first, and
I<acquire> it again later, not the other way around.
=back
=head1 DESIGN PRINCIPLES
This section discusses how the design goals were reached (you be the
judge), how it is implemented, and what overheads this implies.
=over 4
=item Simple to Use
All you have to do is identify the place in your existing code where you
stop touching perl stuff, do your actual work, and start touching perl
stuff again.
Then slap C<perlinterp_release ()> and C<perlinterp_acquire ()> around the
actual work code.
You have to include F<perlmulticore.h> and distribute it with your XS
code, but all these things border on the trivial.
=item Very Efficient
The definition for C<perlinterp_release> and C<perlinterp_release> is very
short:
#define perlinterp_release() perl_multicore_api->pmapi_release ()
#define perlinterp_acquire() perl_multicore_api->pmapi_acquire ()
Both are macros that read a pointer from memory (perl_multicore_api),
dereference a function pointer stored at that place, and call the
function, which takes no arguments and returns nothing.
The first call to C<perlinterp_release> will check for the presence
of any supporting module, and if none is loaded, will create a dummy
implementation where both C<pmapi_release> and C<pmapi_acquire> execute
this function:
static void perl_multicore_nop (void) { }
So in the case of no magical module being loaded, all calls except the
first are two memory accesses and a predictable function call of an empty
function.
Of course, the overhead is much higher when these functions actually
implement anything useful, but you always get what you pay for.
With L<Coro::Multicore>, every release/acquire involves two pthread
switches, two coro thread switches, a bunch of syscalls, and sometimes
interacting with the event loop.
A dedicated thread pool such as the one L<IO::AIO> uses could reduce
these overheads, and would also reduce the dependencies (L<AnyEvent> is a
smaller and more portable dependency than L<Coro>), but it would require a
lot more work on the side of the module author wanting to support it than
this solution.
=item Low Code and Data Size Overhead
On a 64 bit system, F<perlmulticore.h> uses exactly C<8> octets (one
pointer) of your data segment, to store the C<perl_multicore_api>
pointer. In addition it creates a C<16> octet perl string to store the
function pointers in, and stores it in a hash provided by perl for this
purpose.
This is pretty much the equivalent of executing this code:
$existing_hash{perl_multicore_api} = "123456781234567812345678";
And that's it, which is, as I think, indeed very little.
As for code size, on my amd64 system, every call to C<perlinterp_release>
or C<perlinterp_acquire> results in a variation of the following 9-10
octet sequence:
150> mov 0x200f23(%rip),%rax # <perl_multicore_api>
157> callq *0x8(%rax)
The biggest part if the initialisation code, which consists of 11 lines of
typical XS code. On my system, all the code in F<perlmulticore.h> compiles
to less than 160 octets of read-only data.
=item Broad Applicability
While there are alternative ways to achieve the goal of parallel execution
with threads that might be more efficient, this mechanism was chosen
because it is very simple to retrofit existing modules with it, and it
The design goals for this mechanism were to be simple to use, very
efficient when not needed, low code and data size overhead and broad
applicability.
=back
=head1 DISABLING PERL MULTICORE AT COMPILE TIME
You can disable the complete perl multicore API by defining the
symbol C<PERL_MULTICORE_DISABLE> to C<1> (e.g. by specifying
F<-DPERL_MULTICORE_DISABLE> as compiler argument).
This will leave no traces of the API in the compiled code, suitable
"empty" C<perl_release> and C<perl_acquire> definitions will be provided.
This could be added to perl's C<CPPFLAGS> when configuring perl on
platforms that do not support threading at all for example.
=head1 AUTHOR
( run in 0.763 second using v1.01-cache-2.11-cpan-df04353d9ac )