| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Parallel::Loops; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION='0.10'; |
|
4
|
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
# For Tie::ExtraHash - This was the earliest perl version in which I found this |
|
6
|
|
|
|
|
|
|
# class |
|
7
|
16
|
|
|
16
|
|
1080064
|
use 5.008; |
|
|
16
|
|
|
|
|
64
|
|
|
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
|
|
112
|
use strict; |
|
|
16
|
|
|
|
|
32
|
|
|
|
16
|
|
|
|
|
544
|
|
|
368
|
16
|
|
|
16
|
|
112
|
use warnings; |
|
|
16
|
|
|
|
|
48
|
|
|
|
16
|
|
|
|
|
688
|
|
|
369
|
|
|
|
|
|
|
|
|
370
|
16
|
|
|
16
|
|
96
|
use Carp; |
|
|
16
|
|
|
|
|
32
|
|
|
|
16
|
|
|
|
|
1040
|
|
|
371
|
16
|
|
|
16
|
|
9024
|
use IO::Handle; |
|
|
16
|
|
|
|
|
98592
|
|
|
|
16
|
|
|
|
|
768
|
|
|
372
|
16
|
|
|
16
|
|
7632
|
use IO::Select; |
|
|
16
|
|
|
|
|
26128
|
|
|
|
16
|
|
|
|
|
800
|
|
|
373
|
16
|
|
|
16
|
|
11808
|
use File::Temp qw(tempfile); |
|
|
16
|
|
|
|
|
234784
|
|
|
|
16
|
|
|
|
|
1120
|
|
|
374
|
16
|
|
|
16
|
|
10288
|
use Storable; |
|
|
16
|
|
|
|
|
52016
|
|
|
|
16
|
|
|
|
|
992
|
|
|
375
|
16
|
|
|
16
|
|
9648
|
use Parallel::ForkManager; |
|
|
16
|
|
|
|
|
924224
|
|
|
|
16
|
|
|
|
|
25904
|
|
|
376
|
|
|
|
|
|
|
|
|
377
|
|
|
|
|
|
|
sub new { |
|
378
|
16
|
|
|
16
|
0
|
2304
|
my ($class, $maxProcs, %options) = @_; |
|
379
|
16
|
|
|
|
|
80
|
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
|
128
|
my $reader = IO::Handle->new(); |
|
389
|
16
|
|
|
|
|
512
|
my $writer = IO::Handle->new(); |
|
390
|
16
|
50
|
|
|
|
1104
|
pipe( $reader, $writer ) |
|
391
|
|
|
|
|
|
|
or die "Couldn't open a pipe"; |
|
392
|
16
|
|
|
|
|
208
|
$writer->autoflush(1); |
|
393
|
16
|
|
|
|
|
1088
|
my $select = IO::Select->new(); |
|
394
|
16
|
|
|
|
|
272
|
$select->add($reader); |
|
395
|
16
|
|
|
|
|
1296
|
print $writer "test\n"; |
|
396
|
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
# There should be data right away, so lets not risk blocking if it is |
|
398
|
|
|
|
|
|
|
# unreliable |
|
399
|
16
|
|
|
|
|
128
|
my @handles = $select->can_read(0); |
|
400
|
16
|
|
|
|
|
880
|
my $working = (scalar(@handles) == 1); |
|
401
|
|
|
|
|
|
|
|
|
402
|
16
|
|
|
|
|
176
|
close $reader; |
|
403
|
16
|
|
|
|
|
240
|
close $writer; |
|
404
|
|
|
|
|
|
|
|
|
405
|
16
|
|
|
|
|
256
|
return $working; |
|
406
|
|
|
|
|
|
|
} |
|
407
|
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
sub share { |
|
409
|
64
|
|
|
64
|
1
|
29472
|
my ($self, @tieRefs) = @_; |
|
410
|
64
|
|
|
|
|
160
|
foreach my $ref (@tieRefs) { |
|
411
|
64
|
100
|
|
|
|
240
|
if (ref $ref eq 'HASH') { |
|
|
|
100
|
|
|
|
|
|
|
412
|
16
|
|
|
|
|
64
|
my %initialContents = %$ref; |
|
413
|
|
|
|
|
|
|
# $storage will point to the Parallel::Loops::TiedHash object |
|
414
|
16
|
|
|
|
|
32
|
my $storage; |
|
415
|
16
|
|
|
|
|
128
|
tie %$ref, 'Parallel::Loops::TiedHash', $self, \$storage; |
|
416
|
16
|
|
|
|
|
176
|
%$ref = %initialContents; |
|
417
|
16
|
|
|
|
|
160
|
push @{$$self{tieObjects}}, $storage; |
|
|
16
|
|
|
|
|
80
|
|
|
418
|
16
|
|
|
|
|
32
|
push @{$$self{tieHashes}}, [$$self{shareNr}, $ref]; |
|
|
16
|
|
|
|
|
80
|
|
|
419
|
|
|
|
|
|
|
} elsif (ref $ref eq 'ARRAY') { |
|
420
|
16
|
|
|
|
|
48
|
my @initialContents = @$ref; |
|
421
|
|
|
|
|
|
|
# $storage will point to the Parallel::Loops::TiedArray object |
|
422
|
16
|
|
|
|
|
32
|
my $storage; |
|
423
|
16
|
|
|
|
|
112
|
tie @$ref, 'Parallel::Loops::TiedArray', $self, \$storage; |
|
424
|
16
|
|
|
|
|
80
|
@$ref = @initialContents; |
|
425
|
16
|
|
|
|
|
32
|
push @{$$self{tieObjects}}, $storage; |
|
|
16
|
|
|
|
|
48
|
|
|
426
|
16
|
|
|
|
|
32
|
push @{$$self{tieArrays}}, [$$self{shareNr}, $ref]; |
|
|
16
|
|
|
|
|
64
|
|
|
427
|
|
|
|
|
|
|
} else { |
|
428
|
32
|
|
|
|
|
4432
|
croak "Only unblessed hash and array refs are supported by share"; |
|
429
|
|
|
|
|
|
|
} |
|
430
|
32
|
|
|
|
|
112
|
$$self{shareNr}++; |
|
431
|
|
|
|
|
|
|
} |
|
432
|
|
|
|
|
|
|
} |
|
433
|
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
sub in_child { |
|
435
|
250
|
|
|
250
|
0
|
601
|
my ($self) = @_; |
|
436
|
250
|
|
66
|
|
|
2718
|
return $$self{forkManager} && $$self{forkManager}->is_child; |
|
437
|
|
|
|
|
|
|
} |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
sub readChangesFromChild { |
|
440
|
110
|
|
|
110
|
0
|
1426
|
my ($self, $childRdr, $childFinishSub) = @_; |
|
441
|
|
|
|
|
|
|
|
|
442
|
110
|
|
|
|
|
480
|
my $childOutput; |
|
443
|
|
|
|
|
|
|
|
|
444
|
110
|
100
|
|
|
|
917
|
if ($$self{workingSelect}) { |
|
445
|
97
|
|
|
|
|
894
|
local $/; |
|
446
|
97
|
|
|
|
|
4129
|
$childOutput = <$childRdr>; |
|
447
|
|
|
|
|
|
|
} else { |
|
448
|
13
|
|
|
|
|
989
|
my $filename = <$childRdr>; |
|
449
|
13
|
50
|
|
|
|
839
|
open my $in, $filename |
|
450
|
|
|
|
|
|
|
or die "Couldn't open $filename"; |
|
451
|
13
|
|
|
|
|
78
|
binmode $in; |
|
452
|
|
|
|
|
|
|
{ |
|
453
|
13
|
|
|
|
|
33
|
local $/; |
|
|
13
|
|
|
|
|
81
|
|
|
454
|
13
|
|
|
|
|
402
|
$childOutput = <$in>; |
|
455
|
|
|
|
|
|
|
} |
|
456
|
13
|
|
|
|
|
178
|
close $in; |
|
457
|
13
|
|
|
|
|
973
|
unlink $filename; |
|
458
|
|
|
|
|
|
|
|
|
459
|
|
|
|
|
|
|
} |
|
460
|
110
|
50
|
|
|
|
594
|
die "Error getting result contents from child" |
|
461
|
|
|
|
|
|
|
if $childOutput eq ''; |
|
462
|
|
|
|
|
|
|
|
|
463
|
110
|
|
|
|
|
309
|
my @output; |
|
464
|
110
|
|
|
|
|
416
|
eval { |
|
465
|
110
|
|
|
|
|
251
|
@output = @{ Storable::thaw($childOutput) }; |
|
|
110
|
|
|
|
|
3454
|
|
|
466
|
|
|
|
|
|
|
}; |
|
467
|
110
|
50
|
|
|
|
8946
|
if ($@) { |
|
468
|
0
|
|
|
|
|
0
|
die "Error interpreting result from child: $@"; |
|
469
|
|
|
|
|
|
|
} |
|
470
|
110
|
|
|
|
|
227
|
my $error = shift @output; |
|
471
|
110
|
|
|
|
|
216
|
my $retval = shift @output; |
|
472
|
|
|
|
|
|
|
|
|
473
|
110
|
|
|
|
|
321
|
foreach my $set (@{$$self{tieHashes}}) { |
|
|
110
|
|
|
|
|
541
|
|
|
474
|
110
|
|
|
|
|
545
|
my ($outputNr, $h) = @$set; |
|
475
|
110
|
|
|
|
|
211
|
foreach my $k (keys %{$output[$outputNr]}) { |
|
|
110
|
|
|
|
|
434
|
|
|
476
|
110
|
|
|
|
|
2484
|
$$h{$k} = $output[$outputNr]{$k}; |
|
477
|
|
|
|
|
|
|
} |
|
478
|
|
|
|
|
|
|
} |
|
479
|
110
|
|
|
|
|
201
|
foreach my $set (@{$$self{tieArrays}}) { |
|
|
110
|
|
|
|
|
279
|
|
|
480
|
110
|
|
|
|
|
714
|
my ($outputNr, $a) = @$set; |
|
481
|
110
|
|
|
|
|
304
|
foreach my $v (@{$output[$outputNr]}) { |
|
|
110
|
|
|
|
|
278
|
|
|
482
|
110
|
|
|
|
|
1839
|
push @$a, $v; |
|
483
|
|
|
|
|
|
|
} |
|
484
|
|
|
|
|
|
|
} |
|
485
|
110
|
50
|
|
|
|
387
|
if ($error) { |
|
486
|
0
|
|
|
|
|
0
|
die "Error from child: $error"; |
|
487
|
|
|
|
|
|
|
} |
|
488
|
110
|
50
|
|
|
|
242
|
$childFinishSub->() |
|
489
|
|
|
|
|
|
|
if $childFinishSub; |
|
490
|
110
|
|
|
|
|
482
|
return @$retval; |
|
491
|
|
|
|
|
|
|
} |
|
492
|
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
sub printChangesToParent { |
|
494
|
15
|
|
|
15
|
0
|
74
|
my ($self, $error, $retval, $parentWtr) = @_; |
|
495
|
15
|
|
|
|
|
45
|
my $outputNr = 0; |
|
496
|
15
|
|
|
|
|
78
|
my @childInfo = ($error, $retval); |
|
497
|
15
|
|
|
|
|
38
|
foreach (@{$$self{tieObjects}}) { |
|
|
15
|
|
|
|
|
75
|
|
|
498
|
30
|
|
|
|
|
451
|
push @childInfo, $_->getChildInfo(); |
|
499
|
|
|
|
|
|
|
} |
|
500
|
|
|
|
|
|
|
{ |
|
501
|
15
|
|
|
|
|
49
|
local $SIG{PIPE} = sub { |
|
502
|
0
|
|
|
0
|
|
0
|
die "Couldn't print to pipe"; |
|
503
|
15
|
|
|
|
|
1991
|
}; |
|
504
|
15
|
100
|
|
|
|
211
|
if ($$self{workingSelect}) { |
|
505
|
10
|
|
|
|
|
236
|
print $parentWtr Storable::freeze(\@childInfo); |
|
506
|
|
|
|
|
|
|
} else { |
|
507
|
5
|
|
|
|
|
164
|
my ($fh, $filename) = tempfile(); |
|
508
|
5
|
|
|
|
|
5037
|
binmode $fh; |
|
509
|
5
|
|
|
|
|
114
|
print $fh Storable::freeze(\@childInfo); |
|
510
|
5
|
|
|
|
|
925
|
close $fh; |
|
511
|
5
|
|
|
|
|
161
|
print $parentWtr $filename; |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
} |
|
514
|
|
|
|
|
|
|
} |
|
515
|
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
sub while { |
|
517
|
33
|
|
|
33
|
1
|
633
|
my ($self, $continueSub, $bodySub, $finishSub) = @_; |
|
518
|
33
|
|
|
|
|
226
|
my @retvals; |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# This is used if $$self{workingSelect} |
|
521
|
33
|
|
|
|
|
99
|
my $childCounter = 0; |
|
522
|
33
|
|
|
|
|
72
|
my $nrRunningChildren = 0; |
|
523
|
33
|
|
|
|
|
464
|
my $select = IO::Select->new(); |
|
524
|
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
# Else this is used |
|
526
|
33
|
|
|
|
|
516
|
my %childHandles; |
|
527
|
|
|
|
|
|
|
|
|
528
|
33
|
|
|
|
|
756
|
my $fm = Parallel::ForkManager->new($$self{maxProcs}); |
|
529
|
33
|
|
|
|
|
86298
|
$$self{forkManager} = $fm; |
|
530
|
33
|
|
|
|
|
82
|
my %childFinishSubs; |
|
531
|
|
|
|
|
|
|
$fm->run_on_finish( sub { |
|
532
|
114
|
|
|
114
|
|
66102930
|
my ($pid) = @_; |
|
533
|
114
|
100
|
|
|
|
644
|
if ($$self{workingSelect}) { |
|
534
|
101
|
|
|
|
|
430
|
$nrRunningChildren--; |
|
535
|
|
|
|
|
|
|
} else { |
|
536
|
13
|
|
|
|
|
83
|
my $childRdr = $childHandles{$pid}; |
|
537
|
|
|
|
|
|
|
push @retvals, $self->readChangesFromChild( |
|
538
|
13
|
|
|
|
|
224
|
$childRdr, $childFinishSubs{$childRdr} |
|
539
|
|
|
|
|
|
|
); |
|
540
|
13
|
|
|
|
|
296
|
close $childRdr; |
|
541
|
|
|
|
|
|
|
} |
|
542
|
33
|
|
|
|
|
368
|
}); |
|
543
|
33
|
|
|
|
|
386
|
while (my $childData = $continueSub->()) { |
|
544
|
|
|
|
|
|
|
# Setup pipes so the child can send info back to the parent about |
|
545
|
|
|
|
|
|
|
# output data. |
|
546
|
135
|
|
|
|
|
5116
|
my $parentWtr = IO::Handle->new(); |
|
547
|
135
|
|
|
|
|
10705
|
my $childRdr = IO::Handle->new(); |
|
548
|
135
|
50
|
|
|
|
13043
|
pipe( $childRdr, $parentWtr ) |
|
549
|
|
|
|
|
|
|
or die "Couldn't open a pipe"; |
|
550
|
135
|
|
|
|
|
1780
|
binmode $parentWtr; |
|
551
|
135
|
|
|
|
|
327
|
binmode $childRdr; |
|
552
|
135
|
|
|
|
|
2448
|
$parentWtr->autoflush(1); |
|
553
|
|
|
|
|
|
|
|
|
554
|
135
|
50
|
|
|
|
18843
|
if ($finishSub) { |
|
555
|
|
|
|
|
|
|
$childFinishSubs{$childRdr} = sub { |
|
556
|
0
|
|
|
0
|
|
0
|
$finishSub->($childData); |
|
557
|
0
|
|
|
|
|
0
|
}; |
|
558
|
|
|
|
|
|
|
} |
|
559
|
|
|
|
|
|
|
|
|
560
|
135
|
100
|
|
|
|
485
|
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
|
|
|
|
774
|
$nrRunningChildren >= $$self{maxProcs} ? undef : 0 |
|
565
|
|
|
|
|
|
|
); |
|
566
|
115
|
|
|
|
|
22103
|
for my $fh ( @ready ) { |
|
567
|
|
|
|
|
|
|
push @retvals, $self->readChangesFromChild( |
|
568
|
63
|
|
|
|
|
2255
|
$fh, $childFinishSubs{$fh} |
|
569
|
|
|
|
|
|
|
); |
|
570
|
63
|
|
|
|
|
324
|
$select->remove($fh); |
|
571
|
63
|
|
|
|
|
3703
|
close $fh; |
|
572
|
|
|
|
|
|
|
} |
|
573
|
|
|
|
|
|
|
} |
|
574
|
|
|
|
|
|
|
|
|
575
|
135
|
|
|
|
|
864
|
my $pid = $fm->start( ++$childCounter ); |
|
576
|
135
|
100
|
|
|
|
175865
|
if ($pid) { |
|
577
|
|
|
|
|
|
|
# We're running in the parent... |
|
578
|
120
|
|
|
|
|
3259
|
close $parentWtr; |
|
579
|
120
|
100
|
|
|
|
882
|
if ($$self{workingSelect}) { |
|
580
|
105
|
|
|
|
|
413
|
$nrRunningChildren++; |
|
581
|
105
|
|
|
|
|
2935
|
$select->add($childRdr); |
|
582
|
|
|
|
|
|
|
} else { |
|
583
|
15
|
|
|
|
|
408
|
$childHandles{$pid} = $childRdr; |
|
584
|
|
|
|
|
|
|
} |
|
585
|
120
|
|
|
|
|
22332
|
next; |
|
586
|
|
|
|
|
|
|
} |
|
587
|
|
|
|
|
|
|
|
|
588
|
|
|
|
|
|
|
# We're running in the child |
|
589
|
15
|
|
|
|
|
755
|
close $childRdr; |
|
590
|
|
|
|
|
|
|
|
|
591
|
15
|
|
|
|
|
158
|
my @retval; |
|
592
|
15
|
|
|
|
|
46
|
eval { |
|
593
|
15
|
|
|
|
|
614
|
@retval = $bodySub->(); |
|
594
|
|
|
|
|
|
|
}; |
|
595
|
15
|
|
|
|
|
186
|
my $error = $@; |
|
596
|
|
|
|
|
|
|
|
|
597
|
15
|
50
|
|
|
|
120
|
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
|
|
|
|
|
76
|
@retval = (); |
|
601
|
|
|
|
|
|
|
} |
|
602
|
|
|
|
|
|
|
|
|
603
|
15
|
|
|
|
|
598
|
$self->printChangesToParent($error, \@retval, $parentWtr); |
|
604
|
15
|
|
|
|
|
2073
|
close $parentWtr; |
|
605
|
|
|
|
|
|
|
|
|
606
|
15
|
|
|
|
|
216
|
$fm->finish($childCounter); # pass an exit code to finish |
|
607
|
|
|
|
|
|
|
} |
|
608
|
|
|
|
|
|
|
|
|
609
|
18
|
100
|
|
|
|
646
|
if ($$self{workingSelect}) { |
|
610
|
17
|
|
|
|
|
332
|
while (my @ready = $select->can_read()) { |
|
611
|
34
|
|
|
|
|
33817
|
for my $fh (@ready) { |
|
612
|
|
|
|
|
|
|
push @retvals, $self->readChangesFromChild( |
|
613
|
34
|
|
|
|
|
720
|
$fh, $childFinishSubs{$fh} |
|
614
|
|
|
|
|
|
|
); |
|
615
|
34
|
|
|
|
|
1798
|
$select->remove($fh); |
|
616
|
34
|
|
|
|
|
2275
|
close $fh; |
|
617
|
|
|
|
|
|
|
} |
|
618
|
|
|
|
|
|
|
} |
|
619
|
|
|
|
|
|
|
} |
|
620
|
|
|
|
|
|
|
|
|
621
|
18
|
|
|
|
|
773
|
$fm->wait_all_children; |
|
622
|
18
|
|
|
|
|
369
|
delete $$self{forkManager}; |
|
623
|
18
|
|
|
|
|
1345
|
return @retvals; |
|
624
|
|
|
|
|
|
|
} |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
# foreach is implemented via while above |
|
627
|
|
|
|
|
|
|
sub foreach { |
|
628
|
16
|
|
|
16
|
1
|
160
|
my ($self, $varRef, $arrayRef, $sub); |
|
629
|
16
|
50
|
|
|
|
64
|
if (ref $_[1] eq 'ARRAY') { |
|
630
|
16
|
|
|
|
|
48
|
($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
|
|
|
|
|
48
|
my $i = -1; |
|
643
|
81
|
|
|
81
|
|
658
|
$self->while( sub { ++$i <= $#{$arrayRef} }, sub { |
|
|
81
|
|
|
|
|
1195
|
|
|
644
|
|
|
|
|
|
|
# Setup either $varRef or $_, if no such given before calling $sub->() |
|
645
|
5
|
50
|
|
5
|
|
595
|
if ($varRef) { |
|
646
|
0
|
|
|
|
|
0
|
$$varRef = $arrayRef->[$i]; |
|
647
|
|
|
|
|
|
|
} else { |
|
648
|
5
|
|
|
|
|
22
|
$_ = $arrayRef->[$i]; |
|
649
|
|
|
|
|
|
|
} |
|
650
|
5
|
|
|
|
|
160
|
$sub->(); |
|
651
|
16
|
|
|
|
|
160
|
}); |
|
652
|
|
|
|
|
|
|
} |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
package Parallel::Loops::TiedHash; |
|
655
|
16
|
|
|
16
|
|
400
|
use Tie::Hash; |
|
|
16
|
|
|
|
|
64
|
|
|
|
16
|
|
|
|
|
704
|
|
|
656
|
16
|
|
|
16
|
|
112
|
use base 'Tie::ExtraHash'; |
|
|
16
|
|
|
|
|
48
|
|
|
|
16
|
|
|
|
|
10736
|
|
|
657
|
|
|
|
|
|
|
|
|
658
|
|
|
|
|
|
|
sub TIEHASH { |
|
659
|
16
|
|
|
16
|
|
64
|
my ( $class, $loops, $storageRef ) = @_; |
|
660
|
16
|
|
|
|
|
96
|
my $storage = bless [ {}, { loops => $loops, childKeys => {} } ], $class; |
|
661
|
16
|
|
|
|
|
48
|
$$storageRef = $storage; |
|
662
|
16
|
|
|
|
|
48
|
return $storage; |
|
663
|
|
|
|
|
|
|
} |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
sub STORE { |
|
666
|
125
|
|
|
125
|
|
6893
|
my ( $data, $key, $value ) = @_; |
|
667
|
|
|
|
|
|
|
|
|
668
|
125
|
|
|
|
|
1037
|
my $hash = $$data[0]; |
|
669
|
125
|
|
|
|
|
869
|
my $extra = $$data[1]; |
|
670
|
125
|
|
|
|
|
920
|
my $loops = $$extra{loops}; |
|
671
|
|
|
|
|
|
|
|
|
672
|
125
|
100
|
|
|
|
642
|
if ( $loops->in_child() ) { |
|
673
|
15
|
|
|
|
|
313
|
$$extra{childKeys}{$key} = $value; |
|
674
|
|
|
|
|
|
|
} |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
# warn sprintf "Setting $key to $value"; |
|
677
|
125
|
|
|
|
|
1728
|
$$hash{$key} = $value; |
|
678
|
|
|
|
|
|
|
} |
|
679
|
|
|
|
|
|
|
|
|
680
|
|
|
|
|
|
|
sub getChildInfo { |
|
681
|
15
|
|
|
15
|
|
73
|
my ($self, $outputNr) = @_; |
|
682
|
15
|
|
|
|
|
64
|
my $extra = $$self[1]; |
|
683
|
15
|
|
|
|
|
58
|
return $extra->{childKeys}; |
|
684
|
|
|
|
|
|
|
} |
|
685
|
|
|
|
|
|
|
|
|
686
|
|
|
|
|
|
|
package Parallel::Loops::TiedArray; |
|
687
|
16
|
|
|
16
|
|
8896
|
use Tie::Array; |
|
|
16
|
|
|
|
|
19824
|
|
|
|
16
|
|
|
|
|
496
|
|
|
688
|
16
|
|
|
16
|
|
128
|
use base 'Tie::Array'; |
|
|
16
|
|
|
|
|
16
|
|
|
|
16
|
|
|
|
|
7952
|
|
|
689
|
|
|
|
|
|
|
|
|
690
|
|
|
|
|
|
|
sub TIEARRAY { |
|
691
|
16
|
|
|
16
|
|
48
|
my ( $class, $loops, $storageRef ) = @_; |
|
692
|
16
|
|
|
|
|
80
|
my $storage = bless { arr => [], loops => $loops, childArr => [] }, $class; |
|
693
|
16
|
|
|
|
|
48
|
$$storageRef = $storage; |
|
694
|
16
|
|
|
|
|
32
|
return $storage; |
|
695
|
|
|
|
|
|
|
} |
|
696
|
|
|
|
|
|
|
|
|
697
|
159
|
|
|
159
|
|
143250
|
sub FETCHSIZE { scalar @{ $_[0]->{arr} } } |
|
|
159
|
|
|
|
|
620
|
|
|
698
|
0
|
|
|
0
|
|
0
|
sub STORESIZE { $#{ $_[0]->{arr} } = $_[1] - 1 } |
|
|
0
|
|
|
|
|
0
|
|
|
699
|
90
|
|
|
90
|
|
454
|
sub STORE { $_[0]->{arr}->[ $_[1] ] = $_[2] } |
|
700
|
180
|
|
|
180
|
|
700
|
sub FETCH { $_[0]->{arr}->[ $_[1] ] } |
|
701
|
51
|
|
|
51
|
|
5898
|
sub CLEAR { @{ $_[0]->{arr} } = () } |
|
|
51
|
|
|
|
|
669
|
|
|
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
|
125
|
|
|
125
|
|
651
|
my $self = shift; |
|
710
|
|
|
|
|
|
|
|
|
711
|
125
|
100
|
|
|
|
373
|
if ( $$self{loops}->in_child() ) { |
|
712
|
15
|
|
|
|
|
114
|
push( @{ $self->{childArr} }, @_ ); |
|
|
15
|
|
|
|
|
160
|
|
|
713
|
|
|
|
|
|
|
} |
|
714
|
|
|
|
|
|
|
|
|
715
|
125
|
|
|
|
|
1646
|
push( @{ $self->{arr} }, @_ ); |
|
|
125
|
|
|
|
|
839
|
|
|
716
|
|
|
|
|
|
|
} |
|
717
|
|
|
|
|
|
|
|
|
718
|
|
|
|
|
|
|
sub getChildInfo { |
|
719
|
15
|
|
|
15
|
|
96
|
my ($self) = @_; |
|
720
|
15
|
|
|
|
|
73
|
return $self->{childArr}; |
|
721
|
|
|
|
|
|
|
} |
|
722
|
|
|
|
|
|
|
|
|
723
|
|
|
|
|
|
|
1; |