переупорядочивание трехмерных векторных триплетов в основном порядке столбцов происходит медленно

#c #c #sse #simd

#c #c #sse #simd

Вопрос:

У меня много (x1, y1, z1), (x2, y2, z2), (x3, y3, z3) векторных триплетов одинарной точности, и я хочу изменить их порядок, поэтому (x1, y1, z1), (x2, y2, z2), (x3, y3, z3) становится (x1, x2, x3,0, y1, y2, y3,0, z1, z2, z3,0)

Цель состоит в том, чтобы подготовить набор данных для вычисления на основе SSE. Для этого у меня есть следующий код:

 for (int i=0;i<count;i  )
{
    Vect3F p0 = get_first_point(i);
    Vect3F p1 = get_second_point(i);
    Vect3F p2 = get_third_point(i);
    int idx = i*3;
    scratch[idx] = Vec4F(p0.x, p1.x, p2.x, 0); // These 3 rows are the slowest
    scratch[idx 1] = Vec4F(p0.y, p1.y, p2.y, 0);
    scratch[idx 2] = Vec4F(p0.z, p1.z, p2.z, 0);
}
 

Последние 3 строки цикла выполняются чрезвычайно медленно, они занимают 90% времени всего моего алгоритма!

Это нормально? Могу ли я ускорить такую перетасовку? (scratch — это статическая переменная, выровненная по 16. Функция вызывается часто, поэтому я думаю, что блоки scratch не должны исчезать из кэша.)

Комментарии:

1. Похоже, здесь создается много временных объектов. Надеюсь, компилятор устраняет конструкторы, присваивания и т. Д. Рассматривали ли вы возможность реализации членов Vect3F, Vect4F как объединение с выровненным типом SSE (__m128)?

2. К сожалению, Vect3F не удалось выровнять, он должен быть длиной 12 байт, поэтому я не могу использовать SSE при переупорядочении чисел с плавающей запятой. Когда это будет сделано, я использую _mm_load_ps для загрузки данных в регистры (и это быстро). Теперь я расширил все свои конструкторы и назначения: float* a = (float*)(cache i*3); a[0] = p0.x; a[1] = p1.x; a[2] = p2.x; a[4] = p0.y; a[5] = p1.y; a[6] = p2.y; a[8] = p0.z; a[9] = p1.z; a[10] = p2.z; немного помогло, но все равно очень медленно.

3. Этот фрагмент даже компилируется? Вы объявляете Vect3F p0 три раза!

4. По сути, это транспонирование матрицы. Транспонируйте Google sse, и вы получите несколько более быстрых версий.

5. Даже если компилятор оптимизирует доступ к временным объектам p0, p1, p2, было бы лучше использовать: const Vect3F amp; p0 = points[i];

Ответ №1:

Прежде всего, вы не должны создавать 3 временных векторных объекта. Вместо:

 tri = triangles[i];
Vect3F p0 = points[indices[tri]];
Vect3F p1 = points[indices[tri 1]];
Vect3F p2 = points[indices[tri 2]];
 

Вы должны просто скопировать данные с помощью memcpy(); Создайте цикл, который распространяется на всю вашу коллекцию и копирует необработанные данные. Это самый быстрый способ, который я могу придумать.

Использование 3 переменных запускает множество конструкторов, которые работают мучительно медленно. Второй способ (из комментария) не намного лучше по той же причине.

Комментарии:

1. Заставляет меня задуматься, какие компиляторы вы используете и какие флаги. Я думаю, что код должен быть в некоторой степени отображен одинаково с включенной надлежащей оптимизацией.