EV-Future
view release on metacpan or search on metacpan
lib/EV/Future.pm view on Meta::CPAN
The handle also reports progress:
my $h = parallel_limit(\@tasks, 4, sub { });
printf "%d in flight, %d to go\n", $h->active, $h->pending;
=head1 EV::Future::Handle
The object every primitive returns in non-void context. It is a lightweight
reference to the operation and owns nothing: destroying the handle does not
cancel the operation, and the operation ending does not invalidate the handle.
Every method below is safe to call at any time, including on a handle whose
operation is already over, where C<cancel> does nothing and both counters read
0.
The object wraps a raw pointer to one refcounted cell, so duplicating it would
produce a second owner of that cell and, eventually, a double free. Two
duplication routes are defended against: a thread clone yields C<undef>
(C<EV::Future::Handle> sets C<CLONE_SKIP>), and a C<Storable> freeze, thaw or
C<dclone>, which does not honour C<CLONE_SKIP>, yields an inert dead handle
rather than a second owner.
That is not a general guarantee. A deep cloner that copies the underlying
scalar directly, bypassing both hooks, still yields a second live owner;
C<Clone::clone> is one such. Do not copy a handle by any means other than
assigning the reference itself, and create a handle in, and use it from, one
interpreter.
=head2 cancel([$fire])
Stop dispatching. With no argument, or a false one, C<final_cb> never fires;
with a true C<$fire> it fires exactly once, with no arguments, before C<cancel>
returns. Idempotent, and a no-op once the operation is over. See L</CANCELLATION>
above for what cancellation can and cannot reach.
=head2 pending
Tasks that have not yet completed, counting both those in flight and those not
yet dispatched.
=head2 active
Tasks currently in flight. C<parallel> and C<race> report the same value here as
for C<pending>, because they dispatch every task before returning; C<series>
always reports 1 while it is running.
=head2 What the counters guarantee
Both read 0 once the operation is over, so C<pending == 0> means it has finished
or been cancelled. The one exception is an operation abandoned by an escaping
unsafe-mode exception, whose counters freeze instead; see
L</"Safe vs unsafe mode">.
In unsafe mode, double-calling C<done> corrupts the completion counter, so
C<pending> under-reports for C<parallel> and C<parallel_limit> for the same
reason C<final_cb> can fire early. The same double-call can drive the in-flight
count below zero; C<active> is floored at 0 rather than reporting a negative.
=head1 BENCHMARKS
1000 synchronous tasks, 5000 iterations (C<bench/comparison.pl>). The script
covers the task form only, so C<race> and the three map primitives have no
figures here; see L</"Task form versus map form"> below for those. Measured
with EV 4.37, AnyEvent 7.17, Future::XS 0.15, Promise::XS 0.21 and
Future::Utils 0.52.
--- PARALLEL (iterations/sec) ---
EV::Future (unsafe) 9,259
EV::Future (safe) 4,098
AnyEvent::cv (begin/end) 1,563
Future::XS::wait_all 1,563
Promise::XS::all 1,323
--- PARALLEL LIMIT 10 (iterations/sec) ---
EV::Future (unsafe) 7,692
EV::Future (safe) 4,032
Future::Utils::fmap_void 624
--- SERIES (iterations/sec) ---
EV::Future (unsafe) 7,692
AnyEvent::cv (stack-safe) 4,717
EV::Future (safe) 3,968
Future::XS (chain) 1,241
Promise::XS (chain) 1,157
=head2 Task form versus map form
C<bench/map_vs_task.pl>, 100 elements, unsafe mode, as cachegrind instruction
counts per call, which are deterministic where wall-clock timings on a loaded
machine are not:
parallel, task list reused 150,376
parallel_map, worker reused 186,106 +24%
parallel, task list per call 264,389
parallel_map, worker per call 186,193 -30%
Per element the map form is the more expensive of the two: it pushes the item
as well as C<done>, which costs around 360 instructions per element that the
task form does not pay.
It still wins in practice, because a task closure has to capture its own
element, so the task form has to rebuild the whole list on every call: about
1,140 instructions per element, roughly three times what the extra push costs.
The map form has nothing per-element to rebuild, which is why its two rows above
are within 0.05% of each other; hoisting the worker out of the call buys
nothing.
So the map form is about 30% cheaper in the usual case, where the list is built
at the call site, and about 24% dearer in the case where you can hoist a fixed
task list and reuse it across calls. Reach for the map form for its ergonomics,
and expect the performance to follow in the common case rather than always.
=head1 SEE ALSO
L<EV>, L<Future::XS>, L<Promise::XS>
=head1 AUTHOR
vividsnow
=head1 LICENSE
This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.
=cut
1;
( run in 1.966 second using v1.01-cache-2.11-cpan-4ab04211f4c )