Сроки приложения CUDA с использованием событий

Я использую следующие две функции для определения времени различных частей (cudaMemcpyHtoD, выполнения ядра, cudaMemcpyDtoH) моего кода (который включает в себя multi-gpus, параллельные ядра на одном и том же графическом процессоре, последовательное выполнение ядер и т. Д.). Как я понимаю, эти функции будут записывать время, прошедшее между событиями, но я предполагаю, что вставка событий в течение времени жизни кода может привести к накладным расходам и неточностям. Я хотел бы услышать критические замечания, общие советы по улучшению этих функций и предостережение уполномоченных относительно них.

//Create event and start recording
cudaEvent_t *start_event(int device, cudaEvent_t *events, cudaStream_t streamid=0)
{
        cutilSafeCall( cudaSetDevice(device) );
        cutilSafeCall( cudaEventCreate(&events[0]) );
        cutilSafeCall( cudaEventCreate(&events[1]) );
        cudaEventRecord(events[0], streamid);

    return events;
 }

 //Return elapsed time and destroy events
 float end_event(int device, cudaEvent_t *events, cudaStream_t streamid=0)
 {

        float elapsed = 0.0;
        cutilSafeCall( cudaSetDevice(device) );
        cutilSafeCall( cudaEventRecord(events[1], streamid) );
        cutilSafeCall( cudaEventSynchronize(events[1]) );
        cutilSafeCall( cudaEventElapsedTime(&elapsed, events[0], events[1]) );

        cutilSafeCall( cudaEventDestroy( events[0] ) );
        cutilSafeCall( cudaEventDestroy( events[1] ) );

        return elapsed;
 }

Использование:

cudaEvent_t *events;
cudaEvent_t event[2]; //0 for start and 1 for end
...
events = start_event( cuda_device, event, 0 );
<Code to time>
printf("Time taken for the above code... - %f secs\n\n", (end_event(cuda_device, events, 0) / 1000) );

1 ответ

Решение

Во-первых, если это для производственного кода, вы можете захотеть сделать что-то между вторым cudaEventRecord и cudaEventSynchronize(). В противном случае это может снизить способность вашего приложения перекрывать работу графического процессора и процессора.

Далее я бы отделил создание и уничтожение событий от записи событий. Я не уверен в стоимости, но в общем случае вы можете не вызывать cudaEventCreate и cudaEventDestroy часто.

Что бы я сделал, это создать такой класс

class EventTimer {
public:
  EventTimer() : mStarted(false), mStopped(false) {
    cudaEventCreate(&mStart);
    cudaEventCreate(&mStop);
  }
  ~EventTimer() {
    cudaEventDestroy(mStart);
    cudaEventDestroy(mStop);
  }
  void start(cudaStream_t s = 0) { cudaEventRecord(mStart, s); 
                                   mStarted = true; mStopped = false; }
  void stop(cudaStream_t s = 0)  { assert(mStarted);
                                   cudaEventRecord(mStop, s); 
                                   mStarted = false; mStopped = true; }
  float elapsed() {
    assert(mStopped);
    if (!mStopped) return 0; 
    cudaEventSynchronize(mStop);
    float elapsed = 0;
    cudaEventElapsedTime(&elapsed, mStart, mStop);
    return elapsed;
  }

private:
  bool mStarted, mStopped;
  cudaEvent_t mStart, mStop;
};

Обратите внимание, что я не включил cudaSetDevice() - мне кажется, что следует оставить код, который использует этот класс, чтобы сделать его более гибким. Пользователь должен убедиться, что одно и то же устройство активно при вызове запуска и остановки.

PS: NVIDIA не намерена полагаться на CUTIL для производственного кода - она ​​используется просто для удобства в наших примерах и не так тщательно тестируется или оптимизируется, как сами библиотеки CUDA и компиляторы. Я рекомендую вам распаковать такие вещи, как cutilSafeCall(), в свои собственные библиотеки и заголовки.

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