Numba Paralellisation

Hi guys, I’m fairly new with Python and especially Numba but I’m stuck in a weird situation where I don’t quite understand what’s happening.

Maybe you can tell me what I am doing wrong.

I have a 3D matrix as a numpy array:

location = [[x,y,z]
 ...
 [x,y,z]]

I try to perform a distance calculation on it in a parallelized way, as follow :

@njit(parallel=True, nogil=True, debug=True)
def parallelDistances(locations: NDArray[float64]):
    locationLen = locations.shape[0]
    allMinDists = zeros((1, locationLen))
    for idLoc, location in enumerate(locations):
        minDistsOneLocation = zeros((1, locationLen))
        for index in prange(locationLen):
            otherPoint = locations[index]
            dist = (location[0] - otherPoint[0])**2+(location[1] - otherPoint[1])**2+(location[2] - otherPoint[2])**2
            minDistsOneLocation[index] = dist
        allMinDists[idLoc] = minDistsOneLocation[minDistsOneLocation > 0].min()

    return allMinDists

But as soon as I call :

print(myparallelDistance(locations))

The following errors occures:

C:\Python310\lib\site-packages\numba\core\lowering.py:107: NumbaDebugInfoWarning: Could not find source for function: <function parallelDistances at 0x000002179C5D8AF0>. Debug line information may be inaccurate.
  warnings.warn(NumbaDebugInfoWarning(msg))
C:\Python310\lib\site-packages\numba\core\lowering.py:107: NumbaDebugInfoWarning: Could not find source for function: <function __numba_parfor_gufunc_0x217a0f9a7a0 at 0x00000217A0E15000>. Debug line information may be inaccurate.
  warnings.warn(NumbaDebugInfoWarning(msg))
C:\Python310\lib\site-packages\numba\core\lowering.py:107: NumbaDebugInfoWarning: Could not find source for function: <function __numba_parfor_gufunc_0x217a0f99030 at 0x00000217A0F94E50>. Debug line information may be inaccurate.
  warnings.warn(NumbaDebugInfoWarning(msg))
C:\Python310\lib\site-packages\numba\core\lowering.py:107: NumbaDebugInfoWarning: Could not find source for function: <function __numba_parfor_gufunc_0x217a0f24be0 at 0x00000217A0E15000>. Debug line information may be inaccurate.
  warnings.warn(NumbaDebugInfoWarning(msg))
C:\Python310\lib\site-packages\numba\core\lowering.py:107: NumbaDebugInfoWarning: Could not find source for function: <function __numba_parfor_gufunc_0x217a1089e40 at 0x00000217A16355A0>. Debug line information may be inaccurate.
  warnings.warn(NumbaDebugInfoWarning(msg))
C:\Python310\lib\site-packages\numba\core\lowering.py:107: NumbaDebugInfoWarning: Could not find source for function: <function __numba_parfor_gufunc_0x217a0f245e0 at 0x00000217A1635900>. Debug line information may be inaccurate.
  warnings.warn(NumbaDebugInfoWarning(msg))

I cannot seem to understand how it is possible that when I don’t run it parallelized, it runs no problem but as soon as it is parallelized, it crashes.

Do you guys have any idea of what I am doing wrong ?
Thank you in advance for your help.
Feel free to ask for more informations ! :slight_smile:

Welcome to the Numba tribe!

Your code raises a few questions. For starters, you’re showing a warning, but that’s probably not the error/exception that you’re actually getting?

You mention having a 3D array with locations, but isn’t it a 2D array of size [n_locations, n_coordinates]?

You probably want to skip calculating the distance between the same point, since it will always be zero and therefore the minimum.

It doesn’t seem necessary to define the distance arrays as 2D (using [1, locationLen]. It would technically work, but you would still need to set the items using allMinDists[0, idLoc] otherwise you’ll probably get an index error since the first dimension has length 1. Eitherway it’s probably best to avoid that and just use a 1D array in that case.

From a performance perspective it’s probably best to parallelize the outer loop, instead of the inner loop. And storing all distances is not necessary if you only want to keep the minimum, and especially allocating that entire array each time within the loop will slow things down. Even if you do want to keep all distances, it’s better to allocate that array once upfront, and “reset” it at each iteration minDistsOneLocation[:] = 0.0.

Perhaps something like:

@njit(parallel=True, nogil=True)
def parallel_distances(locations):
    
    n_loc = locations.shape[0]
    min_dist = np.zeros(n_loc, dtype=np.float32)
    
    for i in prange(n_loc):
        
        p1 = locations[i, :]
        min_dist_loc = np.inf
        
        for j in range(n_loc):
            
            if i == j:
                continue
            
            p2 = locations[j, :]
            
            dist = (
                (p1[0] - p2[0])**2 + 
                (p1[1] - p2[1])**2 +
                (p1[2] - p2[2])**2
            )

            min_dist_loc = min(min_dist_loc, dist)
            
        min_dist[i] = min_dist_loc

    return min_dist

Hey @Rutger , first of all, I thank you a lot for taking the time to read and reply to my question !

You’re right, I used the wrong words to describe my array. It is actually a 2D array with 3D coordinates inside !

I didn’t think about removing the same point as I thought that it would be a single computation more but you’re right, if we can avoid it, better avoid it ! :slight_smile:

Regarding the issue that was crashing my code, it seems that you were also right and we shouldn’t prange inside a loop (at least in my case). I moved the prange on the first loop and it doesn’t crash anymore ! :ok_hand:
Definitely going to use the optimizations you’ve suggested in my final version though !

I’m very grateful for all the explanations and tips about optimization you’ve provided !
Have a great day, best regards.
Damien

Good to hear you got it working!

Regarding the use of prange, using it on the inner loop shouldn’t crash it. So if that’s really the case for you it might be nice to try and make reproducible example of it.

When I tried replicating your initial attempt, the crash for occurred due to the indexing I mentioned. The code used allMinDists[idLoc] which was a 2D array of shape [1, n]. So it would fail trying to index that first (length 1) dimension with a higher value.

Thanks for getting back, I’ll try to reproduce it but you may totally be right !
I will try it and I’ll let you know if there is a reproductible example that I can provide you ! :slight_smile:
Thanks again for the help !

1 Like