英文:
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论