Python Numba Cuda Copy_To_Host Slow - PullRequest
       109

Python Numba Cuda Copy_To_Host Slow

0 голосов
/ 28 октября 2019

Я недавно начал изучать использование cuda для оптимизации поиска по числовым массивам. Ниже приведен упрощенный фрагмент кода, демонстрирующий эту проблему.

import numpy as np
import time
from numba import cuda


@cuda.jit
def count_array4(device_array,  pivot_point, device_output_array):
    for i in range(len(device_array)):
        if (pivot_point - 0.05) < device_array[i] < (pivot_point + 0.05):
            device_output_array[i] = True
        else:
            device_output_array[i] = False


width = 512
height = 512
size = width * height
print(f'Number of records {size}')
array_of_random = np.random.rand(size)
device_array = cuda.to_device(array_of_random)

start = time.perf_counter()
device_output_array = cuda.device_array(size)
print(f'Copy Host to Device: {time.perf_counter() - start}')

for x in range(10):
    start = time.perf_counter()
    count_array4[512, 512](device_array,  .5, device_output_array)
    print(f'Run: {x} Time: {time.perf_counter() - start}')

start = time.perf_counter()
output_array = device_output_array.copy_to_host()
print(f'Copy Device to Host: {time.perf_counter() - start}')

print(np.sum(output_array))

Это дает мне ожидаемую оптимизацию при обработке, однако время, необходимое для возврата данных на хост, кажется чрезвычайно высоким.

Number of records 262144
Copy Host to Device: 0.00031610000000004135
Run: 0 Time: 0.0958601
Run: 1 Time: 0.0001626999999999601
Run: 2 Time: 0.00012100000000003774
Run: 3 Time: 0.00011590000000005762
Run: 4 Time: 0.00011419999999995323
Run: 5 Time: 0.0001126999999999656
Run: 6 Time: 0.00011289999999997136
Run: 7 Time: 0.0001122999999999541
Run: 8 Time: 0.00011490000000002887
Run: 9 Time: 0.00011200000000000099
Copy Device to Host: 13.0583358
26110.0

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

1 Ответ

1 голос
/ 29 октября 2019

Запуски ядра являются асинхронными, и драйвер может поставить в очередь несколько запусков. В результате вы измеряете только издержки запуска ядра в цикле, а затем передача данных, которая является блокирующим вызовом, захватывает все время выполнения ядра. Вы можете изменить это поведение, изменив свой код следующим образом:

for x in range(10):
    start = time.perf_counter()
    count_array4[512, 512](device_array,  .5, device_output_array)
    cuda.synchronize()
    print(f'Run: {x} Time: {time.perf_counter() - start}')

Вызов синхронизации позволяет убедиться, что каждый запуск ядра завершен, а устройство бездействует до запуска другого ядра. Эффект должен состоять в том, что время работы каждого ядра будет увеличиваться, а указанное время передачи будет уменьшаться.

...