2014-01-27 2 views
12

Как я могу очистить верхние 128 бит м2:Как очистить верхние 128 бит значения __m256?

__m256i m2 = _mm256_set1_epi32(2); 
__m128i m1 = _mm_set1_epi32(1); 

m2 = _mm256_castsi128_si256(_mm256_castsi256_si128(m2)); 
m2 = _mm256_castsi128_si256(m1); 

не работает - в документации Intel, для _mm256_castsi128_si256 внутреннего говорит, что «верхние биты результирующего вектора не определены». В то же время я могу легко сделать это в сборке:

VMOVDQA xmm2, xmm2 //zeros upper ymm2 
VMOVDQA xmm2, xmm1 

Конечно, я не хотел бы использовать «и» или _mm256_insertf128_si256() и такие.

+1

Что не так с использованием встроенной сборки? Вы уже являетесь специфическим процессором, если работаете с внутренними функциями AVX. –

+0

Будет 'm2 >> 128;'? – ryyker

+4

Sergey: нет встроенной сборки в 64-битном VC. Кроме того, компилятор C часто создает более быстрый код, чем я бы сделал - он может использовать порядок умных instr и другие трюки. – seda

ответ

2

Посмотрите, что генерирует компилятор для этого:

__m128i m1 = _mm_set1_epi32(1); 
__m256i m2 = _mm256_set_m128i(_mm_setzero_si128(), m1); 

или же это:

__m128i m1 = _mm_set1_epi32(1); 
__m256i m2 = _mm256_setzero_si256(); 
m2 = _mm256_inserti128_si256 (m2, m1, 0); 

Версия лязгом я здесь, кажется, генерировать тот же код либо (vxorps + vinsertf128) , но YMMV.

+1

Paul: все мои компиляторы (ICC 14, VC 17, GC 4.8.1) используют vinserti128. С m2 = _mm256_castsi128_si256 (m1) все они используют более быструю vmovdqa и очищают верхнюю половину, но я не уверен, могу ли я на это положиться. – seda

4

К сожалению, идеальное решение будет зависеть от того, какой компилятор вы используете, а у некоторых из них is нет идеального решения.

Есть несколько основных способов, которыми мы могли бы написать это:

Версия A:

ymm = _mm256_set_m128i(_mm_setzero_si128(), _mm256_castsi256_si128(ymm)); 

Версия B:

ymm = _mm256_blend_epi32(_mm256_setzero_si256(), 
         ymm, 
         _MM_SHUFFLE(0, 0, 3, 3)); 

Версия C:

ymm = _mm256_inserti128_si256(_mm256_setzero_si256(), 
           _mm256_castsi256_si128(ymm), 
           0); 

Каждый из них выполняет именно то, что мы хотим, очищая верхние 128 бит 256-битного регистра YMM, поэтому любой из них можно безопасно использовать. Но что наиболее оптимально? Ну, это зависит от того, какой компилятор вы используете ...

GCC:

Вариант A: Не поддерживается на всех, потому что НКУ не хватает _mm256_set_m128i внутренней. (Может быть смоделировано, конечно, но это будет сделано с использованием одной из форм в «B» или «C».)

Версия B: Скомпилирована для неэффективного кода. Идиома не распознается, а встроенные средства переводятся буквально в машинные коды. Временный регистр YMM обнуляется с использованием VPXOR, а затем он смешивается с входным регистром YMM с использованием VPBLENDD.

Версия C: Идеально. Хотя этот код выглядит пугающим и неэффективным, все версии GCC, которые поддерживают генерацию кода AVX2, распознают эту идиому. Вы получаете ожидаемую команду VMOVDQA xmm?, xmm?, которая неявно очищает верхние биты.

Предпочитает версию C!

Clang:

Вариант А: Составитель неэффективного кода. Временный регистр YMM обнуляется с использованием VPXOR, а затем он вставляется во временный регистр YMM с использованием VINSERTI128 (или форм с плавающей запятой, в зависимости от версии и опций).

Версия B & C: Также составлен для неэффективного кода. Временный регистр YMM снова обнуляется, но здесь он смешивается с входным регистром YMM с использованием VPBLENDD.

Ничего идеального!

ICC:

Вариант A: Ideal. Производит ожидаемую инструкцию VMOVDQA xmm?, xmm?.

Версия B: Скомпилирована для неэффективного кода. Zeros представляет собой временный регистр YMM, а затем смешивает нули со входом регистра YMM (VPBLENDD).

Версия C: Также составлена ​​для неэффективного кода. Zeros - временный регистр YMM, а затем использует VINSERTI128 для ввода нулей во временный регистр YMM.

Предпочитаете версию A!

MSVC:

Версия A и C: Составитель неэффективного кода. Zeroes регистрирует временный регистр YMM, а затем использует VINSERTI128 (A) или VINSERTF128 (C) для ввода нулей во временный регистр YMM.

Версия B: Также составлена ​​для неэффективного кода. Zeros представляет собой временный регистр YMM, а затем смешивает его с регистром входного YMM, используя VPBLENDD.

Ничего идеального!


В заключении, то можно получить GCC и ICC испускать идеальную VMOVDQA инструкции, если вы используете правильную последовательность кода. Но я не вижу никакого способа заставить Clang или MSVC безопасно испускать команду VMOVDQA. Эти компиляторы не имеют возможности оптимизации.

Итак, на Clang и MSVC у нас есть выбор между XOR + blend и XOR + insert. Что лучше? Обратимся к Agner Fog's instruction tables (электронную таблицу версии also available):

На Ryzen архитектуры AMD в: (бульдозер-семья похожа на AVX __m256 эквивалентов этих, и AVX2 на экскаватор):

Instruction | Ops | Latency | Reciprocal Throughput | Execution Ports 
---------------|-----|---------|-----------------------|--------------------- 
    VMOVDQA  | 1 | 0 |   0.25   | 0 (renamed) 
    VPBLENDD  | 2 | 1 |   0.67   | 3 
    VINSERTI128 | 2 | 1 |   0.67   | 3 

Agner туман кажется чтобы пропустить некоторые инструкции AVX2 в разделе Ryzen его таблиц. См. this AIDA64 InstLatX64 result для подтверждения того, что VPBLENDD ymm выполняет то же самое, что и VPBLENDW ymm на Ryzen, вместо того, чтобы быть таким же, как VBLENDPS ymm (пропускная способность 1 c от 2 uops, которые могут работать на 2 портах).

См. Также an Excavator/Carrizo InstLatX64, показывающий, что VPBLENDD и VINSERTI128 имеют равную производительность (2 цикла латентности, 1 на пропускную способность каждого цикла). То же самое для VBLENDPS/VINSERTF128.

На архитектуры Intel (Haswell, Бродуэлла и Skylake):

Instruction | Ops | Latency | Reciprocal Throughput | Execution Ports 
---------------|-----|---------|-----------------------|--------------------- 
    VMOVDQA  | 1 | 0-1 |   0.33   | 3 (may be renamed) 
    VPBLENDD  | 1 | 1 |   0.33   | 3 
    VINSERTI128 | 1 | 3 |   1.00   | 1 

Очевидно, что VMOVDQA является оптимальным на обоих AMD и Intel, но мы уже знали, что, и это, кажется, не быть одним из вариантов на Clang или MSVC до тех пор, пока их генераторы кода не улучшатся, чтобы распознать одну из вышеупомянутых идиом, или добавочное внутреннее значение добавлено для этой точной цели.

К счастью, VPBLENDD не менее VINSERTI128 как для процессоров AMD, так и для процессоров Intel. На процессорах Intel VPBLENDD - Значительное улучшение по сравнению с VINSERTI128. (Фактически, это почти так же хорошо, как и VMOVDQA, в редком случае, когда последнее нельзя переименовать, за исключением необходимости иметь всю нулевую векторную константу.) Предпочитайте последовательность внутренних признаков, которая приводит к инструкции VPBLENDD, если вы не можете уговорить вас компилятор для использования VMOVDQA.

Если вам нужно с плавающей точкой __m256 или __m256d версию этого, выбор сложнее. На Ryzen, VBLENDPS имеет пропускную способность 1c, но у VINSERTF128 - 0.67c. На всех других процессорах (включая семейство AMD Bulldozer) VBLENDPS равен или лучше. Это много лучше на Intel (то же, что и для целого). Если вы оптимизируете специально для AMD, вам может потребоваться больше тестов, чтобы увидеть, какой вариант наиболее быстрый в вашей конкретной последовательности кода, иначе смесь. Это еще хуже, чем у Рызена.

В целом, то, нацеливание родового x86 и поддержка как много различных компиляторов, насколько это возможно, мы можем сделать:

#if (defined _MSC_VER) 

    ymm = _mm256_blend_epi32(_mm256_setzero_si256(), 
          ymm, 
          _MM_SHUFFLE(0, 0, 3, 3)); 

#elif (defined __INTEL_COMPILER) 

    ymm = _mm256_set_m128i(_mm_setzero_si128(), _mm256_castsi256_si128(ymm)); 

#elif (defined __GNUC__) 

    // Intended to cover GCC and Clang. 
    ymm = _mm256_inserti128_si256(_mm256_setzero_si256(), 
            _mm256_castsi256_si128(ymm), 
            0); 

#else 
    #error "Unsupported compiler: need to figure out optimal sequence for this compiler." 
#endif 

Смотрите это и версию А, В и С, отдельно on the Godbolt compiler explorer.

Возможно, вы могли бы построить на этом, чтобы определить свою собственную внутреннюю основу на основе макросов, пока что-то лучше не спустится с щуки.

+1

Я также попытался вставить полосу нулей в верхнюю полосу ymm: '_mm256_inserti128_si256 (ymm, _mm_setzero_si128(), 1);'. gcc компилирует его в фактический 'vinserti128', а clang превращает его в смесь, поэтому ничего нового нет. ICC компилирует его в «VMOVDQA». –

+0

Связанные: Процессоры Intel никогда не устраняют «vmovdqa же, то же» или «mov же, то же».Когда регистры различны, они почти всегда преуспевают, если у вас нет цепочки переименований, в которых нет элементов ALU. (например, «movdqa xmm0, xmm1' /' movdqa xmm1, xmm0' в цикле). Затем некоторые будут обрабатываться во время переименования, а некоторые будут выполнять блок выполнения. –

+0

Если я не использую '/ arch: AVX', MSVC использует не-AVX' xorps xmm2, xmm2' в версии A !!! https://godbolt.org/g/UwSvWh –

Смежные вопросы