Отрицательный прирост скорости с использованием Numba Vectorize target =’cuda’

#cuda #anaconda #vectorization #numba

#cuda #anaconda #векторизация #numba

Вопрос:

Я пытаюсь проверить эффективность использования декоратора @vectorize модуля Python Numba для ускорения фрагмента кода, соответствующего моему фактическому коду. Я использую фрагмент кода, представленный в CUDAcast # 10, доступный здесь и показанный ниже:

 import numpy as np
from timeit import default_timer as timer
from numba import vectorize


@vectorize(["float32(float32, float32)"], target='cpu')
def VectorAdd(a,b):
        return a   b

def main():
        N = 32000000

        A = np.ones(N, dtype=np.float32)
        B = np.ones(N, dtype=np.float32)
        C = np.zeros(N, dtype=np.float32)


        start = timer()
        C = VectorAdd(A, B)
        vectoradd_time = timer() - start

        print("C[:5] = "   str(C[:5]))
        print("C[-5:] = "   str(C[-5:]))

        print("VectorAdd took %f seconds" % vectoradd_time)

if __name__ == '__main__':
        main()
  

В демонстрации в CUDAcast демонстратор получает 100-кратное ускорение, отправляя уравнение большого массива на графический процессор через декоратор @vectorize. Однако, когда я устанавливаю цель @vectorize для графического процессора:

 @vectorize(["float32(float32, float32)"], target='cuda')
  

… результат в 3-4 раза медленнее. С target = ‘cpu’ мое время выполнения составляет 0,048 секунды; с target = ‘cuda’ мое время выполнения составляет 0,22 секунды. Я использую ноутбук DELL Precision с процессором Intel Core i7-4710MQ и графическим процессором NVIDIA Quadro K2100M. Результаты запуска nvprof (NVIDIA profiler tool) показывают, что большая часть времени тратится на обработку памяти (ожидаемо), но даже оценка функции на GPU занимает больше времени, чем весь процесс на CPU. Очевидно, что это не тот результат, на который я надеялся, но связано ли это с какой-то ошибкой с моей стороны или это разумно, исходя из моего оборудования и кода?

Ответ №1:

Этот вопрос также интересен для меня. Я попробовал ваш код и получил похожие результаты. Чтобы как-то исследовать эту проблему, я написал ядро CUDA с использованием cuda.jit и добавил его в ваш код:

 import numpy as np
from timeit import default_timer as timer
from numba import vectorize, cuda

N = 16*50000 #32000000
blockdim = 16, 1
griddim = int(N/blockdim[0]), 1

@cuda.jit("void(float32[:], float32[:])")
def VectorAdd_GPU(a, b):
    i = cuda.grid(1)
    if i < N:
        a[i]  = b[i]

@vectorize("float32(float32, float32)", target='cpu')
def VectorAdd(a,b):
    return a   b


A = np.ones(N, dtype=np.float32)
B = np.ones(N, dtype=np.float32)
C = np.zeros(N, dtype=np.float32)

start = timer()
C = VectorAdd(A, B)
vectoradd_time = timer() - start
print("VectorAdd took %f seconds" % vectoradd_time)

start = timer()
d_A = cuda.to_device(A)
d_B = cuda.to_device(B)
VectorAdd_GPU[griddim,blockdim](d_A, d_B)
C = d_A.copy_to_host()
vectoradd_time = timer() - start
print("VectorAdd_GPU took %f seconds" % vectoradd_time)

print("C[:5] = "   str(C[:5]))
print("C[-5:] = "   str(C[-5:]))
  

В этом «тесте» я также учитываю время копирования массивов с хоста на устройство и с устройства на хост. В этом случае функция GPU работает медленнее, чем функция CPU.

Для случая, приведенного выше:

 CPU - 0.0033; 
GPU - 0.0096; 
Vectorize (target='cuda') - 0.15 (for my PC).
  

Если время копирования не учитывается:

 GPU - 0.000245
  

Итак, что я узнал, (1) Копирование с хоста на устройство и с устройства на хост занимает много времени. Это очевидно и хорошо известно. (2) Я не знаю причину, но @vectorize может значительно замедлить вычисления на GPU. (3) Лучше использовать самописные ядра (и, конечно, минимизировать копирование памяти).

Кстати, я также протестировал @cuda.jit, решив уравнение теплопроводности с помощью явной конечно-разностной схемы и обнаружил, что в этом случае время выполнения программы на python сопоставимо с программой на C и обеспечивает ускорение примерно в 100 раз. Это потому, что, к счастью, в этом случае вы можете выполнять много итераций без обмена данными между хостом и устройством.

UPD. Используемое программное и аппаратное обеспечение: Win7 64bit, процессор: Intel Core2 Quad 3 ГГц, графический процессор: NVIDIA GeForce GTX 580.

Комментарии:

1. Роман, спасибо за «проверку работоспособности». Я также ценю ваш код @cuda.jit. Я новичок в Numba и CUDA, так что это будет отличный следующий путь для меня! Можете ли вы добавить в свой пост, какое оборудование вы используете (процессор, графический процессор и т. Д.)?