使用 "high" 内存使用的直接索引访问与使用 "low" 内存使用的 "shifted" 索引访问的理论影响是什么?

What is the theoretical impact of direct index access with "high" memory usage vs. "shifted" index access with "low" memory usage?

好吧,我真的很好奇什么做法更好,我知道它(可能?)根本不会产生任何性能差异(即使在性能关键应用程序中?)但我更好奇影响在考虑优化的情况下对生成的代码进行优化(为了完整起见,如果有任何不同,也 "performance")。

所以问题如下:

元素索引范围从 A 到 B,其中 A > 0 和 B > A(例如,A = 1000 和 B = 2000)。

要存储有关每个元素的信息,有几种可能的解决方案,其中两种使用普通数组的解决方案包括直接索引访问和通过操纵索引进行访问:

示例 1

//declare the array with less memory, "just" 1000 elements, all elements used
std::array<T, B-A> Foo;
//but make accessing by index slower?
//accessing index N where B > N >= A
Foo[N-A];

示例 2

//or declare the array with more memory, 2000 elements, 50% elements not used, not very "efficient" for memory
std::array<T, B> Foo;
//but make accessing by index faster?
//accessing index N where B > N >= A
Foo[N];

我个人会选择#2,因为我真的很喜欢性能,但我认为实际上:

访问任何带有索引的数组涉及将索引乘以元素大小并将其添加到数组本身的基址。

由于我们已经将一个数字加到另一个数字上,因此可以通过在添加 A * sizeof(T) 之前将基地址向下调整 N * sizeof(T) 来轻松完成对 foo[N-A] 的调整,而不是比实际计算 (A-N)*sizeof(T)

换句话说,任何体面的编译器都应该完全隐藏这个减法,假设它是一个常量值。

如果它不是常量[假设您正在使用 std::vector 而不是 std::array,那么您确实会在代码中的某个时刻从 N 中减去 A .这样做仍然很便宜。大多数现代处理器可以在一个周期内完成此操作,结果不会有延迟,因此最坏的情况是在访问中增加一个时钟周期。

当然,如果数字是 1000-2000,则整个方案可能几乎没有什么不同 - 处理的总时间几乎为零,或者因为你做复杂的事情而需要很多时间。但是如果你要让它成为一百万个元素,抵消一百万个元素,它可能会在简单或复杂的分配它们的方法或类似方法之间产生差异。

此外,正如 Hans Passant 所暗示的:现代 OS 具有虚拟内存处理,未实际使用的内存不会填充 "real memory"。在工作中,我正在调查具有 2GB RAM 的板上的奇怪崩溃,并且在查看内存使用情况时,它显示这个应用程序分配了 3GB 的虚拟内存。该板没有交换磁盘(它是嵌入式系统)。事实证明,某些代码只是简单地分配了大块内存,这些内存没有填充任何东西,并且只有在达到 3GB 时才停止工作(32 位处理器,3+1GB 内存在 user/kernel [=38 之间分配) =]).因此,即使对于大块内存,如果您只有一半,如果您实际上不访问它,它实际上也不会占用任何 RAM。

一如既往地涉及性能、编译器等,如果它很重要,请不要相信 "the internet" 会告诉您答案。使用您实际打算使用的代码设置测试,使用您计划生成代码 with/for 和 运行 基准的实际编译器和处理器类型。一些编译器很可能有一个错误特征(在处理器类型 XYZ9278 上),这使得它在大多数其他编译器这样做的情况下产生可怕的代码 "with no overhead at all"。