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

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

compiler used: icc

#include<stdio.h>
#include<emmintrin.h>
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


person arunmoezhi    schedule 06.11.2012    source источник
comment
связано с заголовком: как распечатать число __uint128_t с помощью gcc?   -  person Mysticial    schedule 06.11.2012
comment
Обратите внимание, что некоторые компиляторы имеют встроенную поддержку printf для типов SIMD, например Все версии gcc, clang и т. Д. От Apple поддерживают _1_ для печати _2_ в виде целых чисел 4 x 32 бита.   -  person jfs    schedule 06.11.2012
comment
Я использую компилятор Intel   -  person Paul R    schedule 06.11.2012
comment
Есть ли способ сделать замаскированное сложение. Скажем, я хотел бы сохранить только альтернативные элементы (c [0], c [2])?   -  person arunmoezhi    schedule 06.11.2012
comment
_1_ - это идентификационный элемент для добавления. Таким образом, замаскируйте один из входных операндов, и соответствующие элементы _2_ будут _3_.   -  person arunmoezhi    schedule 06.11.2012
comment
Замените 0 на c = a + (b & mask), если хотите int.   -  person Peter Cordes    schedule 17.04.2016


Ответы (4)


Используйте эту функцию, чтобы распечатать их:

#include <stdint.h>
#include <string.h>

void print128_num(__m128i var)
{
    uint16_t val[8];
    memcpy(val, &var, sizeof(val));
    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-битная поддержка:

#include <inttypes.h>

void print128_num(__m128i var) 
{
    int64_t v64val[2];
    memcpy(v64val, &var, sizeof(v64val));
    printf("%.16llx %.16llx\n", v64val[1], v64val[0]);
}

Примечание. приведение &var непосредственно к int* или uint16_t* также будет работать с MSVC, но это нарушает строгий псевдоним и является неопределенным поведением. Использование memcpy является стандартным совместимым способом сделать то же самое, и с минимальной оптимизацией компилятор сгенерирует точно такой же двоичный код.

person askmish    schedule 06.11.2012
comment
оно работает. Я использовал uint32_t для печати 32-битных целых чисел. Но результат обратный. Вместо llx я получаю lld. _3_ сохраняет значения в обратном порядке? - person askmish; 06.11.2012
comment
@NateEldredge: Наверное, нет. 2,4,6,8, или сохранить в локальном массиве более нормально. Вы также можете назначить 8,6,4,2 из _mm_add_epi32 и массив. Это нормально для тестирования / отладки, если это работает, когда вы пытаетесь это сделать. Однако отладчик легче покажет вам, что находится в ваших векторах, чем распечатки отладки. - person arunmoezhi; 06.11.2012
comment
также: _mm_extract_epi32 - person Peter Cordes; 17.04.2016
comment
Как насчет __m128i bp = _mm_set_epi32(0xFF, 0xfe,0xfa,0xfb); std::cout << std::setfill('0') << std::hex<<std::setw(16)<< bp.m128i_i64[1]<<std::setw(16)<< bp.m128i_i64[0];? Тогда вам не понадобится _2_. - person Алексей Неудачи&; 25.10.2018
comment
Вы должны использовать int *val = (int*)&var и memcpy. Тогда весь остальной код будет работать нормально. И упрощает, потому что, я думаю, вы можете использовать диапазон, например _3_. - person Nanashi No Gombe; 16.06.2020

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

Обратное небезопасно (указывает int* на часть объекта __m128i). MSVC гарантирует, что это безопасно, но GCC / clang - нет. (-fstrict-aliasing включен по умолчанию). Иногда это работает с GCC / clang, но зачем рисковать? Иногда это даже мешает оптимизации; см. этот вопрос и ответ. См. Также Переинтерпретирует_кастирование между аппаратными SIMD указатель вектора и соответствующий тип неопределенного поведения?

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

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

#include <immintrin.h>
#include <stdint.h>
#include <stdio.h>

#ifndef __cplusplus
#include <stdalign.h>   // 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]);
}

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

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


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

// 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

Переносится через gcc / clang / ICC / MSVC, C и C ++.

person Peter Cordes    schedule 15.10.2017

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

Использование:

#include <string>
#include <cstring>
#include <sstream>

#if defined(__SSE2__)
template <typename T>
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 <iostream>
[..]
__m128i x
[..]
std::cout << __m128i_toString<uint8_t>(x) << std::endl;
std::cout << __m128i_toString<uint16_t>(x) << std::endl;
std::cout << __m128i_toString<uint32_t>(x) << std::endl;
std::cout << __m128i_toString<uint64_t>(x) << std::endl;

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

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

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

person Antonio    schedule 24.09.2014
comment
@PeterCordes Я понимаю вашу точку зрения. Интересно, можно ли вместо этого просто использовать memcpy, который избавил бы от необходимости требовать выровненный буфер. - person Peter Cordes; 15.10.2017
comment
Смотрите мой ответ. Используйте _1_ вместо _2_, если у вас нет C ++ 11 для _3_ или директив, специфичных для компилятора. Вероятно, он все еще будет оптимизирован. (И, кстати, современные Windows / Linux уже выравнивают стек на 16 байт, поэтому компилятору ничего не стоит выровнять буфер, если он действительно сохраняет / перезагружает.) - person Antonio; 16.10.2017
comment
@PeterCordes Но разве memcpy не является подходящей альтернативой? - person Peter Cordes; 16.10.2017
comment
Да, это тоже сработает. Я ожидаю, что компиляторы в целом лучше справятся с _1_. Что касается memcpy, я не удивлюсь, если хотя бы одна компиляция действительно сбросит _2_ в стек, а затем скопирует оттуда в массив. (Хотя, возможно, и нет, но встраивание тривиального memcpy - это то, в чем они обычно довольно хороши.) Тем не менее, _3_ определенно является более идиоматическим способом сделать это, ИМО. На самом деле он полностью оптимизируется до _4_ / _5_ или любых других инструкций с большинством компиляторов (даже когда сохранение / перезагрузка было бы лучше: много крошечных элементов) - person Antonio; 16.10.2017
comment
@PeterCordes Спасибо за понимание! Во-первых, идеальная оптимизация не очень важна для такой функции потоковой передачи. Я адаптирую свой ответ для использования memcpy, который больше соответствует моему стилю кодирования и, как мне кажется, его легче понять. - person Peter Cordes; 16.10.2017
comment
Я думаю, _1_ легче понять, поскольку код со встроенными функциями определенно будет использовать это. Но ладно, если вам нравится _2_ для каламбура, тогда продолжайте. - person Antonio; 16.10.2017
comment
Есть ли способ избежать необходимости использования специального случая storeu для печати целыми числами, а не memcmp или _3_? Это самая неуклюжая вещь в этой функции: / - person Peter Cordes; 17.10.2017
comment
Можно УБ, если sizeof(T) == 1 или что-то в этом роде. (char с тремя unsigned char членами, например, RGB24 пикселей и перегруженный _4_). Может быть, набрать _5_. (Вероятно, худшая альтернатива: _6_ копировать только нужное количество байтов, но это, скорее всего, плохо оптимизируется.) - person Peter Cordes; 17.10.2017
comment
Давайте сделаем sizeof values, у компилятора будет информация, чтобы разобраться и оптимизировать - person Peter Cordes; 17.10.2017
comment
Да, но тогда вы не копируете все байты, а массив недостаточно велик, чтобы вместить весь _1_. Это более сложная оптимизация. Кстати, _2_ определяется только в _3_. gcc определяет обычный _4_ в _5_ (я думаю, потому что C определяет его в _6_). Думаю, лучше всего включить _7_, если вам нужен memcpy, поскольку C ++ не упоминает не _8_ его версия, по крайней мере, на cppreference. - person Antonio; 17.10.2017
comment
Вероятно, оптимизатор будет использовать 16 байтов вместо 15, и, вероятно, вообще не будет использовать memcpy. Я предпочитаю, чтобы это было легко понять - person Peter Cordes; 17.10.2017
comment
Как я и подозревал, ваш оптимизм был неуместен с _1_ и _2_. Они оба фактически делают 15-байтовые копии. Щелкните правой кнопкой мыши строку memcpy в godbolt.org/g/vRtzs3 и перейдите к сборке. (gcc с 8 + 4 + 2 + 1 байтовыми хранилищами, clang с 2 перекрывающимися 8-байтовыми хранилищами). Ничего особенного по сравнению с тем, сколько кода требуется для создания _3_ (и передать его по очереди другому _4_ и уничтожить, если вы хотите распечатать его в _5_), но все же. Может, есть способ лучше округлить размер? - person Antonio; 17.10.2017
comment
Я предпочитаю, чтобы было легко понять. Я добавил примечание о том, как можно удалить if. - person Peter Cordes; 17.10.2017
comment
Кроме того, ваша функция не компилируется, если _1_ неправильно сформирована, даже если она не используется. Знаете ли вы, как лучше сделать то, что не требует для компиляции обеих сторон ветви с константой времени компиляции? Думаю, может быть _2_ сделать отдельные шаблоны для sizeof (T) = 1 или нет. (По-прежнему существует проблема с однобайтовыми классами, которые перегружают _3 _...). (В ссылке Godbolt я просто _4_ удалил эту ветку, чтобы она скомпилировалась с моим _5_ классом.) - person Antonio; 17.10.2017
comment
Да, это проблема производительности только в том случае, если вы используете его с классом без степени 2, а не (int)values. Для удобства чтения имеет смысл оставить его как есть. (Тем более, что нет ничего высокопроизводительного в использовании enable_if и строкового потока для печати вектора.) Если бы вы помещали это в библиотеку, чтобы люди могли использовать ее, не глядя на нее, вместо ответа SO, вы бы сделали другой выбор. - person Peter Cordes; 17.10.2017
comment
@NateEldredge: Я уверен, что это не строго законно (если вы не используете uint*_t или что-то в этом роде). Я отправил ответ, который безопасен. - person Peter Cordes; 17.10.2017

#include<stdio.h>
#include<emmintrin.h>
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;
}

Также обратите внимание, что

#include<stdio.h>
#include<emmintrin.h>
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;
}
не имеет информации о сохраняемом типе. Это могут быть 8-битные целые, 16-битные, 32-битные и т. Д. Некоторые компиляторы поддерживают расширения полей _2_. Но это точно не стандартно и не в GCC.

person Lucien    schedule 25.12.2013
comment
@PeterCordes, относительно вашего комментария о том, что это не совсем законно, есть ли способ получить предупреждение компилятора? Я пробовал использовать -fno-strict-aliasing, но не получил предупреждения. Я также пробовал _2_ проверить наличие предупреждения или ошибки во время выполнения, но не получил ни того, ни другого. - person Peter Cordes; 15.10.2017
comment
@dannyadam: Интересно, но похоже, что эти проверки не обнаруживают явных нарушений строгого псевдонима: godbolt.org / z / qo4vre например -Wstrict-aliasing для массива -fsanitize=undefined. - person dannyadam; 26.11.2020
comment
Источник + выход ASM на Godbolt компилятор исследователь : proof он компилируется с MSVC и так далее. - person Peter Cordes; 26.11.2020