Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is numpy.power slower for integer exponents?

I chose these numbers randomly, but these results seem to be consistent --- a float exponent is 25%-50% faster than an integer one. How are these handled differently?

In [209]: %timeit -n 100000 -r 100 np.power(3.71242, 7)
100000 loops, best of 100: 3.45 µs per loop

In [210]: %timeit -n 100000 -r 100 np.power(3.71242, 7.0)
100000 loops, best of 100: 1.98 µs per loop
like image 911
DilithiumMatrix Avatar asked Nov 06 '14 02:11

DilithiumMatrix


People also ask

Is numpy slower than Python?

NumPy Arrays are faster than Python Lists because of the following reasons: An array is a collection of homogeneous data-types that are stored in contiguous memory locations.

How does numpy calculate power?

power() function takes two main arguments: 1) The array of base 2). The np. power() returns the array with elements of the first array raised to the power element of the second array. This means if we have two arrays (of the same size ) arr1 and arr2, then numpy.


1 Answers

np.power is a universal function (ufunc). These functions can be used on scalars and arrays which have a variety of different datatypes, but must first check the type of input values so that they can determine which internal loop to use to generate suitable output values.

If the input types do not map to any of the ufunc's predefined loops, the ufunc will try to cast the input values to suitable types (unless it is told otherwise). This checking and conversion of input values has a performance cost associated with it, explaining the timings observed in the question.

The types attribute of a ufunc shows how input datatypes will map to an output datatype. Below is the list of mappings for np.power:

>>> np.power.types # 'input input -> output'
['bb->b', 'BB->B', 'hh->h', 'HH->H', 'ii->i', 'II->I', 'll->l', 'LL->L', 'qq->q', 
 'QQ->Q', 'ee->e', 'ff->f', 'dd->d', 'gg->g', 'FF->F', 'DD->D', 'GG->G', 'OO->O']

Floating-point numbers belong to character code 'g', Python integers belong to 'l'. A full list of these character codes can be found here.

Note that for this ufunc, the datatypes of the two input values must be the same. There is no mapping for a mix of float and int input datatypes, for example.

But we can still give np.power different datatypes and let it cast the values to appropriate datatypes. For a float and an int, a float64 number is returned:

>>> np.power(3.71242, 7).dtype
dtype('float64')

Above you can see that the only input which maps to the float64 character code g is two other g values: 'gg->g'.

So, behind the scenes, np.power(3.71242, 7) took a Python float and a Python int and had to decide which it could safely recast and to what type. The int value was safely promoted to a float type g. The ufunc then knew which loop to run and returned another g value.

For this reason, not mixing input datatypes results in better performance for np.power.

like image 148
Alex Riley Avatar answered Oct 09 '22 22:10

Alex Riley