2013-09-05 5 views
0

Я использую некоторые классы, которые используют некоторые контейнеры для хранения данных; существуют классы с многомерными контейнерами. Эти классы перегружают operator() для индексации данных. Я использую такие объекты много в циклах и хочу их векторизовать. GCC не может напрямую их векторизовать; он говорит: «Никаких возможностей SLP, найденных в базовом блоке» и не отменяет векторизации.
Как бы я начал векторизовать свой код?Как векторизовать цикл с функторами?

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

+0

, когда вы говорите "контейнер" означает вы СТЛ контейнер? Если вы хотите иметь векторизацию своих алгоритмов, вам нужно будет очень аккуратно управлять своей памятью, если это возможно, без таковых контейнеров. – hidrargyro

+0

Вам нужно предоставить оператор '.data()', который возвращает ограниченный вектор или '. doVectorized() 'операция. – Mikhail

ответ

2

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

Однако, даже если ваша память хорошо выровнена, есть еще одна возможность, которая может задержать вас. Георг Hager х Герхарды Wellein, авторы уважаемой книги «Введение в высокопроизводительные вычисления для ученых и производственников», Явное состояние, что C++ перегрузка оператора может предотвратить петлю векторизации

По их собственным словам:

«(.. ..) STL может определить этот оператор следующим образом (адаптировано из ГНУ ISO C++ исходный библиотека):

const T& operator[](size_t __n) const{ return *(this->_M_impl._M_start + __n); } 

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

Хороший друг убедил меня, что это больше не так для контейнеров stl, потому что компиляторы могут устранить слой косвенности, связанный с operator[]. Но, кажется, вы написали свой собственный контейнер, поэтому вы должны проверить, может ли компилятор устранить слой косвенности, связанный с вашим собственным operator()! Хорошая кросс-проверка заключается в том, чтобы предоставить вам возможность напрямую работать с базовым массивом, который хранится в вашем контейнере (что означает: написать функцию-член, аналогичную std::vector.data(), и использовать указатели C как «итератор» внутри вашей петли).

Сноска о выравнивании памяти:

Проблема: предположим, что вы хотите векторизации c[i] = a[i] + b[i].

Первый факт: size(double) = 8 bytes = 64 бит.

Второй факт: Существует инструкция сборки, которая считывает 2 двойников в памяти и поставить их на 128 бит регистра => с одной инструкцией сборки вы можете прочитать 2 двойников => они могут читать a[0] и a[1] затем b[0] и b[1]!

Третий факт: когда вы применяете инструкцию в регистре, вы одновременно делаете 2 суммы из 64 бит double.

Проблема заключается в том, что сборка может читать только a[0] и a[1] в то же время, только если a[0] и b[0] в адреса памяти, которые кратны 16 (если они не являются, он может проверить, если a[1] и b[1] является выравнивание и т.д. д). Вот почему память может быть проблемой, которая препятствует векторизации. Чтобы исправить это, вы должны написать контейнеры-распределители, которые гарантируют, что первый элемент вашего контейнера будет записан на адрес памяти, который кратен 16.

Обновление: This answer содержит подробное описание того, как закодировать распределитель, который выравнивает вашей памяти.

Update 2: еще один полезный answer для обучения, как код распределители

Update 3: альтернативный подход с использованием posix_memalign/_aligned_malloc