Почему мои данные не выровнены? - PullRequest
4 голосов
/ 04 июня 2010

Я пытаюсь выяснить, как лучше предварительно рассчитать некоторые значения синуса и косинуса, сохранить их в выровненных блоках, а затем использовать их позже для вычислений SSE:

В начале моей программы,Я создаю объект с членом:

static __m128 *m_sincos;

, затем инициализирую этот член в конструкторе:

m_sincos = (__m128*) _aligned_malloc(Bins*sizeof(__m128), 16);
for (int t=0; t<Bins; t++)
  m_sincos[t] = _mm_set_ps(cos(t), sin(t), sin(t), cos(t));



Когда я иду использовать m_sincos, я сталкиваюсь стри проблемы:
- Кажется, данные не выровнены

movaps xmm0, m_sincos[t] //crashes
movups xmm0, m_sincos[t] //does not crash

- Кажется, переменные не верны

movaps result, xmm0 // returns values that are not what is in m_sincos[t]
//Although, putting a watch on m_sincos[t] displays the correct values

- Что меня действительно смущает, так этозаставляет все работать (но слишком медленно):

__m128 _sincos = m_sincos[t];
movaps xmm0, _sincos
movaps result, xmm0

Ответы [ 2 ]

10 голосов
/ 04 июня 2010

m_sincos[t] является выражением C. Однако в инструкции по сборке (__asm?) Это интерпретируется как режим адресации x86 с совершенно другим результатом. Например, VS2008 SP1 компилируется:

movaps xmm0, m_sincos[t]

into: (см. Окно разборки при сбое приложения в режиме отладки)

movaps xmm0, xmmword ptr [t]

Эта интерпретация пытается скопировать 128-битное значение, хранящееся по адресу переменной t, в xmm0. t, однако, является 32-битным значением по вероятному невыровненному адресу. Выполнение инструкции может вызвать сбой выравнивания и приведет к неверным результатам в нечетном случае, когда адрес t выровнен.

Вы можете исправить это, используя соответствующий режим адресации x86. Вот медленная, но ясная версия:

__asm mov eax, m_sincos                  ; eax <- m_sincos
__asm mov ebx, dword ptr t
__asm shl ebx, 4                         ; ebx <- t * 16 ; each array element is 16-bytes (128 bit) long
__asm movaps xmm0, xmmword ptr [eax+ebx] ; xmm0 <- m_sincos[t]

Sidenote:

Когда я помещаю это в полную программу, происходит что-то странное:

#include <math.h>
#include <tchar.h>
#include <xmmintrin.h>

int main()
{
    static __m128 *m_sincos;
    int Bins = 4;

    m_sincos = (__m128*) _aligned_malloc(Bins*sizeof(__m128), 16);
    for (int t=0; t<Bins; t++) {
        m_sincos[t] = _mm_set_ps(cos((float) t), sin((float) t), sin((float) t), cos((float) t));
        __asm movaps xmm0, m_sincos[t];
        __asm mov eax, m_sincos
        __asm mov ebx, t
        __asm shl ebx, 4
        __asm movaps xmm0, [eax+ebx];
    }

    return 0;
}

Когда вы запустите это, если вы будете следить за окном регистров, вы можете заметить что-то странное. Хотя результаты верны, xmm0 получает правильное значение до того, как будет выполнена инструкция movaps. Как это происходит?

Просмотр сгенерированного кода сборки показывает, что _mm_set_ps() загружает результаты sin / cos в xmm0, а затем сохраняет их по адресу памяти m_sincos[t]. Но значение остается там и в xmm0. _mm_set_ps является «внутренним», а не вызовом функции; он не пытается восстановить значения регистров, которые он использует после того, как это сделано.

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

1 голос
/ 04 июня 2010

Вы должны всегда использовать instrinsics или даже просто включать и оставлять их, вместо того, чтобы явно их кодировать. Это потому, что __asm ​​не переносим на 64-битный код.

...