  Frameworks like Numpy are a popular choice for application developers from
varied fields such as image processing to bio-informatics to machine learning.
Numpy is often used to develop prototypes or for deployment since it provides
efficient implementation for operations involving arrays. Such an approach
requires every operation to be executed eagerly. The result of each operation
needs to be stored in memory which increases the memory footprint of the
application. It also increases the bandwidth requirements since all uses must
read from this memory. We propose an approach that records the sequence of
Numpy operations for defered execution. When the values of an array are needed,
for example when the values are stored to disk or displayed on screen, the
sequence of operations required to compute these value are compiled into a
function and executed. This removes the need to store/load intermediates in
slow memory, resulting in better performance. In cases where the library
implementation is more efficient (like matrix-matrix multiply), those are used
instead. The approach also allows us to seamlessly target both multicore CPUs
and NVIDIA GPUs, thereby porting the Numpy application to these architectures
without changing the user program. The benefit of the approach is evaluated by
targeting computation samples from various domains and on average on order of
magnitude performance improvement over Numpy is observed.
