Переполнение сборки SSE2 с использованием встроенных функций - PullRequest
1 голос
/ 09 августа 2011

Я новичок в SSE и SSE2, и я написал небольшую выборку C (выделяя два счетчика, один увеличивая другой, уменьшая, а не добавляя два), который работает, как и ожидалось. Я использовал встроенные функции и Microsoft Visual Studio 10 C ++ Express. В качестве второго шага я хотел понять, что происходит под капотом, но сейчас я озадачен. Например, операция присваивания в циклах for компилируется в:

__m128i a_ptr = _mm_load_si128((__m128i*)&(a_aligned[i]));
 mov         eax,dword ptr [i]  
 mov         ecx,dword ptr [a_aligned]  
 movdqa      xmm0,xmmword ptr [ecx+eax*2]  
 movdqa      xmmword ptr [ebp-1C0h],xmm0  
 movdqa      xmm0,xmmword ptr [ebp-1C0h]  
 movdqa      xmmword ptr [a_ptr],xmm0  

Я понимаю, что первые две строки получают компоненты адреса a_aligned, а третья строка копирует его в регистр xmm0. Но я не понимаю, почему он копируется обратно в память, чем снова в xmm0 (чем в a_ptr). Я думаю, что встроенная функция _mm_load_si128 должна скопировать 128 битов a_aligned [i] в ​​xmm0 и больше ничего. Почему это случилось? Я ошибаюсь теоретически? Если нет, как я должен намекает компилятору? Является ли мой пример кода правильным (в том смысле, что в нем нет лишних)? Вот мой полный пример кода:

#include <xmmintrin.h>
#include <emmintrin.h>
#include <iostream>

int main(int argc, char *argv[]) {
    unsigned __int16 *a_aligned = (unsigned __int16 *)_mm_malloc(32 * sizeof(unsigned __int16),16);
    unsigned __int16 *b_aligned = (unsigned __int16 *)_mm_malloc(32 * sizeof(unsigned __int16),16);
    unsigned __int16 *c_aligned = (unsigned __int16 *)_mm_malloc(32 * sizeof(unsigned __int16),16);

    for(int i = 0; i < 32; i++) {
        a_aligned[i] = i;
        b_aligned[i] = i;
        c_aligned[i] = 0;
    }

    for(int i = 0; i < 32; i+=8) {
        __m128i a_ptr = _mm_load_si128((__m128i*)&(a_aligned[i]));
        __m128i b_ptr = _mm_load_si128((__m128i*)&(b_aligned[i]));
        __m128i res = _mm_add_epi16(a_ptr, b_ptr);
        _mm_store_si128((__m128i*)&(c_aligned[i]), res);
    }

    for(int i = 1; i < 32; i++) {
        std::cout << c_aligned[i] << " ";
    }

    _mm_free(a_aligned);
    _mm_free(b_aligned);
    _mm_free(c_aligned);
    return 0;
}

Ответы [ 2 ]

2 голосов
/ 09 августа 2011

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

        __m128i a_ptr = _mm_load_si128((__m128i*)&(a_aligned[i]));
011D10A0  movdqa      xmm0,xmmword ptr [eax] 
        __m128i b_ptr = _mm_load_si128((__m128i*)&(b_aligned[i]));
011D10A4  movdqa      xmm1,xmmword ptr [edx+eax] 
        __m128i res = _mm_add_epi16(a_ptr, b_ptr);
011D10A9  paddw       xmm0,xmm1 
        _mm_store_si128((__m128i*)&(c_aligned[i]), res);
011D10AD  movdqa      xmmword ptr [ecx+eax],xmm0 

Выглядит лучше, не так ли?

1 голос
/ 09 августа 2011

Включите оптимизацию в настройках компилятора (используйте конфигурацию выпуска вместо отладки).

...