Как избежать memcpy с flip и транспонировать в arrayfire?

Я использую arrayfire для ускорения некоторого кода C ++ с помощью графического процессора (OpenCL). У меня есть af :: массив 600 МБ и более, который мне нужно перевернуть по измерению столбца, а затем перенести его.

До сих пор я выполнял эти операции практически на месте с помощью подпрограммы C ++. Однако сейчас я хотел бы сделать это с AF, но заметил чрезмерное использование памяти в библиотеке AF. У меня есть две проблемы с этим:

1) Я совершенно не понимаю, почему любая операция (например, flip или T) в массиве 300 МБ должна когда-либо использовать намного больше, чем 900 МБ памяти.
2) Я хотел бы знать, как избежать создания копии массива foo. Я думал, что, заключив операции в отдельную функцию, я избавлюсь от любых копий.

У меня есть такой код:

void prepare_array(af::array &a) {
af::array b = af::flip(a, 1);              // ~1400MB
a = b.T();                                 // ~3000MB
}

af::array foo = af::randn(768,16384,3,1,c64); // ~300MB
prepare_array(foo);
af::deviceGC();                              // ~600MB

Мне нужно выполнить эту операцию только один раз, поэтому скорость менее важна, чем использование памяти, но я бы предпочел выполнять эти операции в рамках AF.

(Вся статистика использования памяти считывается с помощью gpustat из пакета драйверов ядра NVIDIA в Debian.)

Использование памяти также чрезмерно для серверной части процессора.


Благодаря ответу umar-arshad: Когда я в прошлый раз использовал профилирование mem-использования, я запустил код на процессоре — предполагая, что он будет вести себя одинаково. Я дважды проверил измерения на GPU и используя gpustat и nvidia-smi. На самом деле в коде измерения были разные и как ты объяснил. Теперь это имеет смысл — по крайней мере, в части графического процессора.

Возможно, на процессоре foo — это сначала только f64, потому что используется только действительная часть, и он становится c64, используя flip или транспонирование.

Тот факт, что «распределение запускает неявное устройство, синхронизируется во всех очередях на определенных платформах» вместе с этим веб-сайтом: http://forums.accelereyes.com/forums/viewtopic.php?f=17&т = 43097&р = 61730&hilit = копия + хост + память + в + а + массив # p61727
и af :: printMemInfo ();
помог мне, наконец, выяснить большую часть обработки памяти AF. Ускорение моей программы значительно.

Однако на данный момент единственной альтернативой для выполнения этих двух операций на месте (или с минимально возможными накладными расходами) является использование:

// Generate/store data in std::array<af::cdouble> foo_unwrap = new af::cdouble[768*16384*3*1];

// Flip/Transpose foo_unwrap in plain C/C++, like in:
// for(column = 0; column < max_num_column/2; column++)
//   swap column with max_num_column-1-column
//
// http://www.geeksforgeeks.org/inplace-m-x-n-size-matrix-transpose/
//   but for Column-Major Order matrices
//
// and afterwards whenever needed do ad-hoc:
af::cdouble* first_elem = (af::cdouble*) &(foo_unwrap[0]); // to ensure correct type detection via AF
af::array foo = af::array(768,16384,3,1, first_elem, afDevice);

Однако это довольно громоздко, потому что я не хотел беспокоиться о формате Row- / Column-Major и магии индекса. Так что я все еще ищу предложения здесь.

2

Решение

ArrayFire использует диспетчер памяти, чтобы избежать ненужного выделения и освобождения. Это необходимо, потому что все выделения запускают неявную синхронизацию устройства во всех очередях на определенных платформах. Это может стать очень дорогостоящим, поэтому ArrayFire будет отслеживать af::arrays, которые выходят за пределы и повторно используют их при необходимости. ArrayFire также выделяет память при запуске.

В вашем случае вы выделяете ~ 600 МБ при вызове RANDU (c64 является сложным двойным, поэтому каждый элемент составляет 16 байтов). Для операции переворота выделено еще 600 МБ памяти, которая хранится в b, Transpose выделит 600 МБ, но зарезервирует старые значения для повторного использования. На данный момент у вас есть около 1800 МБ памяти, выделенной из-за этих операций.

Когда вы вернетесь из prepared_array вызов функции b выйдет из области видимости и будет помечен для удаления. На данный момент у вас есть 3 буфера по 600 МБ каждый. Два буфера не используются, но ArrayFire может использовать эти буферы в будущих операциях. Оба неиспользуемых массива будут освобождены после вызова deviceGC Функция, хотя есть вероятность, что вы будете распределять массивы одинакового размера, поэтому полезно их хранить.

Вы можете отслеживать распределения, сделанные ArrayFire, используя af::printMemInfo() функция.

Disclamer: я один из разработчиков ArrayFire.

2

Другие решения

Других решений пока нет …