Одновременно умножьте все структурные элементы на скаляр

У меня есть структура, которая представляет вектор. Этот вектор состоит из двух однобайтовых целых чисел. Я использую их для сохранения значений от 0 до 255.

typedef uint8_T unsigned char;

struct Vector
{
uint8_T x;
uint8_T y;
};

Теперь основной вариант использования в моей программе — умножить оба элемента вектора на 32-битное значение с плавающей запятой:

typedef real32_T float;

Vector Vector::operator * ( const real32_T f ) const {
return Vector( (uint8_T)(x * f), (uint8_T)(y * f) );
};

Это нужно делать очень часто. Есть ли способ, что эти два умножения могут быть выполнены одновременно? Может быть, путем векторизации, SSE или подобным? Или компилятор Visual studio уже делает это одновременно?

Другой вариант использования — это интерполяция между двумя векторами.

Vector Vector::interpolate(const Vector& rhs, real32_T z) const
{
return Vector(
(uint8_T)(x + z * (rhs.x - x)),
(uint8_T)(y + z * (rhs.y - y))
);
}

Это уже использует оптимизированный подход интерполяции (https://stackoverflow.com/a/4353537/871495).

Но снова значения векторов умножаются на то же скалярное значение.
Есть ли возможность повысить производительность этих операций?

Спасибо

(Я использую Visual Studio 2010 с 64-битным компилятором)

0

Решение

По моему опыту, Visual Studio (особенно старая версия, такая как VS2010) сама по себе не делает много векторизации. Они улучшили его в более новых версиях, поэтому, если вы можете, вы можете увидеть, ускоряет ли ваш код изменение компилятора.

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

Вы можете попробовать следующее:

  • Если это еще не сделано, определите функции в заголовочном файле, чтобы компилятор мог встроить их
  • Если вы используете эти функции в узком цикле, попробуйте выполнить вычисления «вручную» без каких-либо вызовов функций (временно выставьте переменные) и посмотрите, имеет ли это разницу в скорости)
  • Если у вас много векторов, посмотрите, как они расположены в памяти. Храните их непрерывно, чтобы минимизировать потери в кеше.
  • Чтобы SSE работала действительно хорошо, вам нужно работать с 4 значениями одновременно, поэтому умножьте 2 вектора на 2 числа с плавающей точкой. В цикле используйте шаг 2 и напишите статическую функцию, которая рассчитывает 2 вектора одновременно, используя инструкции SSE. Поскольку ваши векторы не выровнены (и вряд ли когда-либо будут с 8-битными переменными), код может работать даже медленнее, чем сейчас, но это стоит попробовать.
  • Если применимо и если вы не зависите от зажима, который происходит с вашим отливкой из float в uint8_t (например, если ваши поплавки находятся в диапазоне [0,1]), попробуйте использовать float везде. Это может позволить компилятору делать гораздо лучшую оптимизацию.
1

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

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

Альтернативно, вы можете использовать lrint() сделать преобразование как объяснено Вот.

1

По вопросам рекламы [email protected]