Python для векторизации цикла numpy

Есть ли у вас какие-либо предложения, как я могу ускорить функцию «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 поверх f_img_update с помощью этого кода, вы зафиксируете время компиляции. Это могло бы сбить ситуацию с толку.

user2357112 11.07.2024 22:07

@jit даже не работает. Он выдает «TypeError: невозможно отразить элемент отраженного контейнера: отраженный список (отраженный список (float64) <iv=None>) <iv=None>»

dany 11.07.2024 22:08
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
2
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Самый простой способ — преобразовать 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

dany 11.07.2024 23:45

Можете ли вы объяснить мне, что такое «assert np.allclose» и как скомпилировать только numba-функцию?

dany 11.07.2024 23:57

@dany Это только для проверки того, что выходные данные функции numba и обычной функции одинаковы (np.allclose). Запуск этого теста в первый раз также компилирует функцию numba, поэтому время правильное (без этапа компиляции).

Andrej Kesely 12.07.2024 00:03

Я понимаю. Первый запуск функции происходит медленно, но последующие выполняются быстро. Есть ли какой-нибудь способ запустить/скомпилировать его, прежде чем мне действительно понадобится запустить функцию?

dany 12.07.2024 00:05

@dany Да, вы можете указать подпись явно, например. @njit("uint16[:, :, :](uint16[:, :, :], float64, float64, float64[:, :], float64)")

Andrej Kesely 12.07.2024 00:11

Это помогает, первый прогон происходит намного быстрее, но последующие прогоны еще быстрее :D Я изменил размеры на (1000, 1000), чтобы получить более дифференциальные результаты. При первом запуске значение снижается с 0,501 до 0,065. Следующие прогоны занимают от 0,003 до 0,004 с.

dany 12.07.2024 00:18

@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)")., что является более общим, но может привести к снижению производительности. .

max9111 12.07.2024 09:45

Другие вопросы по теме