Search code examples
pythonnumpynumericexecution-timeexponentiation

Numpy: Negative Execution Time for exponentiation operation


I am multiplying two large matrices, and it turns out the operation is faster when I first perform exponentiation on the first input:

import time
import numpy as np

a = np.asarray(np.random.uniform(-1,1, (100,40000)), dtype=np.float32)
b = np.asarray(np.random.uniform(-1,1, (40000,20000)), dtype=np.float32)

start = time.time()
d0 = np.dot(a,b)
print "\nA.B           - {:.2f} seconds".format((time.time()-start))

start = time.time()
d1 = np.dot(np.exp(a), b)
print "exp(A).B      - {:.2f} seconds".format((time.time()-start))

start = time.time()
d2 = np.dot(a, np.exp(b))
print "A.exp(B)      - {:.2f} seconds".format((time.time()-start))

start = time.time()
d3 = np.dot(np.exp(a), np.exp(b))
print "exp(A).exp(B) - {:.2f} seconds".format((time.time()-start))

Here are the results:

A.B            1.27 seconds
exp(A).B       1.15 seconds
A.exp(B)       7.31 seconds
exp(A).exp(B)  7.38 seconds

Can anyone explain what am I doing wrong, or how is this possible?


Solution

  • What you are seeing is the reason why you would never run an operation only once when you are benchmarking, which is kind of what you are attempting here. Many things will affect the result you are seeing when you're only doing it once. In this case, probably some caching-effect. Also, the b array is so much bigger than the a array that you cannot draw any conclusions with a vs np.exp(a) when doing np.exp(b) unless you're running in a very controlled environment.

    To more properly benchmark this, we can cut the two last benchmarks and focus on a vs exp(a). Also, we repeat the operation 10,000 times and reduce the size of the arrays to avoid having to wait for several minutes:

    import time
    import numpy as np
    
    a = np.asarray(np.random.uniform(-1,1, (100,400)), dtype=np.float32)
    b = np.asarray(np.random.uniform(-1,1, (400,2000)), dtype=np.float32)
    
    start = time.time()
    for i in xrange(10000):
        d0 = np.dot(a,b)
    print "\nA.B           - {:.2f} seconds".format((time.time()-start))
    
    start = time.time()
    for i in xrange(10000):
        d0 = np.dot(np.exp(a), b)
    print "exp(A).B      - {:.2f} seconds".format((time.time()-start))
    

    This yields the following result on my computer:

    A.B           - 7.87 seconds
    exp(A).B      - 13.24 seconds
    

    As you see, doing np.exp(a) now takes more time than just accessing a, as expected.