首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >利用英特尔内部向量将8位整数与浮点数相乘

利用英特尔内部向量将8位整数与浮点数相乘
EN

Stack Overflow用户
提问于 2020-07-01 14:15:46
回答 1查看 1K关注 0票数 0

我正在编写一个软件栅格,大量使用英特尔的本质(不包括AVX512)。颜色由32位无符号表示,实际上只有4块8位颜色(RGBA)。因此,一个8种颜色的向量可以保存在一个单一的__mm256颜色变量中。但是,我需要通过将单个颜色乘以浮点数来操作这个数组中的单个颜色。换句话说,我可能有另一个浮点/ps值的向量,__mm256 rLight,其中我想要将颜色向量中相应的8位无符号R乘以rLight变量中的浮点数。我找不到任何理智的方法来做这件事。我需要做的似乎是将感兴趣的8个字节提取到一个__mm256浮点数数组中,然后进行乘法,然后将它们转换回无符号,并将它们重新粘贴到原始数组中,但我正在挣扎。

任何看起来很有希望的指示都将不胜感激。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-07-05 22:13:05

--一个8种颜色的向量可以保存在一个__mm256颜色变量中。

这不是最好的方法。将很难添加10+位颜色深度,或伽马校正,或颜色分级。要获得最佳性能,可以考虑使用16位整数,或者浮动。

,我找不到任何理智的方法来做这件事。

  1. 将您的浮点数转换为15位或16位定点。最快的方法是滥用IEEE表示,给scale+offset的单个FMA指令会浮动,所以0..1范围对应于尾数中最不重要的15-16位,然后位转换浮动到整数,并从位数上减去与浮动偏移值相等的int32数。看看我是如何处理64位双倍的,同样的方法可以用于32位浮点数,它只是寄存器、_mm256_sub_epi32.

和_mm256_fmadd_ps中所有8个浮点数的2条指令。

  1. 使用_mm256_packus_epi32复制车道,同时将32位压缩成16位。注意指令使用饱和,将自动剪辑到0。0xFFFF,即您不必在裁剪上浪费CPU周期。

  1. 加载颜色。

  1. 现在是扩大规模的时候了,这里有一种方法:

__m256i scaleBytes( __m256i rgba,__m256i mul ){ __m256i low = _mm256_and_si256( rgba,_mm256_set1_epi16( 0xFF ) );__m256i high = _mm256_and_si256( rgba,_mm256_set1_epi16( 0xF00) );low = _mm256_mulhi_epu16(低,mul );high = _mm256_mulhi_epu16(高,mul );low = _mm256_and_si256( high,_mm256_set1_epi16( 0xFF00 ) );返回_mm256_or_si256(低,高);}

  1. 如果您想要更好的舍入,您可能需要调整上面的代码,因为0xFF * 0xFFFF = FEFF01,即在乘以1.0浮点数后,您将得到0xFE。一种很好的解决方法是为标量器使用1.15个定点,而不是0.16,比例浮动使1.0映射到0x8000,并向scaleBytes函数添加几个位移位指令。在第2步之后,您还需要将缩放值裁剪到0x8000上限,一个_mm256_min_epu16指令就可以了。

更新:我刚刚意识到,对于步骤1,您不需要缩放,只需要偏移就足够了。

代码语言:javascript
复制
// Test values
__m256 floats = _mm256_setr_ps( -1, 0, 0.11f, 0.33f, 0.99f, 1, 1.11f, 12 );

// Floats have 23 bits of mantissa.
// We want [0..1] to map to the least significant 15 of them.
// Therefore, we need to offset the floats by 2 ^ ( 23 - 15 ) = 2 ^ 8
constexpr float offsetFloat = 0x1p8f;
// Same value bit-casted to integer, too bad std::bit_cast only appeared in C++/20
// https://www.h-schmidt.net/FloatConverter/IEEE754.html
constexpr int offsetInt = 0x43800000;

// Compute the integers
floats = _mm256_add_ps( floats, _mm256_set1_ps( offsetFloat ) );
const __m256i result = _mm256_sub_epi32( _mm256_castps_si256( floats ), _mm256_set1_epi32( offsetInt ) );

// Print the result
alignas( 32 ) std::array<int, 8> scalars;
_mm256_store_si256( ( __m256i * )scalars.data(), result );
for( int i : scalars )
    printf( "0x%04x ", i );
printf( "\n" );
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62678875

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档