这里有一些想法:
import numpy as np
from scipy.signal import convolve2d
from numba import njit, prange
arr0 = np.array(
[[5, 2, 6],
[4, 5, 6],
[7, 12, 3]])
np.random.seed(0)
arr1 = np.random.randint(0, 20, (20, 20))
def weird_quazi_det_op(arr):
H, W = arr.shape
out_arr = np.zeros((H-1, W-1))
for h in prange(H-1):
for w in prange(W-1):
out_arr[h, w] = (arr[h, w] - arr[h+1, w+1])**2 + (arr[h+1, w] - arr[h, w+1])**2
return out_arr
@njit(parallel=True)
def weird_quazi_det_patallel(arr):
H, W = arr.shape
out_arr = np.zeros((H-1, W-1))
for h in prange(H-1):
for w in prange(W-1):
out_arr[h, w] = (arr[h, w] - arr[h+1, w+1])**2 + (arr[h+1, w] - arr[h, w+1])**2
return out_arr
@njit(fastmath=True)
def weird_quazi_det_fastmath(arr):
H, W = arr.shape
out_arr = np.zeros((H-1, W-1))
for h in prange(H-1):
for w in prange(W-1):
out_arr[h, w] = (arr[h, w] - arr[h+1, w+1])**2 + (arr[h+1, w] - arr[h, w+1])**2
return out_arr
def weird_quazi_det_conv(arr):
sub0kern = np.array([[1, 0,], [0, -1]])
sub1kern = np.array([[0, -1,], [1, 0]])
sub0arr = convolve2d(arr, sub0kern, mode = 'valid')
sub1arr = convolve2d(arr, sub1kern, mode = 'valid')
out_arr = sub0arr**2+sub1arr**2
return out_arr
# init runs
weird_quazi_det_patallel(arr0)
weird_quazi_det_fastmath(arr0)
print('===== op example: =====')
print(' - op code:')
%timeit weird_quazi_det_op(arr0)
print(' - op code + parallel:')
%timeit weird_quazi_det_patallel(arr0)
print(' - op code + fastmath:')
%timeit weird_quazi_det_fastmath(arr0)
print(' - conv solution:')
%timeit weird_quazi_det_conv(arr0)
print('===== larger example: =====')
print(' - op code:')
%timeit weird_quazi_det_op(arr1)
print(' - op code + parallel:')
%timeit weird_quazi_det_patallel(arr1)
print(' - op code + fastmath:')
%timeit weird_quazi_det_fastmath(arr1)
print(' - conv solution:')
%timeit weird_quazi_det_conv(arr1)
输出:
===== op example: =====
- op code:
The slowest run took 6.15 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 5: 10.6 µs per loop
- op code + parallel:
The slowest run took 251.69 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 5: 5.71 µs per loop
- op code + fastmath:
The slowest run took 32.22 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 5: 678 ns per loop
- conv solution:
The slowest run took 6.65 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 5: 18.7 µs per loop
===== larger example: =====
- op code:
1000 loops, best of 5: 714 µs per loop
- op code + parallel:
The slowest run took 180.35 times longer than the fastest. This could mean that an intermediate result is being cached.
100000 loops, best of 5: 6.33 µs per loop
- op code + fastmath:
The slowest run took 14.04 times longer than the fastest. This could mean that an intermediate result is being cached.
1000000 loops, best of 5: 1.42 µs per loop
- conv solution:
10000 loops, best of 5: 41 µs per loop
我认为使用卷积来执行局部对角线减法,然后对差异求和将是最快的解决方案,但似乎没有超过 numba 编译代码的速度,即使使用最简单的实现也是如此。