1

I've been working on optimizing some Euclidean distance transform calculations for a program that I'm building. To preface, I have little formal training in computer science other than some MOOCs I've been taking.

I've learned through empirical testing in Python that assigning values to individual variables and performing operations on them is faster than performing operations on arrays. Is this observation reproducible for others?

If so, could someone provide a deeper explanation as to why there are such speed differences between these two forms of syntax?

Please see some example code below.

import numpy as np
from math import sqrt
import time

# Numpy array math
def test1(coords):
    results = []
    for coord in coords:
        mins = np.array([1,1,1])
        # The three lines below seem faster than np.linalg.norm()
        mins = (coord - mins)**2
        mins = np.sum(mins) 
        results.append(sqrt(mins))
   
# Individual variable assignment math     
def test2(coords):
    results = []
    for point in coords:
        z, y, x = 1, 1, 1
        z = (point[0] - z)**2
        y = (point[1] - y)**2
        x = (point[2] - x)**2
        mins = sqrt(z + y + x)
        results.append(mins)
        
a = np.random.randint(0, 10, (500000,3))

t = time.perf_counter()
test1(a)
print ("Test 1 speed:", time.perf_counter() - t)

t = time.perf_counter()
test2(a)
print ("Test 2 speed:", time.perf_counter() - t)
  • Test 1 speed: 3.261552719 s
  • Test 2 speed: 0.716983475 s
5
  • 1
    They are not two forms of syntax for the same thing; the first code creates a numpy array object in each iteration while the second code doesn't. Commented Sep 20, 2021 at 22:43
  • 2
    Two reasons stand out to me: 1) memory allocations of numpy arrays being created (as noted by @mkrieger1); 2) the overhead cost of iterating over very small collections (see loop unrolling) Commented Sep 20, 2021 at 22:47
  • 3
    NumPy arrays are optimized for large arrays. Operations have high per-call overhead but very low per-element overhead. You have 3 elements. Per-call overhead dominates. Commented Sep 20, 2021 at 22:54
  • 3
    It'd go faster if you weren't looping manually over coords. Commented Sep 20, 2021 at 22:55
  • 1
    As an experiment, I went from 3.3 seconds on test1 to 2.4, just by moving mins outside of the function to elimintate that per loop array creation. More subtly, for coord in coords: has just as many array creations - one for every coord that is extracted from the array. If you could just remove that, you'd be down to 1.5 seconds. Addressing individual elements in the array is expensive because you have to convert every access to a python float. So, the vectorized solution wins hands down. Commented Sep 20, 2021 at 23:18

1 Answer 1

2

Python operations and memory allocations are generally much slower than Numpy's highly optimized, vectorized array operations. Since you are looping over the array and allocating memory, you don't get any of the benefits that Numpy offers. It's especially bad in your first one because it causes an undue number of allocations of small arrays.

Compare your code to one that offloads all the operations to Numpy instead of having Python do the operations one by one:

def test3(coords):
    mins = (coords - 1)**2
    results = np.sqrt(np.sum(mins, axis=1))
    return results

On my system, this results in:

Test 1 speed: 4.995761550962925
Test 2 speed: 1.3881473205983639
Test 3 speed: 0.05562112480401993
Sign up to request clarification or add additional context in comments.

5 Comments

You don't actually need that tile - broadcasting will handle it.
Wow this is very useful. I was aware of vectorized operations but I have no idea why I've never considered conducting the operations in this manner.
@JacobBumgarner - that's the core of numpy and the projects that use it. Use basic C/Fortran data types and apply operations over entire array. Python's data types are all bulky in comparison.
@tdelaney Thanks for your help. I feel like some of these things should've been obvious to me, but it's better to learn now than never.
@JacobBumgarner - Don't worry. Its not at all obvious.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.