line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Parallel::Loops; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION='0.09'; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# For Tie::ExtraHash - This was the earliest perl version in which I found this |
6
|
|
|
|
|
|
|
# class |
7
|
16
|
|
|
16
|
|
850688
|
use 5.008; |
|
16
|
|
|
|
|
48
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 NAME |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
Parallel::Loops - Execute loops using parallel forked subprocesses |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=encoding utf-8 |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 SYNOPSIS |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
use Parallel::Loops; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $maxProcs = 5; |
20
|
|
|
|
|
|
|
my $pl = Parallel::Loops->new($maxProcs); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my @parameters = ( 0 .. 9 ); |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
# We want to perform some hefty calculation for each @input and |
25
|
|
|
|
|
|
|
# store each calculation's result in %output. For that reason, we |
26
|
|
|
|
|
|
|
# "tie" %output, so that changes to %output in any child process |
27
|
|
|
|
|
|
|
# (see below) are automatically transfered and updated in the |
28
|
|
|
|
|
|
|
# parent also. |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my %returnValues; |
31
|
|
|
|
|
|
|
$pl->share( \%returnValues ); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
$pl->foreach( \@parameters, sub { |
34
|
|
|
|
|
|
|
# This sub "magically" executed in parallel forked child |
35
|
|
|
|
|
|
|
# processes |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# Lets just create a simple example, but this could be a |
38
|
|
|
|
|
|
|
# massive calculation that will be parallelized, so that |
39
|
|
|
|
|
|
|
# $maxProcs different processes are calculating sqrt |
40
|
|
|
|
|
|
|
# simultaneously for different values of $_ on different CPUs |
41
|
|
|
|
|
|
|
# (Do see 'Performance' / 'Properties of the loop body' below) |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
$returnValues{$_} = sqrt($_); |
44
|
|
|
|
|
|
|
}); |
45
|
|
|
|
|
|
|
foreach (@parameters) { |
46
|
|
|
|
|
|
|
printf "i: %d sqrt(i): %f\n", $_, $returnValues{$_}; |
47
|
|
|
|
|
|
|
} |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
You can also use @arrays instead of %hashes, and/or while loops |
50
|
|
|
|
|
|
|
instead of foreach: |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
my @returnValues; |
53
|
|
|
|
|
|
|
$pl->share(\@returnValues); |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
my $i = 0; |
56
|
|
|
|
|
|
|
$pl->while ( sub { $i++ < 10 }, sub { |
57
|
|
|
|
|
|
|
# This sub "magically" executed in parallel forked |
58
|
|
|
|
|
|
|
# child processes |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
push @returnValues, [ $i, sqrt($i) ]; |
61
|
|
|
|
|
|
|
}); |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
And you can have both foreach and while return values so that $pl->share() |
64
|
|
|
|
|
|
|
isn't required at all: |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
my $maxProcs = 5; |
67
|
|
|
|
|
|
|
my $pl = Parallel::Loops->new($maxProcs); |
68
|
|
|
|
|
|
|
my %returnValues = $pl->foreach( [ 0..9 ], sub { |
69
|
|
|
|
|
|
|
# Again, this is executed in a forked child |
70
|
|
|
|
|
|
|
$_ => sqrt($_); |
71
|
|
|
|
|
|
|
}); |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=head1 DESCRIPTION |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
Often a loop performs calculations where each iteration of the loop |
76
|
|
|
|
|
|
|
does not depend on the previous iteration, and the iterations really |
77
|
|
|
|
|
|
|
could be carried out in any order. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
This module allows you to run such loops in parallel using all the |
80
|
|
|
|
|
|
|
CPUs at your disposal. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
Return values are automatically transfered from children to parents via |
83
|
|
|
|
|
|
|
%hashes or @arrays, that have explicitly been configured for that sort |
84
|
|
|
|
|
|
|
of sharing via $pl->share(). Hashes will transfer keys that are |
85
|
|
|
|
|
|
|
set in children (but not cleared or unset), and elements that are |
86
|
|
|
|
|
|
|
pushed to @arrays in children are pushed to the parent @array too (but |
87
|
|
|
|
|
|
|
note that the order is not guaranteed to be the same as it would have |
88
|
|
|
|
|
|
|
been if done all in one process, since there is no way of knowing |
89
|
|
|
|
|
|
|
which child would finish first!) |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
If you can see past the slightly awkward syntax, you're basically |
92
|
|
|
|
|
|
|
getting foreach and while loops that can run in parallel without |
93
|
|
|
|
|
|
|
having to bother with fork, pipes, signals etc. This is all handled |
94
|
|
|
|
|
|
|
for you by this module. |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head2 foreach loop |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
$pl->foreach($arrayRef, $childBodySub) |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Runs $childBodySub->() with $_ set foreach element in @$arrayRef, except that |
101
|
|
|
|
|
|
|
$childBodySub is run in a forked child process to obtain parallelism. |
102
|
|
|
|
|
|
|
Essentially, this does something conceptually similar to: |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
foreach(@$arrayRef) { |
105
|
|
|
|
|
|
|
$childBodySub->(); |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
Any setting of hash keys or pushing to arrays that have been set with |
109
|
|
|
|
|
|
|
$pl->share() will automagically appear in the hash or array in the parent |
110
|
|
|
|
|
|
|
process. |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
If you like loop variables, you can run it like so: |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
$pl->foreach( \@input, sub { |
115
|
|
|
|
|
|
|
my $i = $_; |
116
|
|
|
|
|
|
|
.. bla, bla, bla ... $output{$i} = sqrt($i); |
117
|
|
|
|
|
|
|
} |
118
|
|
|
|
|
|
|
); |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
=head2 while loop |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
$pl->while($conditionSub, $childBodySub [,$finishSub]) |
123
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
Essentially, this does something conceptually similar to: |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
while($conditionSub->()) { |
127
|
|
|
|
|
|
|
$childBodySub->(); |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
except that $childBodySub->() is executed in a forked child process. |
131
|
|
|
|
|
|
|
Return values are transfered via share() like in L above. |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
=head3 While loops must affect condition outside $childBodySub |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
Note that incrementing $i in the $childBodySub like in this example |
136
|
|
|
|
|
|
|
B: |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
$pl->while( sub { $i < 5 }, |
139
|
|
|
|
|
|
|
sub { |
140
|
|
|
|
|
|
|
$output{$i} = sqrt($i); |
141
|
|
|
|
|
|
|
# Won't work! |
142
|
|
|
|
|
|
|
$i++ |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
); |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
Because $childBodySub is executed in a child, and so while $i would |
147
|
|
|
|
|
|
|
be incremented in the child, that change would not make it to the |
148
|
|
|
|
|
|
|
parent, where $conditionSub is evaluated. The changes that make |
149
|
|
|
|
|
|
|
$conditionSub return false eventually I take place outside |
150
|
|
|
|
|
|
|
the $childBodySub so it is executed in the parent. (Adhering to |
151
|
|
|
|
|
|
|
the parallel principle that one iteration may not affect any other |
152
|
|
|
|
|
|
|
iterations - including whether to run them or not) |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head3 Optional $finishSub parameter |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
In order to track progress, an optional C<$finishSub> can be provided. It will |
157
|
|
|
|
|
|
|
be called whenever a child finishes. The return value from the C<$conditionSub> |
158
|
|
|
|
|
|
|
is remembered and provided to the C<$finishSub> as a reference: |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
my $i = 0; |
161
|
|
|
|
|
|
|
my %returnValues = $pl->while ( |
162
|
|
|
|
|
|
|
sub { $i++ < 10 ? $i : 0 }, |
163
|
|
|
|
|
|
|
sub { |
164
|
|
|
|
|
|
|
return ($i, sqrt($i)); |
165
|
|
|
|
|
|
|
}, |
166
|
|
|
|
|
|
|
sub { |
167
|
|
|
|
|
|
|
my ($i) = @_; |
168
|
|
|
|
|
|
|
printf "Child %d has finished\n", $i; |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
); |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
=head2 share |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
$pl->share(\%output, \@output, ...) |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
Each of the arguments to share() are instrumented, so that when a |
177
|
|
|
|
|
|
|
hash key is set or array element pushed in a child, this is transfered |
178
|
|
|
|
|
|
|
to the parent's hash or array automatically when a child is finished. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
B Only keys being set like C<$hash{'key'} = 'value'> and |
181
|
|
|
|
|
|
|
arrays elements being pushed like C will be transfered to |
182
|
|
|
|
|
|
|
the parent. Unsetting keys, or setting particluar array elements with |
183
|
|
|
|
|
|
|
$array[3]='value' will be lost if done in the children. Also, if two different |
184
|
|
|
|
|
|
|
children set a value for the same key, a random one of them will be seen by the |
185
|
|
|
|
|
|
|
parent. |
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
In the parent process all the %hashes and @arrays are full-fledged, and you can |
188
|
|
|
|
|
|
|
use all operations. But only these mentioned operations in the child processes |
189
|
|
|
|
|
|
|
make it back to the parent. |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=head3 Array element sequence not defined |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
Note that when using share() for @returnValue arrays, the sequence of elements |
194
|
|
|
|
|
|
|
in @returnValue is not guaranteed to be the same as you'd see with a normal |
195
|
|
|
|
|
|
|
sequential while or foreach loop, since the calculations are done in parallel |
196
|
|
|
|
|
|
|
and the children may end in an unexpected sequence. But if you don't really |
197
|
|
|
|
|
|
|
care about the order of elements in the @returnValue array then share-ing an |
198
|
|
|
|
|
|
|
array can be useful and fine. |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
If you need to be able to determine which iteration generated what output, use |
201
|
|
|
|
|
|
|
a hash instead. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head2 Recursive forking is possible |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Note that no check is performed for recursive forking: If the main |
206
|
|
|
|
|
|
|
process encouters a loop that it executes in parallel, and the |
207
|
|
|
|
|
|
|
execution of the loop in child processes also encounters a parallel |
208
|
|
|
|
|
|
|
loop, these will also be forked, and you'll essentially have |
209
|
|
|
|
|
|
|
$maxProcs^2 running processes. It wouldn't be too hard to implement |
210
|
|
|
|
|
|
|
such a check (either inside or outside this package). |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
=head1 Exception/Error Handling / Dying |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
If you want some measure of exception handling you can use eval in the child |
215
|
|
|
|
|
|
|
like this: |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
my %errors; |
218
|
|
|
|
|
|
|
$pl->share( \%errors ); |
219
|
|
|
|
|
|
|
my %returnValues = $pl->foreach( [ 0..9 ], sub { |
220
|
|
|
|
|
|
|
# Again, this is executed in a forked child |
221
|
|
|
|
|
|
|
eval { |
222
|
|
|
|
|
|
|
die "Bogus error" |
223
|
|
|
|
|
|
|
if $_ == 3; |
224
|
|
|
|
|
|
|
$_ => sqrt($_); |
225
|
|
|
|
|
|
|
}; |
226
|
|
|
|
|
|
|
if ($@) { |
227
|
|
|
|
|
|
|
$errors{$_} = $@; |
228
|
|
|
|
|
|
|
} |
229
|
|
|
|
|
|
|
}); |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# Now test %errors. $errors{3} should exist as the only element |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
Also, be sure not to call exit() in the child. That will just exit the child |
234
|
|
|
|
|
|
|
and that doesn't work. Right now, exit just makes the parent fail no-so-nicely. |
235
|
|
|
|
|
|
|
Patches to this that handle exit somehow are welcome. |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=head1 Performance |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
=head2 Properties of the loop body |
240
|
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
Keep in mind that a child process is forked every time while or foreach calls |
242
|
|
|
|
|
|
|
the provided sub. For use of Parallel::Loops to make sense, each invocation |
243
|
|
|
|
|
|
|
needs to actually do some serious work for the performance gain of parallel |
244
|
|
|
|
|
|
|
execution to outweigh the overhead of forking and communicating between the |
245
|
|
|
|
|
|
|
processes. So while sqrt() in the example above is simple, it will actually be |
246
|
|
|
|
|
|
|
slower than just running it in a standard foreach loop because of the overhead. |
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
Also, if each loop sub returns a massive amount of data, this needs to be |
249
|
|
|
|
|
|
|
communicated back to the parent process, and again that could outweigh parallel |
250
|
|
|
|
|
|
|
performance gains unless the loop body does some heavy work too. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
=head2 Linux and Windows Comparison |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
On the same VMware host, I ran this script in Debian Linux and Windows XP |
255
|
|
|
|
|
|
|
virtual machines respectively. The script runs a "no-op" sub in 1000 child |
256
|
|
|
|
|
|
|
processes two in parallel at a time |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
my $pl = Parallel::Loops->new(2); |
259
|
|
|
|
|
|
|
$pl->foreach( [1..1000], sub {} ); |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
For comparison, that took: |
262
|
|
|
|
|
|
|
|
263
|
|
|
|
|
|
|
7.3 seconds on Linux |
264
|
|
|
|
|
|
|
43 seconds on Strawberry Perl for Windows |
265
|
|
|
|
|
|
|
240 seconds on Cygwin for Windows |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=head2 fork() e.g. on Windows |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
On some platforms the fork() is emulated. Be sure to read perlfork. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=head2 Temporary files unless select() works - e.g. on Windows |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
E.g. on Windows, select is only supported for sockets, and not for pipes. So we |
274
|
|
|
|
|
|
|
use temporary files to store the information sent from the child to the parent. |
275
|
|
|
|
|
|
|
This adds a little extra overhead. See perlport for other platforms where there |
276
|
|
|
|
|
|
|
are problems with select. Parallel::Loops tests for a working select() and uses |
277
|
|
|
|
|
|
|
temporary files otherwise. |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=head1 SEE ALSO |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
This module uses fork(). ithreads could have been possible too, but was not |
282
|
|
|
|
|
|
|
chosen. You may want to check out: |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
When to use forks, when to use threads ...? |
285
|
|
|
|
|
|
|
L |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
The forks module (not used here) |
288
|
|
|
|
|
|
|
L |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
threads in perlthrtut |
291
|
|
|
|
|
|
|
L |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
I believe this is the only dependency that isn't part of core perl: |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
use Parallel::ForkManager; |
298
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
These should all be in perl's core: |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
use Storable; |
302
|
|
|
|
|
|
|
use IO::Handle; |
303
|
|
|
|
|
|
|
use Tie::Array; |
304
|
|
|
|
|
|
|
use Tie::Hash; |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=head1 BUGS / ENHANCEMENTS |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
No bugs are known at the moment. Send any reports to peter@morch.com. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
Enhancements: |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
Optionally prevent recursive forking: If a forked child encounters a |
313
|
|
|
|
|
|
|
Parallel::Loop it should be possible to prevent that Parallel::Loop instance to |
314
|
|
|
|
|
|
|
also create forks. |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
Determine the number of CPUs so that new()'s $maxProcs parameter can be |
317
|
|
|
|
|
|
|
optional. Could use e.g. Sys::Sysconf, UNIX::Processors or Sys::CPU. |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
Maybe use function prototypes (see Prototypes under perldoc perlsub). |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
Then we could do something like |
322
|
|
|
|
|
|
|
|
323
|
|
|
|
|
|
|
pl_foreach @input { |
324
|
|
|
|
|
|
|
yada($_); |
325
|
|
|
|
|
|
|
}; |
326
|
|
|
|
|
|
|
or |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
pl_foreach $pl @input { |
329
|
|
|
|
|
|
|
yada($_); |
330
|
|
|
|
|
|
|
}; |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
instead of |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
$pl->foreach(\@input, sub { |
335
|
|
|
|
|
|
|
yada($_); |
336
|
|
|
|
|
|
|
}); |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
and so on, where the first suggestion above means global variables (yikes!). |
339
|
|
|
|
|
|
|
Unfortunately, methods aren't supported by prototypes, so this will never be |
340
|
|
|
|
|
|
|
posssible: |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
$pl->foreach @input { |
343
|
|
|
|
|
|
|
yada($_); |
344
|
|
|
|
|
|
|
}; |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
An alternative pointed out by the perlmonks chatterbox could be to use |
347
|
|
|
|
|
|
|
L "if I can stand |
348
|
|
|
|
|
|
|
pain". |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head1 SOURCE REPOSITORY |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
See the git source on github L |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
=head1 COPYRIGHT |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
Copyright (c) 2008 Peter Valdemar Mørch |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
All right reserved. This program is free software; you can redistribute it |
359
|
|
|
|
|
|
|
and/or modify it under the same terms as Perl itself. |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
=head1 AUTHOR |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Peter Valdemar Mørch |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=cut |
366
|
|
|
|
|
|
|
|
367
|
16
|
|
|
16
|
|
80
|
use strict; |
|
16
|
|
|
|
|
32
|
|
|
16
|
|
|
|
|
400
|
|
368
|
16
|
|
|
16
|
|
80
|
use warnings; |
|
16
|
|
|
|
|
32
|
|
|
16
|
|
|
|
|
896
|
|
369
|
|
|
|
|
|
|
|
370
|
16
|
|
|
16
|
|
96
|
use Carp; |
|
16
|
|
|
|
|
16
|
|
|
16
|
|
|
|
|
800
|
|
371
|
16
|
|
|
16
|
|
6960
|
use IO::Handle; |
|
16
|
|
|
|
|
78160
|
|
|
16
|
|
|
|
|
576
|
|
372
|
16
|
|
|
16
|
|
5952
|
use IO::Select; |
|
16
|
|
|
|
|
20576
|
|
|
16
|
|
|
|
|
624
|
|
373
|
16
|
|
|
16
|
|
9280
|
use File::Temp qw(tempfile); |
|
16
|
|
|
|
|
183648
|
|
|
16
|
|
|
|
|
800
|
|
374
|
16
|
|
|
16
|
|
7984
|
use Storable; |
|
16
|
|
|
|
|
40048
|
|
|
16
|
|
|
|
|
736
|
|
375
|
16
|
|
|
16
|
|
7488
|
use Parallel::ForkManager; |
|
16
|
|
|
|
|
716800
|
|
|
16
|
|
|
|
|
21472
|
|
376
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub new { |
378
|
16
|
|
|
16
|
0
|
1792
|
my ($class, $maxProcs, %options) = @_; |
379
|
16
|
|
|
|
|
64
|
my $self = { |
380
|
|
|
|
|
|
|
maxProcs => $maxProcs, |
381
|
|
|
|
|
|
|
shareNr => 0, |
382
|
|
|
|
|
|
|
workingSelect => testWorkingSelect(), |
383
|
|
|
|
|
|
|
}; |
384
|
16
|
|
|
|
|
96
|
return bless $self, $class; |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
|
387
|
|
|
|
|
|
|
sub testWorkingSelect { |
388
|
16
|
|
|
16
|
0
|
96
|
my $reader = IO::Handle->new(); |
389
|
16
|
|
|
|
|
368
|
my $writer = IO::Handle->new(); |
390
|
16
|
50
|
|
|
|
1248
|
pipe( $reader, $writer ) |
391
|
|
|
|
|
|
|
or die "Couldn't open a pipe"; |
392
|
16
|
|
|
|
|
192
|
$writer->autoflush(1); |
393
|
16
|
|
|
|
|
848
|
my $select = IO::Select->new(); |
394
|
16
|
|
|
|
|
192
|
$select->add($reader); |
395
|
16
|
|
|
|
|
960
|
print $writer "test\n"; |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# There should be data right away, so lets not risk blocking if it is |
398
|
|
|
|
|
|
|
# unreliable |
399
|
16
|
|
|
|
|
112
|
my @handles = $select->can_read(0); |
400
|
16
|
|
|
|
|
640
|
my $working = (scalar(@handles) == 1); |
401
|
|
|
|
|
|
|
|
402
|
16
|
|
|
|
|
144
|
close $reader; |
403
|
16
|
|
|
|
|
192
|
close $writer; |
404
|
|
|
|
|
|
|
|
405
|
16
|
|
|
|
|
208
|
return $working; |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
sub share { |
409
|
64
|
|
|
64
|
1
|
23152
|
my ($self, @tieRefs) = @_; |
410
|
64
|
|
|
|
|
112
|
foreach my $ref (@tieRefs) { |
411
|
64
|
100
|
|
|
|
208
|
if (ref $ref eq 'HASH') { |
|
|
100
|
|
|
|
|
|
412
|
16
|
|
|
|
|
32
|
my %initialContents = %$ref; |
413
|
|
|
|
|
|
|
# $storage will point to the Parallel::Loops::TiedHash object |
414
|
16
|
|
|
|
|
32
|
my $storage; |
415
|
16
|
|
|
|
|
96
|
tie %$ref, 'Parallel::Loops::TiedHash', $self, \$storage; |
416
|
16
|
|
|
|
|
144
|
%$ref = %initialContents; |
417
|
16
|
|
|
|
|
128
|
push @{$$self{tieObjects}}, $storage; |
|
16
|
|
|
|
|
48
|
|
418
|
16
|
|
|
|
|
32
|
push @{$$self{tieHashes}}, [$$self{shareNr}, $ref]; |
|
16
|
|
|
|
|
64
|
|
419
|
|
|
|
|
|
|
} elsif (ref $ref eq 'ARRAY') { |
420
|
16
|
|
|
|
|
48
|
my @initialContents = @$ref; |
421
|
|
|
|
|
|
|
# $storage will point to the Parallel::Loops::TiedArray object |
422
|
16
|
|
|
|
|
16
|
my $storage; |
423
|
16
|
|
|
|
|
80
|
tie @$ref, 'Parallel::Loops::TiedArray', $self, \$storage; |
424
|
16
|
|
|
|
|
64
|
@$ref = @initialContents; |
425
|
16
|
|
|
|
|
32
|
push @{$$self{tieObjects}}, $storage; |
|
16
|
|
|
|
|
32
|
|
426
|
16
|
|
|
|
|
16
|
push @{$$self{tieArrays}}, [$$self{shareNr}, $ref]; |
|
16
|
|
|
|
|
48
|
|
427
|
|
|
|
|
|
|
} else { |
428
|
32
|
|
|
|
|
2880
|
croak "Only unblessed hash and array refs are supported by share"; |
429
|
|
|
|
|
|
|
} |
430
|
32
|
|
|
|
|
64
|
$$self{shareNr}++; |
431
|
|
|
|
|
|
|
} |
432
|
|
|
|
|
|
|
} |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub in_child { |
435
|
254
|
|
|
254
|
0
|
572
|
my ($self) = @_; |
436
|
254
|
|
66
|
|
|
2238
|
return $$self{forkManager} && $$self{forkManager}->is_child; |
437
|
|
|
|
|
|
|
} |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
sub readChangesFromChild { |
440
|
112
|
|
|
112
|
0
|
1182
|
my ($self, $childRdr, $childFinishSub) = @_; |
441
|
|
|
|
|
|
|
|
442
|
112
|
|
|
|
|
467
|
my $childOutput; |
443
|
|
|
|
|
|
|
|
444
|
112
|
100
|
|
|
|
654
|
if ($$self{workingSelect}) { |
445
|
99
|
|
|
|
|
816
|
local $/; |
446
|
99
|
|
|
|
|
4226
|
$childOutput = <$childRdr>; |
447
|
|
|
|
|
|
|
} else { |
448
|
13
|
|
|
|
|
578
|
my $filename = <$childRdr>; |
449
|
13
|
50
|
|
|
|
800
|
open my $in, $filename |
450
|
|
|
|
|
|
|
or die "Couldn't open $filename"; |
451
|
13
|
|
|
|
|
105
|
binmode $in; |
452
|
|
|
|
|
|
|
{ |
453
|
13
|
|
|
|
|
45
|
local $/; |
|
13
|
|
|
|
|
143
|
|
454
|
13
|
|
|
|
|
407
|
$childOutput = <$in>; |
455
|
|
|
|
|
|
|
} |
456
|
13
|
|
|
|
|
177
|
close $in; |
457
|
13
|
|
|
|
|
967
|
unlink $filename; |
458
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
} |
460
|
112
|
50
|
|
|
|
666
|
die "Error getting result contents from child" |
461
|
|
|
|
|
|
|
if $childOutput eq ''; |
462
|
|
|
|
|
|
|
|
463
|
112
|
|
|
|
|
204
|
my @output; |
464
|
112
|
|
|
|
|
411
|
eval { |
465
|
112
|
|
|
|
|
172
|
@output = @{ Storable::thaw($childOutput) }; |
|
112
|
|
|
|
|
1631
|
|
466
|
|
|
|
|
|
|
}; |
467
|
112
|
50
|
|
|
|
7506
|
if ($@) { |
468
|
0
|
|
|
|
|
0
|
die "Error interpreting result from child: $@"; |
469
|
|
|
|
|
|
|
} |
470
|
112
|
|
|
|
|
364
|
my $error = shift @output; |
471
|
112
|
|
|
|
|
185
|
my $retval = shift @output; |
472
|
|
|
|
|
|
|
|
473
|
112
|
|
|
|
|
167
|
foreach my $set (@{$$self{tieHashes}}) { |
|
112
|
|
|
|
|
390
|
|
474
|
112
|
|
|
|
|
555
|
my ($outputNr, $h) = @$set; |
475
|
112
|
|
|
|
|
207
|
foreach my $k (keys %{$output[$outputNr]}) { |
|
112
|
|
|
|
|
406
|
|
476
|
112
|
|
|
|
|
1736
|
$$h{$k} = $output[$outputNr]{$k}; |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
} |
479
|
112
|
|
|
|
|
193
|
foreach my $set (@{$$self{tieArrays}}) { |
|
112
|
|
|
|
|
293
|
|
480
|
112
|
|
|
|
|
518
|
my ($outputNr, $a) = @$set; |
481
|
112
|
|
|
|
|
230
|
foreach my $v (@{$output[$outputNr]}) { |
|
112
|
|
|
|
|
235
|
|
482
|
112
|
|
|
|
|
1334
|
push @$a, $v; |
483
|
|
|
|
|
|
|
} |
484
|
|
|
|
|
|
|
} |
485
|
112
|
50
|
|
|
|
250
|
if ($error) { |
486
|
0
|
|
|
|
|
0
|
die "Error from child: $error"; |
487
|
|
|
|
|
|
|
} |
488
|
112
|
50
|
|
|
|
244
|
$childFinishSub->() |
489
|
|
|
|
|
|
|
if $childFinishSub; |
490
|
112
|
|
|
|
|
620
|
return @$retval; |
491
|
|
|
|
|
|
|
} |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
sub printChangesToParent { |
494
|
15
|
|
|
15
|
0
|
163
|
my ($self, $error, $retval, $parentWtr) = @_; |
495
|
15
|
|
|
|
|
112
|
my $outputNr = 0; |
496
|
15
|
|
|
|
|
118
|
my @childInfo = ($error, $retval); |
497
|
15
|
|
|
|
|
35
|
foreach (@{$$self{tieObjects}}) { |
|
15
|
|
|
|
|
166
|
|
498
|
30
|
|
|
|
|
444
|
push @childInfo, $_->getChildInfo(); |
499
|
|
|
|
|
|
|
} |
500
|
|
|
|
|
|
|
{ |
501
|
15
|
|
|
|
|
44
|
local $SIG{PIPE} = sub { |
502
|
0
|
|
|
0
|
|
0
|
die "Couldn't print to pipe"; |
503
|
15
|
|
|
|
|
1672
|
}; |
504
|
15
|
100
|
|
|
|
181
|
if ($$self{workingSelect}) { |
505
|
10
|
|
|
|
|
191
|
print $parentWtr Storable::freeze(\@childInfo); |
506
|
|
|
|
|
|
|
} else { |
507
|
5
|
|
|
|
|
133
|
my ($fh, $filename) = tempfile(); |
508
|
5
|
|
|
|
|
4761
|
binmode $fh; |
509
|
5
|
|
|
|
|
120
|
print $fh Storable::freeze(\@childInfo); |
510
|
5
|
|
|
|
|
813
|
close $fh; |
511
|
5
|
|
|
|
|
166
|
print $parentWtr $filename; |
512
|
|
|
|
|
|
|
} |
513
|
|
|
|
|
|
|
} |
514
|
|
|
|
|
|
|
} |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
sub while { |
517
|
33
|
|
|
33
|
1
|
566
|
my ($self, $continueSub, $bodySub, $finishSub) = @_; |
518
|
33
|
|
|
|
|
200
|
my @retvals; |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# This is used if $$self{workingSelect} |
521
|
33
|
|
|
|
|
66
|
my $childCounter = 0; |
522
|
33
|
|
|
|
|
66
|
my $nrRunningChildren = 0; |
523
|
33
|
|
|
|
|
393
|
my $select = IO::Select->new(); |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
# Else this is used |
526
|
33
|
|
|
|
|
456
|
my %childHandles; |
527
|
|
|
|
|
|
|
|
528
|
33
|
|
|
|
|
628
|
my $fm = Parallel::ForkManager->new($$self{maxProcs}); |
529
|
33
|
|
|
|
|
68685
|
$$self{forkManager} = $fm; |
530
|
33
|
|
|
|
|
77
|
my %childFinishSubs; |
531
|
|
|
|
|
|
|
$fm->run_on_finish( sub { |
532
|
114
|
|
|
114
|
|
66106607
|
my ($pid) = @_; |
533
|
114
|
100
|
|
|
|
648
|
if ($$self{workingSelect}) { |
534
|
101
|
|
|
|
|
532
|
$nrRunningChildren--; |
535
|
|
|
|
|
|
|
} else { |
536
|
13
|
|
|
|
|
106
|
my $childRdr = $childHandles{$pid}; |
537
|
|
|
|
|
|
|
push @retvals, $self->readChangesFromChild( |
538
|
13
|
|
|
|
|
226
|
$childRdr, $childFinishSubs{$childRdr} |
539
|
|
|
|
|
|
|
); |
540
|
13
|
|
|
|
|
237
|
close $childRdr; |
541
|
|
|
|
|
|
|
} |
542
|
33
|
|
|
|
|
287
|
}); |
543
|
33
|
|
|
|
|
353
|
while (my $childData = $continueSub->()) { |
544
|
|
|
|
|
|
|
# Setup pipes so the child can send info back to the parent about |
545
|
|
|
|
|
|
|
# output data. |
546
|
135
|
|
|
|
|
5016
|
my $parentWtr = IO::Handle->new(); |
547
|
135
|
|
|
|
|
8427
|
my $childRdr = IO::Handle->new(); |
548
|
135
|
50
|
|
|
|
9414
|
pipe( $childRdr, $parentWtr ) |
549
|
|
|
|
|
|
|
or die "Couldn't open a pipe"; |
550
|
135
|
|
|
|
|
1111
|
binmode $parentWtr; |
551
|
135
|
|
|
|
|
255
|
binmode $childRdr; |
552
|
135
|
|
|
|
|
2836
|
$parentWtr->autoflush(1); |
553
|
|
|
|
|
|
|
|
554
|
135
|
50
|
|
|
|
16275
|
if ($finishSub) { |
555
|
|
|
|
|
|
|
$childFinishSubs{$childRdr} = sub { |
556
|
0
|
|
|
0
|
|
0
|
$finishSub->($childData); |
557
|
0
|
|
|
|
|
0
|
}; |
558
|
|
|
|
|
|
|
} |
559
|
|
|
|
|
|
|
|
560
|
135
|
100
|
|
|
|
447
|
if ($$self{workingSelect}) { |
561
|
|
|
|
|
|
|
# Read data from children that are ready. Block if maxProcs has |
562
|
|
|
|
|
|
|
# been reached, so that we are sure to close some file handle(s). |
563
|
|
|
|
|
|
|
my @ready = $select->can_read( |
564
|
115
|
100
|
|
|
|
1055
|
$nrRunningChildren >= $$self{maxProcs} ? undef : 0 |
565
|
|
|
|
|
|
|
); |
566
|
115
|
|
|
|
|
215386
|
for my $fh ( @ready ) { |
567
|
|
|
|
|
|
|
push @retvals, $self->readChangesFromChild( |
568
|
76
|
|
|
|
|
1881
|
$fh, $childFinishSubs{$fh} |
569
|
|
|
|
|
|
|
); |
570
|
76
|
|
|
|
|
443
|
$select->remove($fh); |
571
|
76
|
|
|
|
|
4679
|
close $fh; |
572
|
|
|
|
|
|
|
} |
573
|
|
|
|
|
|
|
} |
574
|
|
|
|
|
|
|
|
575
|
135
|
|
|
|
|
788
|
my $pid = $fm->start( ++$childCounter ); |
576
|
135
|
100
|
|
|
|
152731
|
if ($pid) { |
577
|
|
|
|
|
|
|
# We're running in the parent... |
578
|
120
|
|
|
|
|
3398
|
close $parentWtr; |
579
|
120
|
100
|
|
|
|
892
|
if ($$self{workingSelect}) { |
580
|
105
|
|
|
|
|
881
|
$nrRunningChildren++; |
581
|
105
|
|
|
|
|
2615
|
$select->add($childRdr); |
582
|
|
|
|
|
|
|
} else { |
583
|
15
|
|
|
|
|
420
|
$childHandles{$pid} = $childRdr; |
584
|
|
|
|
|
|
|
} |
585
|
120
|
|
|
|
|
20792
|
next; |
586
|
|
|
|
|
|
|
} |
587
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
# We're running in the child |
589
|
15
|
|
|
|
|
522
|
close $childRdr; |
590
|
|
|
|
|
|
|
|
591
|
15
|
|
|
|
|
70
|
my @retval; |
592
|
15
|
|
|
|
|
154
|
eval { |
593
|
15
|
|
|
|
|
452
|
@retval = $bodySub->(); |
594
|
|
|
|
|
|
|
}; |
595
|
15
|
|
|
|
|
43
|
my $error = $@; |
596
|
|
|
|
|
|
|
|
597
|
15
|
50
|
|
|
|
159
|
if (! defined wantarray) { |
598
|
|
|
|
|
|
|
# Lets not waste any energy printing stuff to the parent, if the |
599
|
|
|
|
|
|
|
# parent isn't going to use the return values anyway |
600
|
15
|
|
|
|
|
53
|
@retval = (); |
601
|
|
|
|
|
|
|
} |
602
|
|
|
|
|
|
|
|
603
|
15
|
|
|
|
|
686
|
$self->printChangesToParent($error, \@retval, $parentWtr); |
604
|
15
|
|
|
|
|
1988
|
close $parentWtr; |
605
|
|
|
|
|
|
|
|
606
|
15
|
|
|
|
|
307
|
$fm->finish($childCounter); # pass an exit code to finish |
607
|
|
|
|
|
|
|
} |
608
|
|
|
|
|
|
|
|
609
|
18
|
100
|
|
|
|
579
|
if ($$self{workingSelect}) { |
610
|
17
|
|
|
|
|
457
|
while (my @ready = $select->can_read()) { |
611
|
23
|
|
|
|
|
34803
|
for my $fh (@ready) { |
612
|
|
|
|
|
|
|
push @retvals, $self->readChangesFromChild( |
613
|
23
|
|
|
|
|
496
|
$fh, $childFinishSubs{$fh} |
614
|
|
|
|
|
|
|
); |
615
|
23
|
|
|
|
|
681
|
$select->remove($fh); |
616
|
23
|
|
|
|
|
1535
|
close $fh; |
617
|
|
|
|
|
|
|
} |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
} |
620
|
|
|
|
|
|
|
|
621
|
18
|
|
|
|
|
729
|
$fm->wait_all_children; |
622
|
18
|
|
|
|
|
387
|
delete $$self{forkManager}; |
623
|
18
|
|
|
|
|
1094
|
return @retvals; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
# foreach is implemented via while above |
627
|
|
|
|
|
|
|
sub foreach { |
628
|
16
|
|
|
16
|
1
|
112
|
my ($self, $varRef, $arrayRef, $sub); |
629
|
16
|
50
|
|
|
|
48
|
if (ref $_[1] eq 'ARRAY') { |
630
|
16
|
|
|
|
|
32
|
($self, $arrayRef, $sub) = @_; |
631
|
|
|
|
|
|
|
} else { |
632
|
|
|
|
|
|
|
# Note that this second usage is not documented (and hence not |
633
|
|
|
|
|
|
|
# supported). It isn't really useful, but this is how to use it just in |
634
|
|
|
|
|
|
|
# case: |
635
|
|
|
|
|
|
|
# |
636
|
|
|
|
|
|
|
# my $foo; |
637
|
|
|
|
|
|
|
# my %returnValues = $pl->foreach( \$foo, [ 0..9 ], sub { |
638
|
|
|
|
|
|
|
# $foo => sqrt($foo); |
639
|
|
|
|
|
|
|
# }); |
640
|
0
|
|
|
|
|
0
|
($self, $varRef, $arrayRef, $sub) = @_; |
641
|
|
|
|
|
|
|
} |
642
|
16
|
|
|
|
|
32
|
my $i = -1; |
643
|
81
|
|
|
81
|
|
487
|
$self->while( sub { ++$i <= $#{$arrayRef} }, sub { |
|
81
|
|
|
|
|
968
|
|
644
|
|
|
|
|
|
|
# Setup either $varRef or $_, if no such given before calling $sub->() |
645
|
5
|
50
|
|
5
|
|
390
|
if ($varRef) { |
646
|
0
|
|
|
|
|
0
|
$$varRef = $arrayRef->[$i]; |
647
|
|
|
|
|
|
|
} else { |
648
|
5
|
|
|
|
|
22
|
$_ = $arrayRef->[$i]; |
649
|
|
|
|
|
|
|
} |
650
|
5
|
|
|
|
|
140
|
$sub->(); |
651
|
16
|
|
|
|
|
112
|
}); |
652
|
|
|
|
|
|
|
} |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
package Parallel::Loops::TiedHash; |
655
|
16
|
|
|
16
|
|
256
|
use Tie::Hash; |
|
16
|
|
|
|
|
48
|
|
|
16
|
|
|
|
|
544
|
|
656
|
16
|
|
|
16
|
|
80
|
use base 'Tie::ExtraHash'; |
|
16
|
|
|
|
|
32
|
|
|
16
|
|
|
|
|
8096
|
|
657
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
sub TIEHASH { |
659
|
16
|
|
|
16
|
|
48
|
my ( $class, $loops, $storageRef ) = @_; |
660
|
16
|
|
|
|
|
80
|
my $storage = bless [ {}, { loops => $loops, childKeys => {} } ], $class; |
661
|
16
|
|
|
|
|
32
|
$$storageRef = $storage; |
662
|
16
|
|
|
|
|
48
|
return $storage; |
663
|
|
|
|
|
|
|
} |
664
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
sub STORE { |
666
|
127
|
|
|
127
|
|
2215
|
my ( $data, $key, $value ) = @_; |
667
|
|
|
|
|
|
|
|
668
|
127
|
|
|
|
|
3950
|
my $hash = $$data[0]; |
669
|
127
|
|
|
|
|
649
|
my $extra = $$data[1]; |
670
|
127
|
|
|
|
|
1019
|
my $loops = $$extra{loops}; |
671
|
|
|
|
|
|
|
|
672
|
127
|
100
|
|
|
|
877
|
if ( $loops->in_child() ) { |
673
|
15
|
|
|
|
|
252
|
$$extra{childKeys}{$key} = $value; |
674
|
|
|
|
|
|
|
} |
675
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
# warn sprintf "Setting $key to $value"; |
677
|
127
|
|
|
|
|
1463
|
$$hash{$key} = $value; |
678
|
|
|
|
|
|
|
} |
679
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
sub getChildInfo { |
681
|
15
|
|
|
15
|
|
47
|
my ($self, $outputNr) = @_; |
682
|
15
|
|
|
|
|
43
|
my $extra = $$self[1]; |
683
|
15
|
|
|
|
|
47
|
return $extra->{childKeys}; |
684
|
|
|
|
|
|
|
} |
685
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
package Parallel::Loops::TiedArray; |
687
|
16
|
|
|
16
|
|
6368
|
use Tie::Array; |
|
16
|
|
|
|
|
14624
|
|
|
16
|
|
|
|
|
400
|
|
688
|
16
|
|
|
16
|
|
80
|
use base 'Tie::Array'; |
|
16
|
|
|
|
|
32
|
|
|
16
|
|
|
|
|
6128
|
|
689
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
sub TIEARRAY { |
691
|
16
|
|
|
16
|
|
32
|
my ( $class, $loops, $storageRef ) = @_; |
692
|
16
|
|
|
|
|
64
|
my $storage = bless { arr => [], loops => $loops, childArr => [] }, $class; |
693
|
16
|
|
|
|
|
32
|
$$storageRef = $storage; |
694
|
16
|
|
|
|
|
32
|
return $storage; |
695
|
|
|
|
|
|
|
} |
696
|
|
|
|
|
|
|
|
697
|
159
|
|
|
159
|
|
136097
|
sub FETCHSIZE { scalar @{ $_[0]->{arr} } } |
|
159
|
|
|
|
|
1101
|
|
698
|
0
|
|
|
0
|
|
0
|
sub STORESIZE { $#{ $_[0]->{arr} } = $_[1] - 1 } |
|
0
|
|
|
|
|
0
|
|
699
|
90
|
|
|
90
|
|
546
|
sub STORE { $_[0]->{arr}->[ $_[1] ] = $_[2] } |
700
|
180
|
|
|
180
|
|
680
|
sub FETCH { $_[0]->{arr}->[ $_[1] ] } |
701
|
51
|
|
|
51
|
|
5788
|
sub CLEAR { @{ $_[0]->{arr} } = () } |
|
51
|
|
|
|
|
568
|
|
702
|
0
|
|
|
0
|
|
0
|
sub POP { pop( @{ $_[0]->{arr} } ) } |
|
0
|
|
|
|
|
0
|
|
703
|
0
|
|
|
0
|
|
0
|
sub SHIFT { shift( @{ $_[0]->{arr} } ) } |
|
0
|
|
|
|
|
0
|
|
704
|
0
|
|
|
0
|
|
0
|
sub UNSHIFT { my $o = shift; unshift( @{ $o->{arr} }, @_ ) } |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
705
|
0
|
|
|
0
|
|
0
|
sub EXISTS { exists $_[0]->{arr}->[ $_[1] ] } |
706
|
0
|
|
|
0
|
|
0
|
sub DELETE { delete $_[0]->{arr}->[ $_[1] ] } |
707
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
sub PUSH { |
709
|
127
|
|
|
127
|
|
753
|
my $self = shift; |
710
|
|
|
|
|
|
|
|
711
|
127
|
100
|
|
|
|
391
|
if ( $$self{loops}->in_child() ) { |
712
|
15
|
|
|
|
|
138
|
push( @{ $self->{childArr} }, @_ ); |
|
15
|
|
|
|
|
85
|
|
713
|
|
|
|
|
|
|
} |
714
|
|
|
|
|
|
|
|
715
|
127
|
|
|
|
|
538
|
push( @{ $self->{arr} }, @_ ); |
|
127
|
|
|
|
|
782
|
|
716
|
|
|
|
|
|
|
} |
717
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
sub getChildInfo { |
719
|
15
|
|
|
15
|
|
100
|
my ($self) = @_; |
720
|
15
|
|
|
|
|
65
|
return $self->{childArr}; |
721
|
|
|
|
|
|
|
} |
722
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
1; |