4

下面的代码使用 1 个字节的内存。

byte[] n = new byte[] { 10, 20, 30, 40 }; //memory looks like this 10 | 20 | 30 | 40

与下面的这段代码相比,它使用 4 个字节的内存。

byte n1 = 10; // memory looks like this | 10 | 0 | 0 | 0 | 20 | 0 | 0 | 0 | 30 | 0 | 0.....
byte n2 = 20;
byte n3 = 30;
byte n4 = 40;

这是在 Visual Studio 2012 + 2010 中测试的,我认为现代编译器应该为我做优化?如果放置间距确实更快,以便字节秘密地变成非功能 int,那么它为什么对数组不做同样的事情,如果字节数组每个值占用 4 个字节从而渲染整个字节数组,它会变得更快字节的目的在 32 位/64 位机器上完全没用?总结一下:为什么编译器会以这两种不同的方式分配内存,哪一种是低效的方式?第 2 部分 32 位 / 64 位机器上的字节的实际目的是什么,如果将它存储在实际的连续字节块中效率很低,因为当您单独声明字节时 VS 拒绝这样做。

4

2 回答 2

7

我认为这里确实有两个问题:

为什么编译器不将四个bytes 打包成一个Int32

局部变量通常不会针对存储进行优化,而是针对访问速度进行优化。由于访问单个未对齐字节的速度有时在一条指令中是不可能的,并且直到最近(2009 年),它比对齐地址慢一个数量级,编译器作者通常使用对齐宽度作为合理的权衡。

除此之外,.Net 框架不是针对 x86 而是针对公共语言基础架构虚拟机指定的。CLI 规范必须支持最低公分母,并且 IA64 和 ARM 不支持非 QWORD 对齐的内存访问。为此,CLI 堆栈“只能存储至少 4 字节宽的值”(P.330)。

他们为什么这么做?我会想象潜在或实际的性能提升超过内存使用量的增加。鉴于在任何给定范围内有64 个函数局部变量的额外限制,应该强烈希望(除了良好的设计之外)保持给定范围内的变量数量很小。因此,净开销限制为 192 字节,这相当于我的系统中额外使用了 0.0000002% 的内存。

请记住,如果您正在访问一个字节数组,您实际上是在存储一个指针 - 这是内存地址的宽度(4 或 8 个字节)并直接访问内存。您正在管理哪个字节是哪个语义并承担这种复杂性。

如何以紧凑的形式存储东西以最大程度地减少内存使用

正如您所指出的,如果您的数据是大量字节,请使用字节数组来避免开销。如果您的数据是不同类型的,请使用允许访问打包数据的众多类之一(想到字段集BinaryReader的, BinaryWriter, BitConverter, unsafecode , structs )。StructLayout.Pack

如果您有大量数据,请使用具有固定布局结构的内存映射文件以最大限度地减少内存使用,同时仍然允许数据集大于机器中的内存量。它比普通的内存访问更难吗?是的,是的——但优化是管理内存使用、速度和程序员劳动力的平衡行为。最便宜的通常是内存。

或者,花几百块钱买足够的内存,没关系。32 GB(newegg 上 240 美元)在大多数情况下允许相当多的不关心。

于 2013-08-19T06:08:10.047 回答
4

当您创建一个包含 n 字节或 n 字节变量的数组时,编译器具有不同的优化自由度。该数组具有固定的内存布局,因为数组是一种密集的数据结构,值之间没有填充。

另一方面,如果您声明字节值,JITer 会尝试在 4 或 8 字节边界 (x64) 上对齐它们以确保对齐的内存访问。对齐与非对齐的内存访问对读取访问的影响可能约为 30%。这是一个有价值的优化。在这种情况下,字节之间的空洞不是问题,因为更少的空间不会让你更快。正确的内存对齐是获得最佳性能的关键。

于 2013-08-19T05:10:55.760 回答