Я - pradeep и один из разработчиков ArrayFire.
Во-первых, все бэкэнды функций ArrayFire (CUDA и OpenCL) имеют некоторую стоимость запуска, которая включает прогрев устройства и / или кэширование ядра (ядра кэшируются.первый раз вызывается определенная функция).По этой причине вы замечаете лучшее время выполнения после первого запуска.Это также является причиной, по которой мы почти всегда настоятельно рекомендуем использовать нашу встроенную функцию timeit для определения времени кода массива, поскольку он усредняется по ряду прогонов, а не по первому прогону.
Как вы уже поняли из своих экспериментов, всегда лучше держать закрепленное распределение памяти контролируемым образом.Если вы еще не знаете о компромиссах, связанных с использованием закрепленной памяти, вы можете начать с этой записи в блоге от NVIDIA (в равной степени это относится к закрепленной памяти из бэкэнда OpenCL, с любыми ограничениями, определенными для поставщика, конечно).Общее руководство, предложенное в сообщении с гиперссылкой, выглядит следующим образом:
Не следует чрезмерно выделять закрепленную память.Это может снизить общую производительность системы, поскольку уменьшает объем физической памяти, доступной операционной системе и другим программам.Слишком много сложно сказать заранее, поэтому, как и во всех оптимизациях, протестируйте свои приложения и системы, на которых они работают, на предмет оптимальных параметров производительности.
Если возможно, ниже приведен маршрут Iпотребовалось бы использовать закрепленную память для ваших БПФ
- Инкапсулировать закрепленные выделения / освобождает в формате RAII, что вы уже делаете сейчас из своего отредактированного описания.
- Делайте только закрепленное выделение памятиодин раз, если это возможно - если ваш размер данных статичен.
Помимо этого, я думаю, что ваша функция неверна в двух отношениях.Я перейду к функции в порядке строк.
af :: af_cdouble * device_ptr = af :: pinned (signal.size ());
Этот вызов неНе выделяйте память на устройстве / графическом процессоре.Это заблокированная страница памяти на хосте, RAM.
af :: array s (signal.size (), device_ptr, afDevice);
Так как af:: pinned не выделяет память устройства, это не указатель устройства, а перечисление afHost.Таким образом, вызов будет af::array s(signal.size(), ptr);
Вы сами используете s.write
правильно, но я считаю, что это не нужно в вашем случае использования.
Следующее, что я буду делать.
- Используйте конструкцию RAII для указателя, возвращаемого
af::pinned
, и выделите его только один раз.Убедитесь, что у вас не слишком много этих выделенных страниц. - Используйте выделенное для страниц выделение как обычное размещение хоста вместо
std::vector<complex>
, потому что это память хоста, просто заблокированная страница.Это потребует написания дополнительного кода на стороне вашего хоста, если вы работаете с std::vector
каким-либо образом.В противном случае, вы можете просто использовать RAIIed-pinned-pointer для хранения ваших данных. - Все, что вам нужно сделать, это перенести данные FFT на устройство:
af::array s(size, ptr)
При этом, операции, которые вам нужно было бы перенести из перенесенной памяти в графический процессор, последний вызов в приведенном выше списке;исполнение fft;скопировать обратно на хост.