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

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

本文关键字:内存 访问 索引 使用 shifted 情况 用情 是什么 low high 使用量      更新时间:2023-10-16

好吧,我真的很好奇什么做法更好,我知道它(可能?)根本不会产生任何性能差异(即使在性能关键型应用程序中?)但我更好奇对生成的代码的影响考虑到优化(为了完整性,还有"性能",如果它有任何区别的话)。

所以问题如下:

元素索引的范围从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::arraystd::vector,那么你确实会在代码的某个时刻从N中减去A。这样做仍然很便宜。大多数现代处理器可以在一个周期内完成此操作,而结果没有延迟,因此在最坏的情况下,会为访问增加一个时钟周期。

当然,如果数字是1000-2000,那么在整个计划中可能几乎没有什么区别 - 要么处理的总时间几乎为零,要么因为你做了复杂的事情而很多。但是,如果你要把它变成一百万个元素,抵消五十万个元素,它可能会在分配它们的简单或复杂方法之间产生差异,或者诸如此类。

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

与往常一样,在性能方面,编译器等,如果它很重要,不要相信"互联网"会告诉你答案。使用您计划用于/为其生成代码的实际编译器和处理器类型设置测试,并使用您实际打算使用的代码设置测试,并运行基准测试。某些编译器很可能有一个错误功能(在处理器类型 XYZ9278 上),这使得它为大多数其他编译器"完全没有开销"的情况下生成可怕的代码。