You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Producing item 1
Running with 1
Finished running with 1
Producing item 2
Running with 2
Finished running with 2
The source async iterable only starts producing the second item after the mapper function has handled the first one, meaning there's a 2 second pause between the two mapper invocations. The total running time of the above code is ~8 seconds.
There is no reason though, why the producing of the second item couldn't start sooner, so that by the time the first mapper invocation finishes, the second one wouldn't have to be delayed by so long. In other words, the producing of the next item and the mapping of the current item(s) could overlap. The running time of the above example could be reduced to ~6 seconds.
The effect of this issue becomes more pronounced when creating a pipeline of pMapIterable steps. Following is an example where 10 items are piped through 3 different mappings (with concurrency 1).
import{setTimeout}from'timers/promises';import{pMapIterable}from'p-map';asyncfunction*source(){for(leti=1;i<=10;++i){console.log('Producing item',i);awaitsetTimeout(1000);yieldi;}}conststepA=pMapIterable(source(),asyncn=>{console.log('Begin A with',n);awaitsetTimeout(1000);console.log('End A with with',n);returnn;},{concurrency: 1});conststepB=pMapIterable(stepA,asyncn=>{console.log('Begin B with',n);awaitsetTimeout(1000);console.log('End B with',n);returnn;},{concurrency: 1});conststepC=pMapIterable(stepB,asyncn=>{console.log('Begin C with',n);awaitsetTimeout(1000);console.log('End C with',n);returnn;},{concurrency: 1});forawait(const_ofstepC){}
It takes ~22 seconds with the current pMapIterable implementation. This could be reduced to ~13 seconds, which is the required minimum time (10s to produce 10 items + 3s to map the last item).
Eagerly invoke iterator.next() even when already at concurrency limit.
Otherwise, when a slot frees up and we have the chance to invoke the
mapper, we first have to wait for iterator.next() to resolve. During
this time the free slot is unused.
With this change the resolution of iterator.next() will already have
started and there is at least a chance that it's ready by the time we
want to invoke the mapper.
In principle, one could even implement a configurable backlog for
iterator.next() results. That would be a bigger change though.
Fixessindresorhus#78
pMapIterable
can waste some concurrency slots when the source async iterable produces items slowly.Here's an example:
The above program outputs:
The source async iterable only starts producing the second item after the mapper function has handled the first one, meaning there's a 2 second pause between the two mapper invocations. The total running time of the above code is ~8 seconds.
There is no reason though, why the producing of the second item couldn't start sooner, so that by the time the first mapper invocation finishes, the second one wouldn't have to be delayed by so long. In other words, the producing of the next item and the mapping of the current item(s) could overlap. The running time of the above example could be reduced to ~6 seconds.
The effect of this issue becomes more pronounced when creating a pipeline of
pMapIterable
steps. Following is an example where 10 items are piped through 3 different mappings (with concurrency 1).It takes ~22 seconds with the current
pMapIterable
implementation. This could be reduced to ~13 seconds, which is the required minimum time (10s to produce 10 items + 3s to map the last item).FYI @Richienb
The text was updated successfully, but these errors were encountered: