使用小数据类型是否对性能(速度和内存)没有用处?

huangapple go评论46阅读模式
英文:

Is using small types useless for performance (speed and memory)?

问题

我最近在Rust上处理了一个项目,其中某些部分的协议使用少于8位的位数。

我发现使用4位整数并没有提高性能或内存,与使用8位整数相比。使用任意大小仅在API方面是任意的,而不是在存储方面。

我看到64位处理器使用64位宽的总线,这是否意味着使用小于64位的类型不会提高性能?

英文:

I recently worked on a project in Rust with a protocol using less than 8 bits for some parts.

I figure out that using a 4bits integer doesn't improve performance or memory as using an 8bits integer.
Using arbitrary size is only arbitrary in terms of API, not in storage.

I saw that 64-bit processors use bus that is 64-bit wide, Is that mean using types smaller than 64-bits doesn't increase performance?

答案1

得分: 2

更窄的数据类型对于数组来说是有利的,可以减少缓存未命中(以及访问多个连续元素的带宽)。对于结构体来说,它们可以更紧凑地打包在一起,这样一个结构体可以适应一个缓存行,而不是多个,如果其中包含许多不同的整数元素或一些小数组的话。尽管总体大小通常不是问题,但缓存占用和内存带宽可能非常重要。

但通常情况下,最小值为1字节,因为CPU可以免费或几乎免费地执行这个操作,但在字节内部装载/修改/存储位或位字段需要更多的工作。

有一些例外,比如位图(位数组),对于某些情况非常有用。能够一次检查64位,而不仅仅是一次检查8字节(使用8字节寄存器)是否为非零对于需要搜索下一个设置位的数组用户来说是有用的。

对于非数组局部变量,通常希望使用所有CPU都能高效执行的类型,比如C的int/unsigned或Rust的i32/u32。编译器通常会将这些变量保存在寄存器中,其中一个变量占用一个寄存器,不管大小如何,而更窄的类型可能意味着编译器必须使用额外的指令来截断为8位或16位。(或者在x86上使用字节或字操作数大小,这有时可能效率较低。)

与此相关:https://stackoverflow.com/questions/46721075/can-modern-x86-hardware-not-store-a-single-byte-to-memory - 硬件实际上可以高效地加载/存储单个字节。有时相对于32位或64位存储而言,存储字节可能会多出一些成本,但如果您将多个字节写在一起,大多数这样的CPU可以合并为一个提交到缓存。

还与x86-64相关:32位操作数大小在代码大小和除法性能方面最有效。https://stackoverflow.com/questions/38303333/the-advantages-of-using-32bit-registers-instructions-in-x86-64

英文:

Narrower types are good for arrays, to reduce cache misses (and bandwidth for accessing multiple elements consecutively). And for structs to pack more together, so a single struct fits in one cache line instead of multiple if there are many different integer elements or some small arrays. Cache footprint and memory bandwidth can be a big deal even though total size usually isn't.

But usually 1 byte is the smallest it's worth considering, because CPUs can do that for free or nearly, but packing bits or bitfields inside bytes takes more work to load / modify / store the containing byte or word.

There are exceptions to this rule, like with a bitmap (array of bits), which is useful for some things. Being able to check 64 bits at once instead of just 8 bytes at once (with an 8-byte register) for being non-zero is useful if users of your array will want to search for the next set bit.

For non-array local variables, usually you want to use something all CPUs can do efficiently, like C's int / unsigned, or Rust's i32 / u32. Compilers will often keep these in registers, where a variable takes up a whole register regardless of size, and narrower might mean the compiler has to use extra instructions to truncate to 8 or 16 bits. (Or on x86, use byte or word operand-size, which can sometimes be less efficient.)


Related: https://stackoverflow.com/questions/46721075/can-modern-x86-hardware-not-store-a-single-byte-to-memory - hardware can in fact load/store single bytes efficiently. Sometimes with a bit of extra cost for a byte store vs. a 32 or 64-bit store, but if you write multiple bytes next to each other, most such CPUs can coalesce into one commit to cache.

Also related for x86-64: 32-bit operand-size is the most efficient in code-size, and also performance for division. https://stackoverflow.com/questions/38303333/the-advantages-of-using-32bit-registers-instructions-in-x86-64

huangapple
  • 本文由 发表于 2023年3月7日 13:14:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/75658253.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定