C# 为什么每个人都使用2^n数字进行分配-&燃气轮机;新StringBuilder(256)

C# 为什么每个人都使用2^n数字进行分配-&燃气轮机;新StringBuilder(256),c#,memory-management,size,C#,Memory Management,Size,15年前,当我用Pascal编程时,我明白了为什么要用2的幂来分配内存。但这似乎仍然是最先进的 C#示例: new StringBuilder(256); new byte[1024]; int bufferSize = 1 << 12; 新的StringBuilder(256); 新字节[1024]; int bufferSize=1IMHO,任何以2的算术运算的精确幂结尾的东西都像一条快车道。二次幂的低级算术运算所需的圈数和位操作比任何其他数字所需的cpu额外工作都少 发现了这

15年前,当我用Pascal编程时,我明白了为什么要用2的幂来分配内存。但这似乎仍然是最先进的

C#示例:

new StringBuilder(256);
new byte[1024];
int bufferSize = 1 << 12;
新的StringBuilder(256);
新字节[1024];

int bufferSize=1IMHO,任何以2的算术运算的精确幂结尾的东西都像一条快车道。二次幂的低级算术运算所需的圈数和位操作比任何其他数字所需的cpu额外工作都少

发现了这个可能的复制品:

我们在现代编程语言和现代硬件中需要这个吗?我想这是个好习惯,但原因是什么

视情况而定。这里有两件事要考虑:

  • 对于小于内存页大小的大小,2的幂和分配空间的任意数之间没有明显的差别
  • 大多数情况下,您将托管数据结构与C#结合使用,因此您甚至不知道到底分配了多少字节
  • 假设您使用
    malloc()
    进行低级分配,使用页面大小的倍数将被认为是一个好主意,即4096或8192;这是因为它允许更有效的内存管理


    我的建议是只分配您需要的,让C为您处理内存管理和分配。

    在某些情况下,这仍然是有意义的,但我更愿意逐案分析我是否需要这种规范,而不是盲目地将其用作良好实践

    例如,在某些情况下,您可能希望使用8位信息(1字节)来寻址一个表

    在这种情况下,我会让桌子的大小为2^8

    Object table = new Object[256];
    
    这样,您就可以只使用一个
    字节
    来寻址表中的任何对象

    即使表实际上更小,并且不使用所有256个位置,您仍然可以保证表到索引以及索引到表的双向映射,这可以防止出现错误,例如,如果您有:

    Object table = new Object[100];
    
    然后有人(可能是其他人)使用超出表范围的字节值访问它

    也许这种双射行为是好的,也许你可以用其他方法来保证你的约束


    也许,考虑到当前编译器智能性的提高,这不再是唯一的好做法。

    不幸的是,如果你想在一个4k的内存页中保留一块内存,那就太愚蠢了。。。人们甚至都不知道:—)(我直到10分钟前才知道……我只是有预感而已。。。一个例子。。。它的代码不安全,依赖于实现(使用32/64位的.NET 4.5)

    因此CLR至少分配了4096+(1或2)个sizeof(int)。。。因此,它已经超过了一个4k内存页。这是合乎逻辑的。。。它必须将数组的大小保持在某个位置,并且将其与数组保持在一起是最聪明的事情(对于那些知道什么是Pascal字符串和
    BSTR
    的人来说,是的,这是相同的原理)


    我要补充的是.NET中的所有对象都有一个syncblck编号和一个
    RuntimeType
    。。。它们至少是
    int
    ,如果不是
    IntPtr
    ,那么每个对象总共有8到16个字节(这在很多地方都有解释……如果您感兴趣,请尝试查找
    .net object header

    是的,这是一个很好的做法,至少有一个原因。 现代处理器的一级缓存线大小为64字节,如果将缓冲区大小设置为2^n(例如1024、4096、…),则将采用完全缓存线,而不会浪费空间。
    在某些情况下,这将有助于防止错误共享问题()。

    因为CPU工作二进制,内存块(页)大小为2^n(为什么要浪费一半内存块),并且这些很容易索引。在这里看到更多:在这里:我认为最重要的是炫耀。。。您无法真正知道为复杂类(
    Dictionary
    或甚至
    List
    StringBuilder
    )分配的确切缓冲区大小,对于字节数组,正如Jack所解释的,这有点道理。页面大小与此无关。您忽略了子分配和内存块元数据。@DavidHeffernan更不用担心使用2^n大小的原因,是不是您这么说的?@DavidHeffernan我重新编写了一点,专门讨论较低级别的分配。@Jack:这里有很多好的观点,谢谢!当考虑GC进行内存碎片整理时,这更有意义。二的幂通常在编译时计算。2.如果它确实更快,编译器可以对其进行优化。3.内存分配比算术要贵几个数量级,所以这种所谓的效率永远无法衡量。根据你所说的,编译器的开销可能很小,但运行时却不会。。我认为这对于特定的语言是正确的,但只适用于那些复杂的编译器。我猜你的答案在这种情况下是准确的,因为这个线程是C#
    byte[] arr = new byte[4096];
    
    fixed (byte* p = arr)
    {
        int size = ((int*)p)[IntPtr.Size == 4 ? -1 : -2];
    }