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 )