Могу ли я правильно сравнить с нулевым регистром в avx?

Я встречал очень странное поведение внутренней инструкции AVX _mm256_testc_pd (). Здесь вы можете увидеть описание этой функции https://software.intel.com/sites/landingpage/IntrinsicsGuide/#techs=AVX,AVX2&text=test&expand=5432

Я использовал его в своем коде 2 раза, ожидая увидеть аналогичные результаты сравнения чего-то с нулевым регистром.

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

int main(void)
{
    unsigned char arr[32] __attribute__ ((aligned (32)));

    __m256d a, zero;
    int res1, res2;

    memset(arr, 0 , 32);
    arr[0] = 0xff;
    arr[4] = 0xff;
    arr[8] = 0xff;
    arr[12] = 0xff;
    arr[16] = 0xff;
    arr[20] = 0xfd;
    arr[24] = 0xff;
    arr[28] = 0xff;


    zero = _mm256_setzero_pd();
    a = _mm256_load_pd((double *)arr);
    res1 = _mm256_testc_pd(zero, a);
    printf("res1 = %d\n" , res1);

    memset(arr, 0xff, 32);
    a = _mm256_load_pd((double *)arr);
    res2 =  _mm256_testc_pd(zero, a);
    printf("res2 = %d\n" , res2);
    return 0;
}

В результате я получил

res1 = 1
res2 = 0

Кто-нибудь понимает, почему это произошло? Я думаю, что в обоих случаях a не равно нулю.

ОБНОВЛЕНИЕ
После обсуждения в комментариях моя проблема была решена, но у меня есть небольшое недоразумение в функциях _mm256_testc_si256 и _mm256_testz_si256

Например:

unsigned char arr[32] __attribute__ ((aligned (32)));

    __m256d a, zero;

    int res1, res2;
    memset(arr, 0 , 32);

    arr[0] = 0x80;

    zero = _mm256_setzero_pd();
    a = _mm256_load_pd((double *)arr);

    res1 = _mm256_testc_si256(_mm256_castpd_si256(zero),_mm256_castpd_si256(a));
    res2 = _mm256_testz_si256(_mm256_castpd_si256(zero),_mm256_castpd_si256(a));
    printf("res1 = %d\n" , res1);
    printf("res2 = %d\n" , res2);

Выход был

res1 = 0
res2 = 1

И я думаю, что только первый правильный. Так почему же эти функции производят другой результат?


c avx
person AlekseyM    schedule 18.08.2015    source источник
comment
Так же arr[20] = 0xfd; это намеренно?   -  person Sourav Ghosh    schedule 18.08.2015
comment
Неважно, это может быть 0xff, такое же поведение.   -  person AlekseyM    schedule 18.08.2015
comment
Действительно ли эта инструкция делает то, что вы от нее ожидаете? Я бы сказал, что вы хотите использовать _mm256_cmp_pd () для сравнения значений.   -  person Caladan    schedule 18.08.2015
comment
Думаю, вы правы, но знаете ли вы, что должно быть в третьем аргументе _mm256_cmp_pd ()?   -  person AlekseyM    schedule 18.08.2015
comment
@AlekseyM: можешь просто передать _CMP_EQ_OQ (== 0).   -  person Paul R    schedule 18.08.2015


Ответы (2)


_mm256_testc_pd работает только с битом знака каждого элемента двойной точности, поэтому наблюдаемое поведение является правильным. Если вы хотите проверить значения с двойной точностью в каждом элементе, сначала используйте подходящую инструкцию сравнения (например, _mm256_cmp_pd с соответствующим параметром _CMP_xxx), а затем используйте _mm256_testc_pd или _mm256_testz_pd, в зависимости от ваших точных требований.

person Paul R    schedule 18.08.2015
comment
Спасибо, это мне очень помогает - person AlekseyM; 18.08.2015
comment
размышляя над этой проблемой, я также использую инструкцию приведения и целочисленное сравнение, это тоже мне помогает _mm256_testc_si256(_mm256_castpd_si256(zero), _mm256_castpd_si256(a)) Может быть, это будет кому-то полезно. - person AlekseyM; 18.08.2015
comment
@AlekseyM: вы должны быть осторожны с использованием целочисленных сравнений для этого, поскольку в IEEE-754 есть два разных значения 0 (0 и -0), каждое из которых должно сравниваться равным 0. Использование целочисленного сравнения не удастся для - 0. - person Paul R; 18.08.2015
comment
@AlekseyM: Вы можете проверить нулевое значение FP, используя целое число PTEST, проверяя его по маске, у которой были все, кроме установленного знакового бита. например ones=_mm256_cmpeq_epi64(same, same); testmask=_mm256_srli_epi64(ones, 1); return _mm256_testz_si256(a, ones); Это проверяет ненулевые биты где угодно, кроме позиций знаковых битов. Однако я бы не рекомендовал эту оптимизацию для частного случая для практического использования! - person Peter Cordes; 18.08.2015
comment
@Peter Cordes: мне нужен код, который вернет 1, если хотя бы один бит в регистре AVX не равен нулю (не имеет значения знаковый бит или нет). Я думаю, что сравнивать с одним - не лучшее решение в данном случае. - person AlekseyM; 20.08.2015
comment
@Paul R: если я использую _mm256_testc_si256 () для сравнения 0 и, например, -0, получу ли я, что они равны? вы знаете, как долго выглядит -0 в шестнадцатеричном формате? Для меня это действительно важно, если проверка -0 для них никого не вернет - person AlekseyM; 20.08.2015
comment
@AlekseyM: единственная разница между 0,0 и -0,0 - это знаковый бит: 0.0 = 0x000...000, -0.0 = 0x800...000. - person Paul R; 20.08.2015
comment
@AlekseyM: Если вы хотите проверить какие-либо установленные биты во всем регистре, используйте _mm256_testz_si256(a, a), как если бы вы использовали test eax, eax / jz в целочисленном регистре. Убедитесь, что вы действительно хотите рассматривать -0 иначе, чем 0 в своей функции. Если вы уверены, то это самый прямой способ проверить наличие ненулевых битов в двоичном представлении значений FP. - person Peter Cordes; 20.08.2015
comment
@AlekseyM: Вы тестируете против zero, а не против себя. PTEST (testz_si256) выполняет И между своими аргументами и устанавливает флаг z, если все биты равны нулю. Он также выполняет src1 AND (NOT src2) и устанавливает флаг c, если все биты в нем равны нулю. Ваш testc всегда будет равен нулю, независимо от a. изменить: я думаю, вы удалили свой комментарий прямо перед тем, как я закончил печатать. :П - person Peter Cordes; 20.08.2015
comment
@AlekseyM: вы можете редактировать комментарии до 5 минут, вместо того, чтобы удалять и нарушать порядок. - person Peter Cordes; 20.08.2015
comment
@PeterCordes: Вы правы, но было неудобно приводить пример кода в комментариях, поэтому я обновил вопрос. Вы можете объяснить, что происходит в моем обновленном коде. - person AlekseyM; 20.08.2015
comment
@AlekseyM: Я уже делал, пока вы удаляли / репостили свои комментарии! См. stackoverflow.com/questions/32072169/ Просто прочтите руководство для функции в руководстве по встроенным функциям или руководство Intel insn ref - person Peter Cordes; 20.08.2015
comment
@PeterCordes: согласно руководству Intel по внутренним функциям int _mm256_testz_si256 (__m256i a, __m256i b) IF (a [255: 0] AND b [255: 0] == 0) ZF: = 1 ELSE ZF: = 0 FI return ZF So i всегда будет получать 0, когда один из аргументов равен нулю, и это не помогает мне определить наличие единиц в регистре - person AlekseyM; 20.08.2015
comment
@AlekseyM: вам не нужно явно тестировать на 0, просто используйте пример, который Питер дал ранее, т.е. _mm256_testz_si256(a, a) - он более эффективен (не требует нулевого вектора) и более интуитивно понятен / читаем. - person Paul R; 20.08.2015
comment
@PaulR: О, я понял, и это работает. Благодарю. Но почему моя версия res1 = _mm256_testc_si256(_mm256_castpd_si256(zero), _mm256_castpd_si256(a)) тоже работает правильно? Согласно руководству по внутренним функциям, этого не должно быть. - person AlekseyM; 20.08.2015
comment
@AlekseyM: Питер объяснил, что в комментариях выше также: версия testc проверяет src1 AND (NOT src2) (хотя похоже, что у вас может быть неправильная аргументация?). - person Paul R; 20.08.2015
comment
@PaulR: Я понимаю это, но что меня действительно смущает, что именно в таком порядке аргументов - первый zero, второй a он работает правильно. Тогда как в логическом порядке - первый a, второй zero работает некорректно. Может интел ошибся)) в описании или в реализации? (да звучит невероятно) - person AlekseyM; 20.08.2015
comment
@AlekseyM: Да, похоже, ваш тест показывает, что описание внутреннего testc неверно. Если порядок аргументов совпадает с порядком, который PTEST использует в синтаксисе Intel / MASM, то это первый инвертируемый аргумент. (И да, это допустимый способ проведения теста. Он также позволяет компилятору свернуть операнд памяти в инструкцию PTEST в цикле, если один из операндов является постоянным. _mm_testz с вектором всех единиц из pcmpeq или mm_testc с вектором "все нули" из _mm_setzero.) Интересно, замечал ли кто-нибудь эту ошибку в документации раньше! - person Peter Cordes; 20.08.2015
comment
Кажется, я помню, что в некоторых заголовках gcc, когда AVX был новым, было несколько ошибок, особенно в отношении транспонированных операндов и т. Д. - возможно, OP имеет старую установку gcc. Также стоит проверить _mm_testc_pd, поскольку между версиями SSE и AVX должна быть по крайней мере согласованность. - person Paul R; 20.08.2015
comment
@PaulR: поведение соответствует тому, что вы получили от PTEST с аргументами в таком порядке, поэтому я думаю, что это ошибка документа в руководстве по встроенным функциям, не ошибка компилятора / заголовка. Я сообщаю об этом сейчас в ветке форума Intel. - person Peter Cordes; 20.08.2015
comment
@PeterCordes: Да, это действительно интересное наблюдение.) Спасибо за столь глубокое исследование. Пожалуйста, покажите мне ссылку на эту ветку форума. - person AlekseyM; 20.08.2015
comment
@PeterCordes: это может быть ошибка документации (или, возможно, ошибка заголовка / встроенной функции) - вариант testc используется очень редко (по моему опыту, по крайней мере), тогда как версия testz используется много (и ведет себя одинаково независимо от в любом случае порядок операндов). - person Paul R; 20.08.2015
comment
@PeterCordes: глядя на руководства Intel, я думаю, что могу понять, откуда взялась путаница - в описании PTEST и др. сказано (перефразируя) CF = SRC AND NOT DEST, но DEST обычно является первым входным параметром во внутреннем, так что testc встроенные функции делают param2 AND NOT param1. - person Paul R; 20.08.2015
comment
@AlekseyM: Ссылка там от? рядом с полем поиска в гиде. Я тоже не сразу нашел. : P software.intel.com/en- нас / форумы / тема /. Ваш комментарий помещен в очередь на рассмотрение администраторами сайта и будет опубликован после утверждения. - person Peter Cordes; 20.08.2015
comment
@PaulR: да, именно так. И руководство по встроенным функциям документирует testc как выполнение NOT param2, о чем я сообщил об ошибке в документации. Не удивлюсь, когда вы скажете, что testc почти никогда не используется. Интересно, что вы можете проверить флаги C и Z, чтобы увидеть, есть ли в векторе сочетание единиц и нулей. - person Peter Cordes; 20.08.2015
comment
@PeterCordes: молодцы, что сообщили об этом - интересно отметить, что _1 _ / _ 2_ имеет аналогичную семантику, но, похоже, они получили это право в Руководстве по внутренним функциям. - person Paul R; 20.08.2015
comment
@PeterCordes: спасибо за сообщение. Надеюсь, в будущем мы увидим правильное описание в документации. - person AlekseyM; 20.08.2015

Спасибо Питеру Кордесу и всем остальным, правильным (и самым красивым) решением моей проблемы было

res = _mm256_testz_si256(_mm256_castpd_si256(a), _mm256_castpd_si256(a))

person AlekseyM    schedule 20.08.2015