Мы с коллегой провели мозговой штурм о том, как уменьшить время передачи памяти между хостом и устройством, и выяснилось, что, возможно, может помочь организация вещей для одного мега-переноса (то есть одного единственного вызова). Это привело меня к созданию контрольного примера, в котором я использовал время для передачи нескольких больших порций данных против множества маленьких порций данных. Я получил очень интересные / странные результаты, и мне было интересно, если у кого-нибудь есть объяснение?
Я не буду помещать здесь весь свой код, поскольку он довольно длинный, но я проверил порцию двумя разными способами:
Явно выписывает все cudaMemcpy's, например ::
cudaEventRecord (начало, 0);
cudaMemcpy (aD, a, nBytes / 10, cudaMemcpyHostToDevice);
cudaMemcpy (aD + 1 * nBytes / 10, a + 1 * nBytes / 10, nBytes / 10, cudaMemcpyHostToDevice);
cudaMemcpy (aD + 2 * nBytes / 10, a + 2 * nBytes / 10, nBytes / 10, cudaMemcpyHostToDevice);
cudaMemcpy (aD + 3 * nBytes / 10, a + 3 * nBytes / 10, nBytes / 10, cudaMemcpyHostToDevice);
cudaMemcpy (aD + 4 * nBytes / 10, a + 4 * nBytes / 10, nBytes / 10, cudaMemcpyHostToDevice);
cudaMemcpy (aD + 5 * nBytes / 10, a + 5 * nBytes / 10, nBytes / 10, cudaMemcpyHostToDevice);
cudaMemcpy (aD + 6 * nBytes / 10, a + 6 * nBytes / 10, nBytes / 10, cudaMemcpyHostToDevice);
cudaMemcpy (aD + 7 * nBytes / 10, a + 7 * nBytes / 10, nBytes / 10, cudaMemcpyHostToDevice);
cudaMemcpy (aD + 8 * nBytes / 10, a + 8 * nBytes / 10, nBytes / 10, cudaMemcpyHostToDevice);
cudaMemcpy (aD + 9 * nBytes / 10, a + 9 * nBytes / 10, nBytes / 10, cudaMemcpyHostToDevice);
cudaEventRecord (стоп, 0);
cudaEventSynchronize (остановка); * * тысяча двадцать-одна
cudaEventElapsedTime (& время, начало, остановка);
Помещение cudaMemcpy в цикл for:
cudaEventRecord (начало, 0);
for (int i = 0; i
{
cudaMemcpy (aD + i * nBytes / nChunks, a + i * nBytes / nChunks, nBytes / nChunks,
cudaMemcpyHostToDevice);
}
cudaEventRecord (стоп, 0);
cudaEventSynchronize (остановка);
cudaEventElapsedTime (& время, начало, остановка);
Отметим, что на всякий случай я также выполнял разогрев в начале каждого теста, хотя я не думаю, что это было необходимо (контекст был создан вызовом cudaMalloc).
Я проверил это на всех объемах передачи в диапазоне от 1 МБ до 1 ГБ, где в каждом тестовом примере передавалось одинаковое количество информации независимо от того, как она была распределена. Пример моего вывода такой:
один большой перевод = 0,451616 мс
10 явных передач = 0,198016 мс
100 явных передач = 0,691712 мс
10 зацикленных передач = 0,174848 мс
100 цикловых передач = 0,683744 мс
1000 циклических передач = 6,145792 мс
10000 зацикленных передач = 104,981247 мс
100000 циклических передач = 13097,441406 мс
Что интересно здесь, и чего я не понимаю, так это то, что 10 переводов были ВСЕГДА быстрее на значительную сумму, чем любой другой, даже один большой перевод! И этот результат оставался неизменным независимо от того, насколько большим или маленьким был набор данных (т. Е. 10x100 МБ против 1x1 ГБ или 10x1 МБ против 1x10 МБ по-прежнему приводят к увеличению скорости в 10 раз). Если у кого-то есть понимание того, почему это так или что я могу делать неправильно, чтобы получить эти странные цифры, мне было бы очень интересно услышать, что вы хотите сказать.
Спасибо!
P.S. Я знаю, что cudaMemcpy несет с собой неявную синхронизацию, и поэтому я мог бы использовать таймер ЦП, а cudaEventSynchronize является избыточным, но я решил, что лучше быть на безопасной стороне
ОБНОВЛЕНИЕ: Я написал функцию, чтобы попытаться воспользоваться этим очевидным разрывом в пространственно-временном континууме производительности. Когда я использую эту функцию, которая написана EXACLTY, как в моих тестовых случаях, эффект исчезает, и я вижу то, что ожидаю (один cudaMemcpy самый быстрый). Возможно, это все больше похоже на квантовую физику, чем на теорию относительности, в которой акт наблюдения меняет поведение ...