Ваша скалярная версия выглядит ужасно (с приведением ссылок для определения типов) и, вероятно, компилируется в действительно неэффективный asm, который намного медленнее, чем копирование каждого 32-битного элемента в битовый шаблон для 1.0f
.Для этого требуется только целое число И и ИЛИ, чтобы сделать это скалярным (если MSVC не смог автоматически векторизовать это для вас), но я не удивлюсь, если компилятор копирует его в регистр XMM или что-то в этом роде.
Ваша первая версия с векторизацией вручную даже не выполняет ту же работу, однако она просто маскирует все незнаковые биты, оставляя -0.0f
или +0.0f
.Таким образом, он будет компилироваться в один vandps ymm0, ymm7, [rdi]
и один SIMD-магазин с vmovups [rdi], ymm0
, плюс некоторые издержки цикла.
Не то, чтобы добавление _mm256_or_ps
с set1(1.0f)
замедляло бы его, вы все равноУзкое место в пропускной способности кэша или пропускной способности хранилища 1 в такт.
Затем вы отредактировали его до версии, которая ограничивается диапазоном -1.0f .. +1.0f
, оставляя входные данные с величиной менее 1,0 неизмененными.Это не будет медленнее, чем две побитовые операции, за исключением того, что Haswell (каньон дьявола) запускает логические значения FP только на порту 5, в отличие от фактических данных FP на порту 0 или порту 1.
Особенно, если вы неДелая что-нибудь еще с вашими поплавками, вы на самом деле захотите использовать _si256
встроенные функции, чтобы использовать только целочисленные инструкции AVX2 для них, для большей скорости на Haswell.(Но тогда ваш код не может работать без AVX2.)
На Skylake и более поздних версиях логические значения FP могут использовать все 3 векторных порта ALU.(https://agner.org/optimize/ для таблиц инструкций и руководства по uarch.)
Ваш код должен выглядеть примерно так:
// outside the loop if you want
const __m256i ones = _mm256_castps_si256(_mm256_set1_ps(1.0f));
for (something ; p += whatever) {
__m256i floats = _mm256_load_si256( (const __m256i*)p );
__m256i signs = _mm256_and_si256(floats, _mm256_set1_epi32(0x80000000));
__m256i applied = _mm256_or_si256(signs, ones);
_mm256_store_si256((__m256i*)p, applied);
}