这是出于历史原因吗?我经常看到像 char foo[256];
或 #define BUF_SIZE 1024
这样的东西。即使我多数情况下只使用2n大小的缓冲区,主要是因为我认为它看起来更优雅,而且我不必考虑一个具体的数字。但我不确定这是否是大多数人使用它们的原因,希望能够得到更多信息。
这是出于历史原因吗?我经常看到像 char foo[256];
或 #define BUF_SIZE 1024
这样的东西。即使我多数情况下只使用2n大小的缓冲区,主要是因为我认为它看起来更优雅,而且我不必考虑一个具体的数字。但我不确定这是否是大多数人使用它们的原因,希望能够得到更多信息。
可能有许多原因,虽然很多人只是出于习惯。
在高效实现循环缓冲区时,特别是在没有硬件除法的架构上(主要是8位微控制器),使用2^n缓冲区非常有用。在这种情况下,模数运算就是简单地对上位比特进行位屏蔽,或者例如256字节缓冲区的情况下,仅使用一个8位索引并让其环绕即可。
在其他情况下,与页面边界、缓存等的对齐可能为某些架构提供了优化机会,但这将非常具体于架构。但可能只是这种缓冲区为编译器提供了优化可能性,所以其他条件相等,为什么不呢?
缓存行通常是2的倍数(通常为32或64)。大小为该数字的整数倍的数据将能够适应(并充分利用)相应数量的缓存行。您可以将更多数据打包到缓存中,性能就越好...因此,我认为那些以这种方式设计结构的人正在优化性能。
除了其他人提到的原因之外,还有一个原因是SSE指令可以同时处理多个元素,而且输入的元素数量总是2的幂次方。将缓冲区设为2的幂次方可以确保不会读取未分配的内存。但只有在实际使用SSE指令时才适用。
我认为最终的原因在大多数情况下是程序员喜欢2的幂次方。
这对于哈希表非常有帮助,因为你需要对索引取模并将其与大小进行比较。如果大小是2的幂次方,则可以通过简单的按位与或&
计算模数,而不是使用实现%
操作符的缓慢除法类指令。
查看旧的Intel i386书籍,and
指令需要2个时钟周期,而div
指令需要40个时钟周期。即使1000倍更快的整体周期时间往往隐藏了最慢的机器操作的影响,但除法的基本复杂性要大得多,因此差距至今仍然存在。
曾经有一段时间,程序员们会尽力避免使用malloc来避免过多的开销。直接从操作系统中获取分配的内存将是(仍然是)特定数量的页面,因此幂次方可能会最大程度地利用分配的粒度。
正如其他人所指出的,程序员喜欢使用2的幂次方。
由于电子学中基于二进制算术的简单性(也可以理解为成本):左移(乘以2),右移(除以2)。
在CPU领域,许多构造都围绕着基于二进制算术展开。用于访问内存结构的总线(控制和数据)通常按照2的幂对齐。电子学(例如CPU)中逻辑实现的成本使得基于二进制的算术变得引人注目。
当然,如果我们有模拟计算机,情况就会不同了。
顺便提一下:位于 X 层的系统属性直接受到其下方系统即第 x-1 层 服务器 层属性的影响。我之所以这样说是因为我发帖时收到了一些关于此问题的评论。
例如,“编译器”级别可以操纵的属性是从它下面的系统即 CPU 中电子器件的属性中继承和派生而来的。
我能想到一些原因:
SIZE_BYTE+ARRAY
,其中大小字节告诉您数组的大小。这意味着该数组的大小可以从1到256的任何大小。我本来想使用shift参数,但是无法想出一个好的理由来证明它的必要性。
一个大小为2的幂次方的缓冲区的好处之一是循环缓冲区处理可以使用简单的与运算而不是除法:
#define BUFSIZE 1024
++index; // increment the index.
index &= BUFSIZE; // Make sure it stays in the buffer.
如果它不是2的幂,那么就需要进行除法。在旧时代(以及目前在小芯片上)这很重要。
页面大小通常是2的幂次方,这也很常见。
在Linux上,当像将缓冲区分块并将其写入套接字或文件描述符时,我喜欢使用getpagesize()。
在二进制中它是一个不错的、圆满的数字,就像10、100或1000000在十进制中一样。
如果它不是2的幂(或者像96=64+32或192=128+64这样接近2的幂),那么你可能会想知道为什么要增加精度。非基于2的四舍五入大小可能来自外部限制或程序员的无知。你会想知道是哪个原因导致了这种情况。
其他答案也指出了一些特殊情况下有效的技术原因。我不会在这里重复任何一个。