You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Intel AVX-512 intrinsics高效转换64个uint8_t为float并实现浮点乘法

问题描述

我分配了一段uint8_t内存:

uint8_t* srcPtr = malloc( sizeof( uint8_t ) * 64 );

srcPtr是测试函数的输入,需要和用户给定的单精度浮点数(比如9.0)相乘。

标量实现代码

for (int i = 0; i < 64; i ++)
    srcPtr[i] *= float_value;

为了用AVX-512 intrinsics实现这个乘法,需要把每个uint8_t转换成float。目前我用的是下面这段指令较多的代码,想知道有没有办法直接把64个uint8_t加载为float类型的AVX-512向量,简化操作?

当前使用的Intrinsics代码

__m512i px = _mm512_loadu_si512((__m512i *)srcPtr);

__m128i input[4];
input[0] = _mm512_extracti32x4_epi32(px, 0);
input[1] = _mm512_extracti32x4_epi32(px, 1);
input[2] = _mm512_extracti32x4_epi32(px, 2);
input[3] = _mm512_extracti32x4_epi32(px, 3);

__m512i output[4];
output[0] = _mm512_cvtepu8_epi32(input[0]);
output[1] = _mm512_cvtepu8_epi32(input[1]);
output[2] = _mm512_cvtepu8_epi32(input[2]);
output[3] = _mm512_cvtepu8_epi32(input[3]);

__m512 p[4];
p[0] = _mm512_cvtepu32_ps(output[0]);
p[1] = _mm512_cvtepu32_ps(output[1]);
p[2] = _mm512_cvtepu32_ps(output[2]);
p[3] = _mm512_cvtepu32_ps(output[3]);

解决方案

当然可以简化,不需要手动拆分再逐个转换。AVX-512提供了更高效的无符号整数扩展转换指令,能大幅精简代码:

方法1:批量扩展转换(基础AVX-512)

直接对加载后的__m512i向量分块处理,用_mm512_extracti64x2_epi64提取16字节块,再完成uint8_t→uint32_t→float的转换:

__m512i px = _mm512_loadu_si512((__m512i*)srcPtr);

// 拆分64个uint8_t为4组,每组16个,扩展为uint32_t向量
__m512i vec0 = _mm512_cvtepu8_epi32(_mm512_extracti64x2_epi64(px, 0));
__m512i vec1 = _mm512_cvtepu8_epi32(_mm512_extracti64x2_epi64(px, 1));
__m512i vec2 = _mm512_cvtepu8_epi32(_mm512_extracti64x2_epi64(px, 2));
__m512i vec3 = _mm512_cvtepu8_epi32(_mm512_extracti64x2_epi64(px, 3));

// 转换为单精度浮点数向量
__m512 p0 = _mm512_cvtepu32_ps(vec0);
__m512 p1 = _mm512_cvtepu32_ps(vec1);
__m512 p2 = _mm512_cvtepu32_ps(vec2);
__m512 p3 = _mm512_cvtepu32_ps(vec3);

方法2:利用AVX-512VL指令集(支持的CPU)

如果CPU支持AVX-512VL(可变长度),可以用256位扩展指令进一步简化提取操作:

__m512i px = _mm512_loadu_si512((__m512i*)srcPtr);

// 提取4组16字节的uint8_t数据
__m256i low16 = _mm512_castsi512_si256(px);
__m256i mid16 = _mm512_extracti32x4_epi32(px, 1);
__m256i high16 = _mm512_extracti32x4_epi32(px, 2);
__m256i highest16 = _mm512_extracti32x4_epi32(px, 3);

// 一步完成uint8_t→uint32_t→float转换
__m512 p0 = _mm512_cvtepu32_ps(_mm256_cvtepu8_epi32(low16));
__m512 p1 = _mm512_cvtepu32_ps(_mm256_cvtepu8_epi32(mid16));
__m512 p2 = _mm512_cvtepu32_ps(_mm256_cvtepu8_epi32(high16));
__m512 p3 = _mm512_cvtepu32_ps(_mm256_cvtepu8_epi32(highest16));

后续乘法与结果压缩

完成转换后,广播标量浮点数并执行乘法,最后将结果压缩回uint8_t存回内存:

// 广播标量float为AVX-512向量
__m512 scalar_vec = _mm512_set1_ps(float_value);
p0 = _mm512_mul_ps(p0, scalar_vec);
p1 = _mm512_mul_ps(p1, scalar_vec);
p2 = _mm512_mul_ps(p2, scalar_vec);
p3 = _mm512_mul_ps(p3, scalar_vec);

// 将float转换为uint32_t,再逐步压缩为uint8_t
__m512i res0 = _mm512_cvtps_epu32(p0);
__m512i res1 = _mm512_cvtps_epu32(p1);
__m512i res2 = _mm512_cvtps_epu32(p2);
__m512i res3 = _mm512_cvtps_epu32(p3);

__m256i pack01 = _mm512_packus_epi32(res0, res1);
__m256i pack23 = _mm512_packus_epi32(res2, res3);
__m128i pack_low = _mm256_packus_epi16(_mm256_castsi256_si128(pack01), _mm256_extracti128_si256(pack01, 1));
__m128i pack_high = _mm256_packus_epi16(_mm256_castsi256_si128(pack23), _mm256_extracti128_si256(pack23, 1));

__m512i final_res = _mm512_set_m128i(pack_high, pack_low);
_mm512_storeu_si512((__m512i*)srcPtr, final_res);

整个流程避免了手动拆分数组的冗余操作,代码更紧凑,执行效率也更高。


内容的提问来源于stack exchange,提问作者AI-developer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 19:45:21