int32_t的延迟是否低于int8_t、int16_t和int64_t
Does int32_t have lower latency than int8_t, int16_t and int64_t?
(我指的是Intel CPU,主要是GCC,但拥有ICC或MSVC(
使用int8_t
、int16_t
或int64_t
与int32_t
相比效率较低,这是真的吗,因为生成了额外的指令来在 CPU 字大小和所选变量大小之间进行转换?
如果有人对此有任何示例或最佳实践,我会很感兴趣?我有时会使用较小的变量大小来减少缓存行负载,但假设我只消耗了 50 字节的缓存行,其中一个变量是 8 位 int,使用剩余的缓存行空间并将 8 位 int 提升为 32 位 int 等可能会更快?
您可以将更多的 uint8_t
s 填充到缓存行中,因此加载 N uint8_t
s 将比加载 N uint32_t
s 更快。
此外,如果您使用的是带有 SIMD 指令的现代英特尔芯片,智能编译器将尽可能矢量化。同样,在代码中使用小变量将允许编译器将更多通道填充到 SIMD 寄存器中。
我认为最好使用最小的尺寸,并将细节留给编译器。当涉及到这样的事情时,编译器可能比你(和我(更聪明。对于许多操作(例如无符号加法(,编译器可以使用相同的代码进行uint8
、uint16
或uint32
(并且只是忽略高位(,因此没有速度差异。
未命中比任何算术或逻辑运算都昂贵得多,因此担心缓存(以及数据大小(几乎总是比简单的算术更好。
(很久以前,在Sun工作站上,使用double
比float
快得多,因为硬件只支持double
。我认为现代 x86 不再如此,因为 SIMD 硬件(SSE 等(直接支持单精度和双精度(。
Mark Lakata的回答指向正确的方向。我想补充几点。
理解和做出优化决策的绝佳资源是 Agner 文档。
指令表文档具有最常见指令的延迟。您可以看到其中一些在本机大小版本中表现更好.
例如,可以消除mov
,mul
具有较少的延迟.
然而,这里我们谈论的是获得 1 个时钟,我们必须执行大量指令来补偿缓存未命中.
如果这就是整个故事,那就不值得了。
真正的问题来自解码器。当您使用一些长度变化的前缀(您将使用非本机大小的单词(时,解码器需要额外的周期。
因此,操作数大小前缀会更改指令其余部分的长度。预解码器无法在单个时钟周期内解决此问题。从此错误中恢复需要 6 个时钟周期。因此,避免使用这种长度变化的前缀非常重要。
如今,在不再更新(但仍然存在(的微拱中,惩罚是严厉的,特别是使用某种算术指令.
在后来的微拱中,这已经减轻了,但惩罚仍然存在。
要考虑的另一个方面是,使用非本机大小需要为指令添加前缀,从而生成更大的代码。这是尽可能接近声明">生成附加指令以在 CPU 字大小和所选变量大小之间进行转换",因为英特尔 CPU 可以处理非本机字大小。
对于其他,特别是RISC,CPU,这通常不是正确的,可以生成更多的指令。
因此,在优化使用数据缓存的同时,也对指令缓存进行了不良利用。
同样毫无价值的是,在通用的 x64 ABI 上,堆栈必须在 16 字节边界上对齐,并且通常编译器以本机字大小或接近的字大小保存本地变量(例如 64 位系统上的 DWORD(.
仅当您分配足够数量的本地变量或使用数组或打包结构时,您才能从使用小变量大小中获得好处.
如果你声明一个uint16_t
var,它可能会占用与单个uint64_t
相同的堆栈空间,所以最好选择最快的大小。
此外,当涉及到数据缓存时,重要的是位置,而不仅仅是数据大小。
那么,该怎么办呢?
幸运的是,您不必在拥有小数据或小代码之间做出决定。
如果您有大量数据,则通常使用数组或指针以及使用中间变量进行处理。一个例子是这行代码。
t = my_big_data[i];
我的方法是:
保持数据的外部表示形式,即
my_big_data
数组,尽可能小。例如,如果该数组存储温度,则对每个元素使用编码uint8_t
。保持数据的内部表示形式,即
t
变量,尽可能接近 CPU 字大小。例如,t
可以是uint32_t
或uint64_t
。
通过这种方式,您可以编程优化缓存并使用本机字大小.
作为奖励,您以后可能会决定切换到 SIMD 指令,而无需重新打包my_big_data
内存布局。
真正的问题是程序员花了太多时间在错误的地方和错误的时间担心效率;过早的优化是编程中所有邪恶(或至少大部分(的根源><。D. 高德纳
当你设计你的结构时,内存布局是问题驱动的。例如,年龄值需要 8 位,以英里为单位的城市距离需要 16 位.
编写代码时,算法使用编译器已知具有的该范围的最快类型。例如,整数比浮点数快,uint_fast8_t
不比uint8_t
慢。
什么时候需要提高性能,首先通过更改算法(通过使用更快的类型、消除冗余操作等(,然后如果需要数据结构(通过对齐、填充、打包等(。
- 从不同线程使用int64的不同字节安全吗
- 你能把一个向量<int64>投射到一个向量<uint8>吗
- C++中饱和短 (int16)
- Int64 和 time_t 之间的 C++ 重载歧义
- 如何将 int16 数组更改为矢量<float>
- 将数组类型更改为 Int16 会节省存储空间吗?
- uint32, int16, uint8 .为什么这些常用的数据类型没有进入标准
- 在 c++ 中设置 int64 值的最高四位
- INT16溢出导致无限循环
- UINT8数组对INT64的质量化失败,但应该起作用
- 为什么我会得到"You must feed a value for placeholder tensor 'output' with dtype int64"?
- 如何使用SSE有效地执行INT8/INT64转换
- INT16在C 上的二进制写作
- 从INT64到字节数组
- 如何将 int64 定点转换为双精度
- 如何在不改变顺序的情况下将int64值移位到无符号int64空间
- 在C++中将 Java long 转换为 int64
- 将C++ to memcpy uchar 翻译成 int64
- int64 时间计算(C++)
- 在C++中,int64和int64_t之间有什么区别