Есть ли у вас какие-либо предложения, как я могу ускорить функцию «f_img_update» в приведенном ниже коде? Я попробовал добавить декоратор numba @jit, но безуспешно. Я предполагаю, что векторизация numpy может сработать, но я недостаточно образован, чтобы заставить ее работать самостоятельно:/
Код:
import numpy as np
import time
import random
from random import randrange
dimensions = (100, 50) # [m]
resolution = 5 # [cm] 1,2,4,5,10,20,25,50,100
pix_res = int(100 / resolution)
height = dimensions[0] * pix_res
width = dimensions[1] * pix_res
img_size = (height, width)
# Make "RGBA" image
img = np.zeros((*img_size, 4), np.uint16)
# DATA PREPARATION
xmin = 1700000
ymin = 1400000
zmin = 100
xmax = xmin + dimensions[0]
ymax = ymin + dimensions[1]
zmax = 150
data = []
for i in range(100000):
xyzi = [random.uniform(xmin, xmax), random.uniform(ymin, ymax), random.uniform(zmin, zmax), randrange(255)]
data.append(xyzi)
# IMAGE UPDATE
def f_img_update(f_img, f_xmin, f_ymin, f_data, f_pix_per_1m): # IMAGE UPDATE
for f_xyzi in f_data:
f_x_img, f_y_img = f_xyzi[0:2]
f_x_img = int((f_x_img - f_xmin) * 100 / f_pix_per_1m)
f_y_img = int((f_y_img - f_ymin) * 100 / f_pix_per_1m)
f_img[f_x_img][f_y_img] = f_xyzi
return f_img
t1 = time.time()
img = f_img_update(img, xmin, ymin, data, pix_res)
t2 = time.time()
print("image update time: ", t2 - t1)
@jit даже не работает. Он выдает «TypeError: невозможно отразить элемент отраженного контейнера: отраженный список (отраженный список (float64) <iv=None>) <iv=None>»
Самый простой способ — преобразовать data
из стандартного списка Python в np.array
, а затем использовать декоратор @njit
:
import random
import time
from random import randrange
import numpy as np
from numba import njit
dimensions = (100, 50) # [m]
resolution = 5 # [cm] 1,2,4,5,10,20,25,50,100
pix_res = int(100 / resolution)
height = dimensions[0] * pix_res
width = dimensions[1] * pix_res
img_size = (height, width)
# Make "RGBA" image
img = np.zeros((*img_size, 4), np.uint16)
# DATA PREPARATION
xmin = 1700000
ymin = 1400000
zmin = 100
xmax = xmin + dimensions[0]
ymax = ymin + dimensions[1]
zmax = 150
data = []
for i in range(100_000):
xyzi = [
random.uniform(xmin, xmax),
random.uniform(ymin, ymax),
random.uniform(zmin, zmax),
randrange(255),
]
data.append(xyzi)
data = np.array(data)
# IMAGE UPDATE
def f_img_update(f_img, f_xmin, f_ymin, f_data, f_pix_per_1m): # IMAGE UPDATE
for f_xyzi in f_data:
f_x_img = f_xyzi[0]
f_y_img = f_xyzi[1]
f_x_img = int((f_x_img - f_xmin) * 100 / f_pix_per_1m)
f_y_img = int((f_y_img - f_ymin) * 100 / f_pix_per_1m)
f_img[f_x_img, f_y_img] = f_xyzi
return f_img
@njit
def f_img_update_numba(f_img, f_xmin, f_ymin, f_data, f_pix_per_1m): # IMAGE UPDATE
for f_xyzi in f_data:
f_x_img = f_xyzi[0]
f_y_img = f_xyzi[1]
f_x_img = int((f_x_img - f_xmin) * 100 / f_pix_per_1m)
f_y_img = int((f_y_img - f_ymin) * 100 / f_pix_per_1m)
f_img[f_x_img, f_y_img] = f_xyzi
return f_img
# compile it first
assert np.allclose(
f_img_update(img, xmin, ymin, data, pix_res),
f_img_update_numba(img, xmin, ymin, data, pix_res),
)
img_copy = img.copy()
t1 = time.time()
_ = f_img_update(img_copy, xmin, ymin, data, pix_res)
t2 = time.time()
print("image update normal: ", t2 - t1)
img_copy = img.copy()
t1 = time.time()
_ = f_img_update_numba(img_copy, xmin, ymin, data, pix_res)
t2 = time.time()
print("image update time numba: ", t2 - t1)
Печатает на моем AMD 5700x:
image update normal: 0.07558226585388184
image update time numba: 0.001255035400390625
У меня более медленный AMD Ryzen 5 4600H, и Numba дает мне еще большую разницу! Спасибо! Результаты: (мой цикл for) время обновления изображения: 0,11602473258972168 обычное обновление изображения: 0,14102983474731445 время обновления изображения число: 0,0030028820037841797
Можете ли вы объяснить мне, что такое «assert np.allclose» и как скомпилировать только numba-функцию?
@dany Это только для проверки того, что выходные данные функции numba и обычной функции одинаковы (np.allclose). Запуск этого теста в первый раз также компилирует функцию numba, поэтому время правильное (без этапа компиляции).
Я понимаю. Первый запуск функции происходит медленно, но последующие выполняются быстро. Есть ли какой-нибудь способ запустить/скомпилировать его, прежде чем мне действительно понадобится запустить функцию?
@dany Да, вы можете указать подпись явно, например. @njit("uint16[:, :, :](uint16[:, :, :], float64, float64, float64[:, :], float64)")
Это помогает, первый прогон происходит намного быстрее, но последующие прогоны еще быстрее :D Я изменил размеры на (1000, 1000), чтобы получить более дифференциальные результаты. При первом запуске значение снижается с 0,501 до 0,065. Следующие прогоны занимают от 0,003 до 0,004 с.
@dany Вы можете использовать @njit(cache=True)
для сохранения результата компиляции. numba.pydata.org/numba-doc/latest/developer/caching.html Также имейте в виду, что подпись для C-непрерывных массивов будет @njit("uint16[:, :, ::1](uint16[:, :, ::1], float64, float64, float64[:, ::1], float64)")
вместо @njit("uint16[:, :, :](uint16[:, :, :], float64, float64, float64[:, :], float64)")
., что является более общим, но может привести к снижению производительности. .
Если вы просто поставите
@jit
поверхf_img_update
с помощью этого кода, вы зафиксируете время компиляции. Это могло бы сбить ситуацию с толку.