While reading through the InVEST utils source code, I noticed that our nodata
masking function returns a boolean array of zeros rather than just False
, and
it made me wonder if the memory allocation for this array was slower than
simply returning False
and letting the numpy array broadcasting handle the
distribution of that single value across all of the comparison array values.
In my tests here (see test-nodata-timings.py
), the runtime is dominated by
random array generation rather than indexing, but there does appear to be a
time savings as array sizes get larger.
This is confirmed with timeit
(see function main_timeit()
):
Indexing option: False
array size: 10*10: 1.9653402079999998s
array size: 10*100: 2.0178617080000003s
array size: 10*1000: 1.9825304170000004s
array size: 10*10000: 1.974148209s
array size: 10*100000: 2.922310917000001s
Indexing option: numpy.zeros(shape, dtype=bool)
array size: 10*10: 1.275730083000001s
array size: 10*100: 1.499919499999999s
array size: 10*1000: 2.1492175420000006s
array size: 10*10000: 8.273289708s
array size: 10*100000: 58.965569708000004s
When using False
as the index, the runtime of creating the index is roughly
constant until we end up with very many pixels, and even then the runtime is a
bit less than double what it was with an order of magnitude fewer pixels.
On the other side of things, the numpy.zeros
indexing is clearly slower, with
runtime increasing roughly linearly with the number of array elements needing
to be created.
Note that the default iterblocks number of elements is 2^16 (about 65,000).
This means that in the case of no defined nodata, we're allocating a whole lot
of array elements that are very likely thrown away and could easily be replaced
with False
. Really this is a minor tweak (especially considering that these
timings are with n=10000 or whatever the default is on timeit.timeit
), but it
could add up on very large arrays.