Performance

nuts-flow does not support concurrency in general but provides nuts that can improve performance by caching or parallelization.

MapPar

Applying a function concurrently to the elements of a flow can be achieved with the MapPar nut. The following toy example converts numbers to their absolute values by applying the abs function in parallel

>>> from nutsflow import *
>>> [-1, -2, -3] >> MapPar(abs) >> Collect()
[1, 2, 3]

Note that the order of the elements in the iterable is preserved. Currently, MapPar is of limited use, since 1) the function applied must be pickable and 2) MapPar creates parallel processes, which are computationally expensive to start.

Cache

nuts-flow supports the caching of results to disk. Here an example in pseudo code

with Cache() as cache:
    for i in range:
        data >> expensive_op >> cache >> ... >> Collect()

Note that caching is only useful if 1) the elements to cache are time-consuming to compute, 2) can be loaded faster than recreated, and 3) the same data flow is executed multiple times, where in the first run the cache is filled and then is used in all subsequent runs.

A common use case is machine learning for vision, where images are preprocessed and a classifier is trained by repeatedly executing a data flow:

with Cache() as cache:
    for epoch in xrange(100):
        images >> preprocess >> cache >> network.train() >> Consume()

Cached elements are pickled to a temporary folder which is deleted when the with block is exited. The cache can be cleared as follows:

with Cache() as cache:
    ...
    cache.clear()

Prefetch

Prefetching is another common method employed in (GPU-based) machine learning to speed up a data flow. Here data is pre-fetched (and pre-processed) on a separate CPU thread while the GPU is performing machine learning on another chunk of data:

images >> preprocess >> Prefetch() >> network.train() >> Consume()

The following two examples demonstrate the difference between processing a data flow with and without pre-fetching. First a flow without pre-fetching that takes one number and prints it

>>> Range(5) >> Print() >> Take(1) >> Consume()
0

now the same flow but with pre-fetching

>>> Range(5) >> Print() >> Prefetch() >> Take(1) >> Consume()
0
1