为何NumPy中np.uint8数组转np.float16比转np.float32速度更慢?
uint8 转 float16 耗时是转 float32 两倍的原因说明
ndarray.astype() 执行类型转换时会返回全新的数组,按照常规认知,np.float16 单元素占2字节内存,比占4字节的np.float32 更省空间,np.uint8 数组转np.float16 的速度理应快于转np.float32,但实际测试结果和预期完全相反:转float16的耗时达到转float32的两倍以上。
测试复现
测试用原始数组
import numpy as np original_array = np.ones([10,512,1280,3], dtype=np.uint8)
测试结果
- 转
np.float16测试代码:
%%timeit -r 10 float16_array = original_array.astype(np.float16)
耗时:93.5 ms ± 1.68 ms per loop (mean ± std. dev. of 10 runs, 10 loops each)
- 转
np.float32测试代码:
%%timeit -r 10 float32_array = original_array.astype(np.float32)
耗时:41.4 ms ± 278 µs per loop (mean ± std. dev. of 10 runs, 10 loops each)
核心原因
这个现象本质是硬件指令支持差异导致的,和内存占用大小没有直接关系:
- 主流桌面级x86 CPU、常规ARM CPU 都没有提供
uint8/整数直接转float16的硬件向量指令,甚至早期CPU连原生float16计算指令都不支持。numpy在这类硬件上执行uint8转float16操作时,走的是纯软件实现的转换路径,实际拆成了两步执行:- 第一步:调用硬件加速的向量指令,把uint8批量转成CPU原生支持的float32,这一步耗时和直接转float32基本持平
- 第二步:逐元素对float32值做软件层面的舍入、阶码尾数截断,转换成float16的存储格式写入新数组,这一步额外消耗了和第一步接近的时间
- 而uint8转float32的操作,CPU本身提供了对应的SIMD(单指令多数据)加速指令,可以一次性批量完成多个元素的并行转换,不需要额外做格式适配,全程走硬件加速路径,实际耗时只有两步操作的float16转换的一半左右。
补充说明
如果是在原生支持FP16计算加速的硬件上执行同样的转换操作,比如带FP16计算单元的NVIDIA独立显卡、支持ARMv8.2-A FP16扩展指令集的新款移动端/服务器ARM处理器,float16转换的速度才会符合“数据量更小、转换更快”的预期。
内容的提问来源于stack exchange,提问作者Laassairi Abdellah
相关产品推荐
相关产品推荐

