ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Compress-LZF/perlmulticore.h
(Generate patch)

Comparing Compress-LZF/perlmulticore.h (file contents):
Revision 1.2 by root, Sun Jun 28 17:40:38 2015 UTC vs.
Revision 1.4 by root, Sun Mar 3 11:33:55 2019 UTC

1/* 1/*
2 * Author: Marc A. Lehmann <xsthreadpool@schmorp.de> 2 * Author: Marc A. Lehmann <xsthreadpool@schmorp.de>
3 * License: public domain, or where this is not possible/at your option, 3 * License: public domain, or where this is not possible/at your option,
4 * CC0 (https://creativecommons.org/publicdomain/zero/1.0/) 4 * CC0 (https://creativecommons.org/publicdomain/zero/1.0/)
5 *
6 * Full documentation can be found at http://perlmulticore.schmorp.de/
7 * The newest version of this header can be downloaded from
8 * http://perlmulticore.schmorp.de/perlmulticore.h
5 */ 9 */
6 10
7#ifndef PERL_MULTICORE_H 11#ifndef PERL_MULTICORE_H
8#define PERL_MULTICORE_H 12#define PERL_MULTICORE_H
9 13
10/* 14/*
11 15
12=head1 NAME 16=head1 NAME
13 17
14perlmulticore.h - the Perl Multicore Specification and Implementation 18perlmulticore.h - implements the Perl Multicore Specification
15 19
16=head1 SYNOPSIS 20=head1 SYNOPSIS
17 21
18 #include "perlmultiore.h" 22 #include "perlmulticore.h"
19 23
20 // in your XS function: 24 // in your XS function:
21 25
22 perlinterp_release (); 26 perlinterp_release ();
23 do_the_C_thing (); 27 do_the_C_thing ();
24 perlinterp_acquire (); 28 perlinterp_acquire ();
25 29
26=head1 DESCRIPTION 30=head1 DESCRIPTION
27 31
28This header file implements a simple mechanism for XS modules to allow 32This documentation is the abridged version of the full documention at
29re-use of the perl interpreter for other threads while doing some lengthy 33L<http://perlmulticore.schmorp.de/>. It's recommended to go there instead
30operation, such as cryptography, SQL queries, disk I/O and so on. 34of reading this document.
31 35
32The design goals for this mechanism were to be simple to use, very 36This header file implements a very low overhead (both in code and runtime)
33efficient when not needed, low code and data size overhead and broad 37mechanism for XS modules to allow re-use of the perl interpreter for other
34applicability. 38threads while doing some lengthy operation, such as cryptography, SQL
39queries, disk I/O and so on.
35 40
36The newest version of this document can be found at
37L<http://pod.tst.eu/http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>.
38
39The nwest version of the header fgile itself, which 41The newest version of the header file itself, can be downloaded from
40includes this documentation, can be downloaded from 42L<http://perlmulticore.schmorp.de/perlmulticore.h>.
41L<http://cvs.schmorp.de/Coro-Multicore/perlmulticore.h>.
42 43
43=head1 HOW DO I USE THIS IN MY MODULES? 44=head1 HOW DO I USE THIS IN MY MODULES?
44 45
45The usage is very simple - you include this header file in your XS module. Then, before you 46The usage is very simple - you include this header file in your XS module. Then, before you
46do your lengthy operation, you release the perl interpreter: 47do your lengthy operation, you release the perl interpreter:
52 perlinterp_acquire (); 53 perlinterp_acquire ();
53 54
54And that's it. This doesn't load any modules and consists of only a few 55And that's it. This doesn't load any modules and consists of only a few
55machine instructions when no module to take advantage of it is loaded. 56machine instructions when no module to take advantage of it is loaded.
56 57
57Here is a simple example, an C<flock> wrapper implemented in XS. Unlike 58More documentation and examples can be found at the perl multicore site at
58perl's built-in C<flock>, it allows other threads (for example, those 59L<http://perlmulticore.schmorp.de>.
59provided by L<Coro>) to execute, instead of blocking the whole perl
60interpreter. For the sake of this example, it requires a file descriptor
61instead of a handle.
62
63 #include "perlmulticore.h" // this header file
64
65 // and in the XS portion
66 int flock (int fd, int operation)
67 CODE:
68 perlinterp_release ();
69 RETVAL = flock (fd, operation);
70 perlinterp_acquire ();
71 OUTPUT:
72 RETVAL
73
74Another example would be to modify L<DBD::mysql> to allow other
75threads to execute while executing SQL queries. One way to do this
76is find all C<mysql_st_internal_execute> and similar calls (such as
77C<mysql_st_internal_execute41>), and adorn them with release/acquire
78calls:
79
80 {
81 perlinterp_release ();
82 imp_sth->row_num= mysql_st_internal_execute(sth, ...);
83 perlinterp_acquire ();
84 }
85
86=head2 HOW ABOUT NOT-SO LONG WORK?
87
88Sometimes you don't know how long your code will take - in a compression
89library for example, compressing a few hundred Kilobyte of data can take
90a while, while 50 Bytes will compress so fast that even attempting to do
91something else could be more costly than just doing it.
92
93This is a very hard problem to solve. The best you can do at the moment is
94to release the perl interpreter only when you think the work to be done
95justifies the expense.
96
97As a rule of thumb, if you expect to need more than a few thousand cycles,
98you should release the interpreter, else you shouldn't. When in doubt,
99release.
100
101For example, in a compression library, you might want to do this:
102
103 if (bytes_to_be_compressed > 2000) perlinterp_release ();
104 do_compress (...);
105 if (bytes_to_be_compressed > 2000) perlinterp_acquire ();
106
107Make sure the if conditions are exactly the same and don't change, so you
108always call acquire when you release, and vice versa.
109
110When you don't have a handy indicator, you might still do something
111useful. For example, if you do some file locking with C<fcntl> and you
112expect the lock to be available immediately in most cases, you could try
113with C<F_SETLK> (which doesn't wait), and only release/wait/acquire when
114the lock couldn't be set:
115
116 int res = fcntl (fd, F_SETLK, &flock);
117
118 if (res)
119 {
120 // error, assume lock is held by another process and do it the slow way
121 perlinterp_release ();
122 res = fcntl (fd, F_SETLKW, &flock);
123 perlinterp_release ();
124 }
125 60
126=head1 THE HARD AND FAST RULES 61=head1 THE HARD AND FAST RULES
127 62
128As with everything, there are a number of rules to follow. 63As with everything, there are a number of rules to follow.
129 64
130=over 4 65=over 4
131 66
132=item I<Never> touch any perl data structures after calling C<perlinterp_release>. 67=item I<Never> touch any perl data structures after calling C<perlinterp_release>.
133 68
134Possibly the most important rule of them all, anything perl is
135completely off-limits after C<perlinterp_release>, until you call 69Anything perl is completely off-limits after C<perlinterp_release>, until
136C<perlinterp_acquire>, after which you can access perl stuff again. 70you call C<perlinterp_acquire>, after which you can access perl stuff
71again.
137 72
138That includes anything in the perl interpreter that you didn't prove to be 73That includes anything in the perl interpreter that you didn't prove to be
139safe, and didn't prove to be safe in older and future versions of perl: 74safe, and didn't prove to be safe in older and future versions of perl:
140global variables, local perl scalars, even if you are sure nobody accesses 75global variables, local perl scalars, even if you are sure nobody accesses
141them and you only try to "read" their value, and so on. 76them and you only try to "read" their value.
142
143If you need to access perl things, do it before releasing the
144interpreter with C<perlinterp_release>, or after acquiring it again with
145C<perlinterp_acquire>.
146 77
147=item I<Always> call C<perlinterp_release> and C<perlinterp_acquire> in pairs. 78=item I<Always> call C<perlinterp_release> and C<perlinterp_acquire> in pairs.
148 79
149For each C<perlinterp_release> call there must be a C<perlinterp_acquire> 80For each C<perlinterp_release> call there must be a C<perlinterp_acquire>
150call. They don't have to be in the same function, and you can have 81call. They don't have to be in the same function, and you can have
151multiple calls to them, as long as every C<perlinterp_release> call is 82multiple calls to them, as long as every C<perlinterp_release> call is
152followed by exactly one C<perlinterp_acquire> call. 83followed by exactly one C<perlinterp_acquire> call at runtime.
153
154For example., this would be fine:
155
156 perlinterp_release ();
157
158 if (!function_that_fails_with_0_return_value ())
159 {
160 perlinterp_acquire ();
161 croak ("error");
162 // croak doesn't return
163 }
164
165 perlinterp_acquire ();
166 // do other stuff
167 84
168=item I<Never> nest calls to C<perlinterp_release> and C<perlinterp_acquire>. 85=item I<Never> nest calls to C<perlinterp_release> and C<perlinterp_acquire>.
169 86
170That simply means that after calling C<perlinterp_release>, you must 87That simply means that after calling C<perlinterp_release>, you must
171call C<perlinterp_acquire> before calling C<perlinterp_release> 88call C<perlinterp_acquire> before calling C<perlinterp_release>
172again. Likewise, after C<perlinterp_acquire>, you can call 89again. Likewise, after C<perlinterp_acquire>, you can call
173C<perlinterp_release> but not another C<perlinterp_acquire>. 90C<perlinterp_release> but not another C<perlinterp_acquire>.
174 91
175=item I<Always> call C<perlinterp_release> first. 92=item I<Always> call C<perlinterp_release> first.
176 93
177Also simple: you I<must not> call C<perlinterp_acquire> without having 94You I<must not> call C<perlinterp_acquire> without having called
178called C<perlinterp_release> before. 95C<perlinterp_release> before.
179 96
180=item I<Never> underestimate threads. 97=item I<Never> underestimate threads.
181 98
182While it's easy to add parallel execution ability to your XS module, it 99While it's easy to add parallel execution ability to your XS module, it
183doesn't mean it is safe. After you release the perl interpreter, it's 100doesn't mean it is safe. After you release the perl interpreter, it's
186code must be thread safe, and if you use any library, that library must be 103code must be thread safe, and if you use any library, that library must be
187thread-safe, too. 104thread-safe, too.
188 105
189Always assume that the code between C<perlinterp_release> and 106Always assume that the code between C<perlinterp_release> and
190C<perlinterp_acquire> is executed in parallel on multiple CPUs at the same 107C<perlinterp_acquire> is executed in parallel on multiple CPUs at the same
191time. If your code can't cope with that, you could consider using a mutex 108time.
192to only allow one such execution, which is still better than blocking
193everybody else from doing anything:
194
195 static pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER;
196
197 perlinterp_release ();
198 pthread_mutex_lock (&my_mutex);
199 do_your_non_thread_safe_thing ();
200 pthread_mutex_unlock (&my_mutex);
201 perlinterp_acquire ();
202
203This isn't as trivial as it looks though, as you need to find out which
204threading system is in use (with L<Coro::Multicore>, it currently is
205always pthreads).
206
207=item I<Don't> get confused by having to release first.
208
209In many real world scenarios, you acquire a resource, do something, then
210release it again. Don't let this confuse you, with this, you already own
211the resource (the perl interpreter) so you have to I<release> first, and
212I<acquire> it again later, not the other way around.
213 109
214=back 110=back
215 111
216 112
217=head1 DESIGN PRINCIPLES 113=head1 DISABLING PERL MULTICORE AT COMPILE TIME
218 114
219This section discusses how the design goals were reached (you be the 115You can disable the complete perl multicore API by defining the
220judge), how it is implemented, and what overheads this implies. 116symbol C<PERL_MULTICORE_DISABLE> to C<1> (e.g. by specifying
117F<-DPERL_MULTICORE_DISABLE> as compiler argument).
221 118
222=over 4 119This could be added to perl's C<CPPFLAGS> when configuring perl on
120platforms that do not support threading at all for example.
223 121
224=item Simple to Use
225
226All you have to do is identify the place in your existing code where you
227stop touching perl stuff, do your actual work, and start touching perl
228stuff again.
229
230Then slap C<perlinterp_release ()> and C<perlinterp_acquire ()> around the
231actual work code.
232
233You have to include F<perlmulticore.h> and distribute it with your XS
234code, but all these things border on the trivial.
235
236=item Very Efficient
237
238The definition for C<perlinterp_release> and C<perlinterp_release> is very
239short:
240
241 #define perlinterp_release() perl_multicore_api->pmapi_release ()
242 #define perlinterp_acquire() perl_multicore_api->pmapi_acquire ()
243
244Both are macros that read a pointer from memory (perl_multicore_api),
245dereference a function pointer stored at that place, and call the
246function, which takes no arguments and returns nothing.
247
248The first call to C<perlinterp_release> will check for the presence
249of any supporting module, and if none is loaded, will create a dummy
250implementation where both C<pmapi_release> and C<pmapi_acquire> execute
251this function:
252
253 static void perl_multicore_nop (void) { }
254
255So in the case of no magical module being loaded, all calls except the
256first are two memory accesses and a predictable function call of an empty
257function.
258
259Of course, the overhead is much higher when these functions actually
260implement anything useful, but you always get what you pay for.
261
262With L<Coro::Multicore>, every release/acquire involves two pthread
263switches, two coro thread switches, a bunch of syscalls, and sometimes
264interacting with the event loop.
265
266A dedicated thread pool such as the one L<IO::AIO> uses could reduce
267these overheads, and would also reduce the dependencies (L<AnyEvent> is a
268smaller and more portable dependency than L<Coro>), but it would require a
269lot more work on the side of the module author wanting to support it than
270this solution.
271
272=item Low Code and Data Size Overhead
273
274On a 64 bit system, F<perlmulticore.h> uses exactly C<8> octets (one
275pointer) of your data segment, to store the C<perl_multicore_api>
276pointer. In addition it creates a C<16> octet perl string to store the
277function pointers in, and stores it in a hash provided by perl for this
278purpose.
279
280This is pretty much the equivalent of executing this code:
281
282 $existing_hash{perl_multicore_api} = "123456781234567812345678";
283
284And that's it, which is, as I think, indeed very little.
285
286As for code size, on my amd64 system, every call to C<perlinterp_release>
287or C<perlinterp_acquire> results in a variation of the following 9-10
288octet sequence:
289
290 150> mov 0x200f23(%rip),%rax # <perl_multicore_api>
291 157> callq *0x8(%rax)
292
293The biggest part if the initialisation code, which consists of 11 lines of
294typical XS code. On my system, all the code in F<perlmulticore.h> compiles
295to less than 160 octets of read-only data.
296
297=item Broad Applicability
298
299While there are alternative ways to achieve the goal of parallel execution
300with threads that might be more efficient, this mechanism was chosen
301because it is very simple to retrofit existing modules with it, and it
302
303The design goals for this mechanism were to be simple to use, very
304efficient when not needed, low code and data size overhead and broad
305applicability.
306
307=back
308 122
309=head1 AUTHOR 123=head1 AUTHOR
310 124
311 Marc A. Lehmann <perlmulticore@schmorp.de> 125 Marc A. Lehmann <perlmulticore@schmorp.de>
126 http://perlmulticore.schmorp.de/
312 127
313=head1 LICENSE 128=head1 LICENSE
314 129
315The F<perlmulticore.h> is put into the public domain. Where this is legally 130The F<perlmulticore.h> header file is put into the public
131domain. Where this is legally not possible, or at your
316not possible, or at your option, it can be licensed under creativecommons 132option, it can be licensed under creativecommons CC0
317CC0 license: L<https://creativecommons.org/publicdomain/zero/1.0/>. 133license: L<https://creativecommons.org/publicdomain/zero/1.0/>.
318 134
319=cut 135=cut
136
320*/ 137*/
321 138
139#define PERL_MULTICORE_MAJOR 1 /* bumped on incompatible changes */
140#define PERL_MULTICORE_MINOR 1 /* bumped on every change */
141
142#if PERL_MULTICORE_DISABLE
143
144#define perlinterp_release() do { } while (0)
145#define perlinterp_acquire() do { } while (0)
146
147#else
148
149START_EXTERN_C
150
151/* this struct is shared between all modules, and currently */
152/* contain only the two function pointers for release/acquire */
322struct perl_multicore_api 153struct perl_multicore_api
323{ 154{
324 void (*pmapi_release)(void); 155 void (*pmapi_release)(void);
325 void (*pmapi_acquire)(void); 156 void (*pmapi_acquire)(void);
326}; 157};
327 158
328static void perl_multicore_init (void); 159static void perl_multicore_init (void);
329 160
330const struct perl_multicore_api perl_multicore_api_init = { perl_multicore_init, abort }; 161static const struct perl_multicore_api perl_multicore_api_init
162 = { perl_multicore_init, 0 };
331 163
332static struct perl_multicore_api *perl_multicore_api 164static struct perl_multicore_api *perl_multicore_api
333 = (struct perl_multicore_api *)&perl_multicore_api_init; 165 = (struct perl_multicore_api *)&perl_multicore_api_init;
334 166
335#define perlinterp_release() perl_multicore_api->pmapi_release () 167#define perlinterp_release() perl_multicore_api->pmapi_release ()
336#define perlinterp_acquire() perl_multicore_api->pmapi_acquire () 168#define perlinterp_acquire() perl_multicore_api->pmapi_acquire ()
337 169
170/* this is the release/acquire implementation used as fallback */
338static void 171static void
339perl_multicore_nop (void) 172perl_multicore_nop (void)
340{ 173{
341} 174}
342 175
176static const char perl_multicore_api_key[] = "perl_multicore_api";
177
178/* this is the initial implementation of "release" - it initialises */
179/* the api and then calls the real release function */
343static void 180static void
344perl_multicore_init (void) 181perl_multicore_init (void)
345{ 182{
346 dTHX; 183 dTHX;
347 184
348 /* check for existing API struct in PL_modglobal */ 185 /* check for existing API struct in PL_modglobal */
349 SV **api_svp = hv_fetch (PL_modglobal, "perl_multicore_api", sizeof ("perl_multicore_api") - 1, 1); 186 SV **api_svp = hv_fetch (PL_modglobal, perl_multicore_api_key,
187 sizeof (perl_multicore_api_key) - 1, 1);
350 188
351 if (SvPOKp (*api_svp)) 189 if (SvPOKp (*api_svp))
352 perl_multicore_api = (struct perl_multicore_api *)SvPVX (*api_svp); /* we have one, use the existing one */ 190 perl_multicore_api = (struct perl_multicore_api *)SvPVX (*api_svp); /* we have one, use the existing one */
353 else 191 else
354 { 192 {
355 /* create a new one with a dummy nop implementation */ 193 /* create a new one with a dummy nop implementation */
194 #ifdef NEWSV
356 SV *api_sv = NEWSV (0, sizeof (*perl_multicore_api)); 195 SV *api_sv = NEWSV (0, sizeof (*perl_multicore_api));
196 #else
197 SV *api_sv = newSV ( sizeof (*perl_multicore_api));
198 #endif
357 SvCUR_set (api_sv, sizeof (*perl_multicore_api)); 199 SvCUR_set (api_sv, sizeof (*perl_multicore_api));
358 SvPOK_only (api_sv); 200 SvPOK_only (api_sv);
359 perl_multicore_api = (struct perl_multicore_api *)SvPVX (api_sv); 201 perl_multicore_api = (struct perl_multicore_api *)SvPVX (api_sv);
360 perl_multicore_api->pmapi_release = 202 perl_multicore_api->pmapi_release =
361 perl_multicore_api->pmapi_acquire = perl_multicore_nop; 203 perl_multicore_api->pmapi_acquire = perl_multicore_nop;
364 206
365 /* call the real (or dummy) implementation now */ 207 /* call the real (or dummy) implementation now */
366 perlinterp_release (); 208 perlinterp_release ();
367} 209}
368 210
211END_EXTERN_C
212
369#endif 213#endif
214
215#endif
216

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines