напечатать переменную __m128i

Я пытаюсь научиться кодировать, используя intrinsics, а ниже – код, который добавляет

compiler used: icc

 #include #include int main() { __m128i a = _mm_set_epi32(1,2,3,4); __m128i b = _mm_set_epi32(1,2,3,4); __m128i c; c = _mm_add_epi32(a,b); printf("%d\n",c[2]); return 0; } 

Я получаю следующую ошибку:

 test.c(9): error: expression must have pointer-to-object type printf("%d\n",c[2]); 

Как распечатать значения в переменной c которая имеет тип __m128i

Используйте эту функцию для печати:

 void print128_num(__m128i var) { uint16_t *val = (uint16_t*) &var; printf("Numerical: %i %i %i %i %i %i %i %i \n", val[0], val[1], val[2], val[3], val[4], val[5], val[6], val[7]); } 

Перед их печатью вы разбиваете 128 бит на 16 бит (или 32 бита).

Это способ 64-битного расщепления и печати, если у вас есть 64-разрядная поддержка:

 void print128_num(__m128i var) { int64_t *v64val = (int64_t*) &var; printf("%.16llx %.16llx\n", v64val[1], v64val[0]); } 
  • Перенос через gcc / clang / ICC / MSVC, C и C ++.
  • полностью безопасный со всеми уровнями оптимизации: отсутствие наложения указателя (в отличие от большинства других ответов)
  • напечатайте в шестнадцатеричном виде элементы u8, u16, u32 или u64 ( на основе ответа @ AG1 )
  • Печатает в порядке памяти (наименее значимый элемент сначала, например _mm_setr_epiX ). Отмените индексы массива, если вы предпочитаете печатать в том же порядке, что и в руководствах Intel, где наиболее значимый элемент находится слева (например, _mm_set_epiX ). Связано: Конвенция для отображения векторных регистров

Использование __m128i* для загрузки из массива int является безопасным, поскольку типы __m128 определены для разрешения сглаживания. (например, в заголовках gcc, определение включает __attribute__((may_alias)) .)

Обратное не безопасно (объект __m128i и указатель int ). В большинстве случаев это может случиться, но зачем рисковать?

(uint32_t*) &my_vector нарушает правила сглаживания C и C ++ и не гарантируется работа так, как вы ожидали . Сохранение в локальном массиве, а затем доступ к нему гарантированно будет безопасным. Он даже оптимизирован для большинства компиляторов, поэтому вы получаете movq / pextrq непосредственно из xmm в целые регистры вместо фактического сохранения / перезагрузки, например.

Source + asm output в проводнике компилятора Godbolt : проверять его с помощью MSVC и т. Д.

 #include  #include  #include  #ifndef __cplusplus #include  // C11 defines _Alignas(). This header defines alignas() #endif void p128_hex_u8(__m128i in) { alignas(16) uint8_t v[16]; _mm_store_si128((__m128i*)v, in); printf("v16_u8: %x %x %x %x | %x %x %x %x | %x %x %x %x | %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]); } void p128_hex_u16(__m128i in) { alignas(16) uint16_t v[8]; _mm_store_si128((__m128i*)v, in); printf("v8_u16: %x %x %x %x, %x %x %x %x\n", v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]); } void p128_hex_u32(__m128i in) { alignas(16) uint32_t v[4]; _mm_store_si128((__m128i*)v, in); printf("v4_u32: %x %x %x %x\n", v[0], v[1], v[2], v[3]); } void p128_hex_u64(__m128i in) { alignas(16) unsigned long long v[2]; // uint64_t might give format-string warnings with %llx; it's just long in some ABIs _mm_store_si128((__m128i*)v, in); printf("v2_u64: %llx %llx\n", v[0], v[1]); } 

Если вам нужна переносимость на C99 или C ++ 03 или ранее (т.е. без C11 / C ++ 11), удалите alignas() и используйте storeu вместо store . Или используйте __attribute__((aligned(16))) или __declspec( align(16) ) .

(Если вы пишете код с внутренними функциями, вы должны использовать недавнюю версию компилятора. Новые компиляторы обычно делают лучше asm, чем старые компиляторы, в том числе для встроенных SSE / AVX. Но, возможно, вы хотите использовать gcc-6.3 с -std=gnu++03 C ++ 03 для кодовой базы, которая не готова для C ++ 11 или что-то еще.)


Пример вывода вызова всех 4 функций на

 // source used: __m128i vec = _mm_setr_epi8(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); // output: v2_u64: 0x807060504030201 0x100f0e0d0c0b0a09 v4_u32: 0x4030201 0x8070605 0xc0b0a09 0x100f0e0d v8_u16: 0x201 0x403 0x605 0x807 | 0xa09 0xc0b 0xe0d 0x100f v16_u8: 0x1 0x2 0x3 0x4 | 0x5 0x6 0x7 0x8 | 0x9 0xa 0xb 0xc | 0xd 0xe 0xf 0x10 

Откорректируйте строки формата, если вы хотите использовать начальные нули для согласованной ширины вывода. См. printf(3) .

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

Таким образом, это может быть реализация на C ++:

 #include  #include  #include  #if defined(__SSE2__) template  std::string __m128i_toString(const __m128i var) { std::stringstream sstr; T values[16/sizeof(T)]; std::memcpy(values,&var,sizeof(values)); //See discussion below if (sizeof(T) == 1) { for (unsigned int i = 0; i < sizeof(__m128i); i++) { //C++11: Range for also possible sstr << (int) values[i] << " "; } } else { for (unsigned int i = 0; i < sizeof(__m128i) / sizeof(T); i++) { //C++11: Range for also possible sstr << values[i] << " "; } } return sstr.str(); } #endif 

Применение:

 #include  [..] __m128i x [..] std::cout << __m128i_toString(x) << std::endl; std::cout << __m128i_toString(x) << std::endl; std::cout << __m128i_toString(x) << std::endl; std::cout << __m128i_toString(x) << std::endl; 

Результат:

 141 114 0 0 0 0 0 0 151 104 0 0 0 0 0 0 29325 0 0 0 26775 0 0 0 29325 0 26775 0 29325 26775 

Примечание: существует простой способ избежать if (size(T)==1) , см. https://stackoverflow.com/a/28414758/2436175

 #include #include int main() { __m128i a = _mm_set_epi32(1,2,3,4); __m128i b = _mm_set_epi32(1,2,3,4); __m128i c; const int32_t* q; //add a pointer c = _mm_add_epi32(a,b); q = (const int32_t*) &c; printf("%d\n",q[2]); //printf("%d\n",c[2]); return 0; } 

Попробуйте этот код.

Interesting Posts
Давайте будем гением компьютера.