Skip to content Skip to sidebar Skip to footer

Python 3 Type Hints For Performance Optimizations

PEP 484 says 'Using type hints for performance optimizations is left as an exercise for the reader.' This suggests to me that, like Common Lisp, type declarations can be used to se

Solution 1:

The speed difference is not due to type-hinting. Python currently, and for the foreseeable future, just discards any hints you offer and continues executing dynamically as it always does.

It's due to the fact that in one case you use floating arithmetic throughout the code (which results in faster execution) while in the other case you don't.

Case in point: Change baselpi1 to the following:

def baselpi1(n : int) -> float:
    n = float(n)
    baselsum  = 0
    i = 1while i < n:
        baselsum += 1.0 / (i * i)
        i += 1return math.sqrt(6.0 * baselsum)

And take a look at the execution times now:

3.141591698659554
0.2511475086212158
3.141591698659554
0.4525010585784912

Yes, it is way slower.

Solution 2:

If you need to do large amounts of numerical computation then numpy offers a good choice generally. Numpy works with lower level data types (such as fixed-width integers -- python's is unbounded). This gives you the sort of type-hinting you are interested in. As numpy is designed to work with large amounts of data in arrays with known types, it can efficiently perform the same operation on an entire array. This also allows numpy to work well with CPUs that SIMD instructions (I'm not aware of a modern CPU without SIMD).

I would normally rewrite your function as such:

import math
import numpy

def baselpi_numpy(n):
    i = numpy.arange(1, n) # array of 1..n
    baselsum = (1 / (i * i)).sum()
    return math.sqrt(6 * baselsum)

However, for large n you will not have enough memory. You'll have to add a bit of extra code to batch the the operation for you. That is:

def baselpi_numpy(n, batch_size=1<<16):
    basel_sum =0
    i =1for i inrange(1, n, batch_size):
        j =min(n, i + batch_size)
        basel_sum += baselsum_numpy(i, j)
    return math.sqrt(6* basel_sum)

def baselsum_numpy(start, end):
    # equivalent ->sum(1/ (i * i) for i inrange(start, end)) 
    i = numpy.arange(start, end, dtype=float)
    # this line and next are memory optimisations which double speed
    # equivalent to i =1/ (i * i)
    i *= i 
    i = numpy.divide(1, i, out=i)
    basel_sum = i.sum()
    return basel_sum

I get the result back in 5.2 seconds on my laptop. Though I haven't tested for the value of n you use, for lower n the numpy version is more than 20 times faster.

Post a Comment for "Python 3 Type Hints For Performance Optimizations"