Сроки приложения 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(), в свои собственные библиотеки и заголовки.