Контейнеры SSE и C ++ - PullRequest
       63

Контейнеры SSE и C ++

10 голосов
/ 07 марта 2011

Есть ли очевидная причина, по которой следующий код не работает?

#include <vector>
#include <emmintrin.h>

struct point {
    __m128i v;

  point() {
    v = _mm_setr_epi32(0, 0, 0, 0);
  }
};

int main(int argc, char *argv[])
{
  std::vector<point> a(3);
}

Спасибо

Редактировать: я использую g ++ 4.5.0 на linux / i686, возможно, я не знаю, чтоЯ делаю здесь, но так как даже следующие segfaults

int main(int argc, char *argv[])
{
  point *p = new point();
}

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

Ответы [ 4 ]

11 голосов
/ 07 марта 2011

Очевидная вещь, которая могла бы пойти не так, была бы, если бы v не был правильно выровнен.

Но он распределяется динамически с помощью vector, поэтому он не подвержен проблемам смещения стека.

Однако, как правильно указывает phooji , значение «template» или «prototype» передается конструктору std::vector, который будет скопирован во все элементы вектора.Именно этот параметр std::vector::vector будет помещен в стек и может быть смещен.

У некоторых компиляторов есть прагма для управления выравниванием стека внутри функции (в основном, компилятор тратит некоторое дополнительное пространство, необходимое для получениявсе локальные объекты правильно выровнены).

Согласно документации Microsoft, Visual C ++ 2010 должен автоматически настроить 8-байтовое выравнивание стека для типов SSE , и делает это с Visual C ++ 2003

Для gcc я не знаю.


Под C ++ 0x, для new point() возвращать невыровненное хранилище - серьезное несоответствие.[basic.stc.dynamic.allocation] говорит (формулировка из черновика n3225):

Функция выделения пытается выделить запрошенный объем памяти.Если он успешен, он должен вернуть адрес начала блока хранения, длина которого в байтах должна быть, по крайней мере, такой же, как запрашиваемый размер.Нет ограничений на содержимое выделенного хранилища при возврате из функции выделения.Порядок, смежность и начальное значение хранилища, выделенного последовательными вызовами функции выделения, не определены.Возвращаемый указатель должен быть соответствующим образом выровнен, чтобы его можно было преобразовать в указатель любого полного типа объекта с фундаментальным требованием выравнивания (3.11), а затем использовать для доступа к объекту или массиву в выделенном хранилище (пока хранилище не будет явно освобожденовызов соответствующей функции освобождения).

И [basic.align] говорит:

Кроме того, запрос на динамическое хранение во время выполнения, для которого запрошенное выравнивание не может бытьhonored должно рассматриваться как сбой при выделении.

Можете ли вы попробовать более новую версию gcc, где это может быть исправлено?

3 голосов
/ 07 марта 2011

Конструктор vector, который вы используете, на самом деле определяется следующим образом:

explicit vector ( size_type n, const T& value= T(), const Allocator& = Allocator() );

(см., Например, http://www.cplusplus.com/reference/stl/vector/vector/).

Другими словами, один элемент создается по умолчанию (т.е. значение параметра по умолчанию при вызове конструктора), а остальные элементы затем создаются путем копирования первого. Я предполагаю, что вам нужен конструктор копирования для point, который правильно обрабатывает (не) копирование значений __m128i.

Обновление: Когда я пытаюсь собрать код с помощью Visual Studio 2010 (v. 10.0.30319.1), я получаю следующую ошибку сборки:

error C2719: '_Val': formal parameter with __declspec(align('16')) won't be aligned c:\program files\microsoft visual studio 10.0\vc\include\vector 870 1   meh

Это говорит о том, что Бен прав, потому что это проблема выравнивания.

1 голос
/ 07 марта 2011

Встроенные функции SSE должны быть выровнены в памяти на 16 байт.Когда вы выделяете __m128 в стеке, проблем не возникает, потому что компилятор автоматически выравнивает их правильно.Распределитель по умолчанию для std::vector<>, который обрабатывает динамическое выделение памяти, не производит выравниваемые выделения.

1 голос
/ 07 марта 2011

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

Обычно распределитель по умолчанию использует оператор new, который обычно не гарантирует выравнивание за пределами размера слова (32-разрядного или 64-разрядного).Чтобы решить эту проблему, может потребоваться реализовать специальный распределитель, который использует _aligned_malloc.

. Кроме того, простым исправлением (хотя и не удовлетворительным) будет присвоение значения локальному __m128iпеременной, затем скопируйте эту переменную в структуру с помощью невыровненной инструкции.Пример:

struct point {
    __m128i v;
    point() {
        __m128i temp = _mm_setr_epi32(0, 0, 0, 0);
        _mm_storeu_si128(&v, temp);
    }
};
...