Как структурировать данные для оптимальной скорости в приложении CUDA - PullRequest
9 голосов
/ 01 февраля 2010

Я пытаюсь написать простую систему частиц, которая использует CUDA для обновления позиций частиц.Прямо сейчас я определяю, что частица имеет объект с положением, определенным с тремя значениями с плавающей точкой, и скоростью, также определенной с тремя значениями с плавающей точкой.При обновлении частиц я добавляю постоянное значение к компоненту скорости Y, чтобы имитировать гравитацию, затем добавляю скорость к текущей позиции, чтобы придумать новую позицию.С точки зрения управления памятью лучше поддерживать два отдельных массива с плавающей точкой для хранения данных или структурирования объектно-ориентированным способом.Примерно так:

struct Vector
{
    float x, y, z;
};

struct Particle
{
    Vector position;
    Vector velocity;
};

Кажется, что размер данных одинаков с любым из методов (4 байта на число с плавающей запятой, 3 числа с плавающей запятой на вектор, 2 вектора на частику, всего 24 байта)ОО-подход позволил бы обеспечить более эффективную передачу данных между процессором и графическим процессором, потому что я мог бы использовать один оператор копирования памяти вместо 2 (и в долгосрочной перспективе больше, так как есть еще несколько битов информации о частицах, которые станут актуальными,например, возраст, продолжительность жизни, вес / масса, температура и т. д.) А еще есть простота чтения кода и простота обращения с ним, что также заставляет меня склоняться к ОО-подходу.Но примеры, которые я видел, не используют структурированные данные, поэтому меня интересует, есть ли причина.

Итак, вопрос в том, что лучше: отдельные массивы данных или структурированные объекты?

1 Ответ

18 голосов
/ 01 февраля 2010

В параллельном программировании данных принято говорить о «Структуре массивов» (SOA) и «Массив структур» (AOS), где первый из двух примеров - AOS, а второй - SOA. Многие парадигмы параллельного программирования, в частности парадигмы в стиле SIMD, предпочитают SOA.

При программировании на GPU причина, по которой SOA обычно предпочитается, заключается в оптимизации доступа к глобальной памяти. Вы можете просмотреть записанную презентацию на Advanced CUDA C от GTC в прошлом году для подробного описания того, как GPU обращается к памяти.

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

С AOS:

position[base + tid].x = position[base + tid].x + velocity[base + tid].x * dt;
//  ^ write to every third address                    ^ read from every third address
//                           ^ read from every third address

С SOA:

position.x[base + tid] = position.x[base + tid] + velocity.x[base + tid] * dt;
//  ^ write to consecutive addresses                  ^ read from consecutive addresses
//                           ^ read from consecutive addresses

Во втором случае, чтение с последовательных адресов означает, что вы имеете 100% эффективность против 33% в первом случае. Обратите внимание, что на старых графических процессорах (с возможностью вычислений 1.0 и 1.1) ситуация намного хуже (эффективность 13%).

Существует еще одна возможность - если в структуре было два или четыре числа с плавающей запятой, вы можете прочитать AOS со 100% эффективностью:

float4 lpos;
float4 lvel;
lpos = position[base + tid];
lvel = velocity[base + tid];
lpos.x += lvel.x * dt;
//...
position[base + tid] = lpos;

Опять же, ознакомьтесь с презентацией Advanced CUDA C для подробностей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...