Внутренняя проблема загрузки Intel

Цель кода — вычесть из каждого символа строки str значение в массиве ключей. Невекторизованная версия программы соответствует последнему циклу в обеих программах. Как этот код:

void decode(const char* key, int m, char* str) {
  int i; int n = strlen(str);
  __m128i k = _mm_loadu_si128((const __m128i*) key);
  for (int i = 0; i + 16 < n; i+=m) {
    __m128i s = _mm_loadu_si128((__m128i*) (str + i));
    s = _mm_sub_epi8(s, k);
    _mm_storeu_si128((__m128i*) (str + i), s);
  }
  for(; i<n; i++) str[i] -= key[i%m];
}

отличается от этого?

void decode(const char* key, int m, char* str) {
  int i, n = strlen(str);
  char keybuf[16] = { 0 };
  memcpy(keybuf, key, m);
  __m128i k = _mm_loadu_si128((__m128i*)keybuf);
  for (i=0; i+16 < n; i += m) {
    __m128i s = _mm_loadu_si128((__m128i*)(str+i));
    s = _mm_sub_epi8(s,k);
    _mm_storeu_si128((__m128i*)(str+i), s);
  }
  for (; i<n; i++) str[i] -= key[i % m]; }

Без копирования в память тот же код не работает так же. Я компилирую с помощью gcc -msse2. Зачем нужна копия памяти?


person spallas    schedule 19.01.2017    source источник
comment
Я думаю, что ваш цикл for неверен в обоих случаях - он должен быть for (int i = 0; i + 16 <= n; i += m) - но это, вероятно, не ваша главная проблема.   -  person Paul R    schedule 19.01.2017
comment
Это код моего профессора, я думаю, что это правильно. Это должно быть что-то о терминаторе строки...   -  person spallas    schedule 19.01.2017
comment
Как я уже сказал, это не главная проблема, но вы можете обнаружить, что делаете слишком много ненужной работы в финальном скалярном цикле для определенных значений n, если не исправите это.   -  person Paul R    schedule 19.01.2017


Ответы (1)


Разница в том, что во втором случае вы загружаете только m символов в keybuf, а остальные элементы остаются инициализированными до 0. Эти дополнительные элементы не влияют на str.

Однако в первой версии у вас, скорее всего, есть ненулевые элементы в конце вектора, поскольку вы вслепую загружаете все 16 элементов из key, независимо от фактической длины ключа.

Чтобы первая версия работала правильно, вам нужно будет замаскировать последние 16 - m элементы k, заставив их быть равными нулю, например.

const int8_t mask[32] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
                          0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 
__m128i k = _mm_loadu_si128((const __m128i*) key); // load 16 elements
k = _mm_and_si128(k, _mm_loadu_si128((const __m128i*)&mask[16 - m]));
                                                   // mask out final 16 - m elements

(Примечание: вероятно, существует более эффективный способ маскирования, но это лучшее, что я смог придумать в кратчайшие сроки. Думаю, он все равно будет более эффективным, чем версия memcpy. См. этот вопрос и его ответы для некоторых других методов.)

person Paul R    schedule 19.01.2017