2014-01-24 2 views
3

Я нахожусь в процессе кодирования простой функции свертки в C++, начиная с самой основной «скользящей оконной» свертки с регулярными произведениями (на данный момент нет материала FFT), до SEE , AVX и, возможно, OpenCL. Однако я столкнулся с проблемой SSE. Мой код выглядит следующим образом:SSE: reinterpret_cast <__m128*> вместо _mm_load_ps

for (x = 0; x < SIZEX - KSIZEX + 1; ++x) 
{ 
    for (y = 0; y < SIZEY - KSIZEY + 1; ++y) 
    {   
     tmp = 0.0f; 

     float fDPtmp = 0.0f; 
     float *Kp = &K[0]; 


     for (xi = 0; xi < KSIZEX; ++xi, Kp=Kp+4) 
     {        
      float *Cp = &C[(x+xi)*SIZEY + y]; 

      __m128 *KpSSE = reinterpret_cast<__m128*>(&K); 
      __m128 *CpSSE = reinterpret_cast<__m128*>(&C[(x + xi)*SIZEY + y]); 
      __m128 DPtmp = _mm_dp_ps(*KpSSE, *CpSSE, 0xFF); 
      _mm_store_ss(&fDPtmp, DPtmp); 

      tmp += fDPtmp; 
     } 

     R[k] = tmp; 
     ++k; 
    } 
} 

Необходимые матрицы инициализируются, как это (размер тех, является considerd хорошо, потому что более простые реализации работают нормально):

__declspec(align(16)) float *C = ReadMatrix("E:\\Code\\conv\\C.bin"); 
__declspec(align(16)) float *K = ReadMatrix("E:\\Code\\conv\\K.bin"); 
__declspec(align(16)) float *R = new float[CSIZEX*CSIZEY]; 

Код аварий при у = 1, поэтому я чувствую, что может быть ошибка с тем, как я обрабатываю указатели. Интересно то, что если я заменить reinterpret_casts с _mm_set_ps, т.е.

__m128 KpSSE = _mm_set_ps(Kp[0], Kp[1], Kp[2], Kp[3]); 
__m128 CpSSE = _mm_set_ps(Cp[0], Cp[1], Cp[2], Cp[3]); 
__m128 DPtmp = _mm_dp_ps(KpSSE, CpSSE, 0xFF); 
_mm_store_ss(&fDPtmp, DPtmp); 

все это работает просто отлично, хотя медленнее, что я обвиняю все операции копирования.

Может кто-нибудь, пожалуйста, указать мне, что именно я делаю неправильно здесь?

Большое спасибо

Pat

Update: Хорошо, так как указал Павла проблема заключается в ReadMatrix (или другое решение было бы использовать _mm_loadu_ps). Что касается ReadMatrix(), он выглядит так:

__declspec(align(16)) float* ReadMatrix(string path) 
{ 
streampos size; 

ifstream file(path, ios::in | ios::binary | ios::ate); 

if (file.is_open()) 
{ 
    size = file.tellg(); 
    __declspec(align(16)) float *C = new float[size]; 
    file.seekg(0, ios::beg); 
    file.read(reinterpret_cast<char*>(&C[0]), size); 
    file.close(); 

    return C; 
} 
else cout << "Unable to open file" << endl; 

} 

Это не делает трюк. Есть ли другой способ сделать это элегантно, а не быть вынужденным читать файл по частям и выполнять memcpy, который, как я полагаю, должен работать ?!

Update:

Тем не менее, кажется, не хотят работать после того, как

размер
__declspec(align(16)) float* ReadMatrix(string path) 

{ streampos;

ifstream file(path, ios::in | ios::binary | ios::ate); 

if (file.is_open()) 
{ 
    size = file.tellg(); 
    __declspec(align(16)) float *C = static_cast<__declspec(align(16)) float*>(_aligned_malloc(size * sizeof(*C), 16)); 
    file.seekg(0, ios::beg); 
    file.read(reinterpret_cast<char*>(&C[0]), size); 
    file.close(); 

    return C; 
} 
else cout << "Unable to open file" << endl; 

}

Я добавил static_cast там, так как это казалось необходимым, чтобы получить код Павла компилировать (т.е. _aligned_malloc возвращает указатель недействительным). Я приближаюсь к просто читать фрагменты файла с fread и memcpy их в alligned массив. :/И снова я нахожусь со своим советом. Спасибо вам большое.

Pat

PS: Non-SSE код прекрасно работает с этими структурами данных. _mm_loadu_ps медленнее, чем использование кода, отличного от SSE.

+0

У вас не может быть указателя на '__m128'. это не имеет смысла, потому что '__m128' отображается в любом из регистров XMM [0-7]. – UmNyobe

+0

'reinterpret_cast <__m128*>' неправильно, вы должны использовать '_mm_loadu_ps'. – Mehrdad

+1

@UmNyobe: нет, код в порядке, и будет работать нормально, если данные будут правильно выровнены. –

ответ

4

Это не делает то, что вы думаете, он делает:

__declspec(align(16)) float *C = ReadMatrix("E:\\Code\\conv\\C.bin"); 

Все, что директива выравнивания достигает здесь является, чтобы выровнять сам (т.е. C) указатель на границе 16 байт, а не содержание указателя.

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

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

UPDATE

Вы повторили ту же ошибку в ReadMatrix:

__declspec(align(16)) float *C = new float[size]; 

снова это не гарантирует выравнивание данных, только указатель C сам. Чтобы исправить это распределение можно использовать _mm_malloc или _aligned_malloc:

float *C = _mm_malloc(size * sizeof(*C), 16); 

или

float *C = _aligned_malloc(size * sizeof(*C), 16); 
+0

Привет, Пол. Большое спасибо за вашу помощь. Он еще не совсем работает (см. Оригинальную запись). Если на вас не слишком много брегонов, могли бы вы (и другие, конечно), посмотреть еще? – pAt84

+0

Уверенный - см. UPDATE в ответе выше. –

+0

Пол, кажется, сглаженный. Добавлены изменения (см. Обновление выше), но он по-прежнему не работает. Сбой при y = 1 в '__m128 DPtmp = _mm_dp_ps (* KpSSE, * CpSSE, 0xFF);', что является нечетным, поскольку отладка показывает, что эти два __mm128 красиво заполнены. Не могли бы вы еще раз взглянуть? – pAt84

1

В ReadMatrix, у вас нет никакой гарантии, что выражение new возвращает выравнивать указатель. Неважно, что вы назначаете выровненному указателю (и я даже не уверен, соответствует ли ваш синтаксис самому указателю или тому, что он указывает).

Вам необходимо использовать _mm_align, или _mm_malloc, или какое-либо другое выравненное средство выделения.

0

Здесь вы не можете использовать reinterpret_cast, и я понимаю, что _mmloadu_ps работает медленно. Но есть и другой способ. Разверните свой цикл, прочитайте в выровненных данных и сдвиньте и замаскируйте новое значение перед выполнением операций над ним. Это будет быстро и правильно. То есть вы можете сделать это в своем внутреннем цикле:

__m128i x = _mm_load_ps(p); 
__m128i y = _mm_load_ps(p + sizeof(float)); 
__m128i z; 

// do your operation on x 1st time this iteration here 

z = _mm_slli_si128(y, sizeof(float) * 3); 
x = _mm_srli_si128(x, sizeof(float)); 
x = _mm_or_si128(x, z); 

// do your operation on x 2nd time this iteration here 

z = _mm_slli_si128(y, sizeof(float) * 2); 
x = _mm_srli_si128(x, sizeof(float) * 2); 
x = _mm_or_si128(x, z); 

// do your operation on x 3rd time this iteration here 

z = _mm_slli_si128(y, sizeof(float)); 
x = _mm_srli_si128(x, sizeof(float) * 3); 
x = _mm_or_si128(x, z); 

// do your operation on x 4th time this iteration here 

x = y; // don’t need to read in x next iteration, only y 

loopCounter += 4 * sizeof(float); 
Смежные вопросы