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

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines