7

我对指针取消引用的速度有疑问。我有这样的结构:

typedef struct _TD_RECT TD_RECT;
struct _TD_RECT {
  double left;
  double top;
  double right;
  double bottom;
};

我的问题是,其中哪一个会更快,为什么?


情况1:

TD_RECT *pRect;
...
for(i = 0; i < m; i++)
{
   if(p[i].x < pRect->left) ...
   if(p[i].x > pRect->right) ...
   if(p[i].y < pRect->top) ...
   if(p[i].y > pRect->bottom) ...
}

案例二:

TD_RECT *pRect;
double left = pRect->left;
double top = pRect->top;
double right = pRect->right;
double bottom = pRect->bottom;
...
for(i = 0; i < m; i++)
{
   if(p[i].x < left) ...
   if(p[i].x > right) ...
   if(p[i].y < top) ...
   if(p[i].y > bottom) ...
}

所以在情况 1 中,循环直接取消引用 pRect 指针以获取比较值。在情况 2 中,在函数的本地空间(在堆栈上)创建了新值,并将值从 pRect 复制到本地变量。通过一个循环会有很多比较。

在我看来,它们会同样慢,因为局部变量也是堆栈上的内存引用,但我不确定......

此外,最好继续按索引引用 p[],或者将 p 增加一个元素并直接取消引用而不使用索引。

有任何想法吗?谢谢 :)

4

5 回答 5

12

您可能会发现它不会对现代编译器产生影响。他们中的大多数可能会对在循环中不改变的表达式执行常见的子表达式消除。假设 C 语句和汇编代码之间存在简单的一对一映射是不明智的。我见过 gcc 输出的代码会让我的汇编技能蒙羞。

但这既不是 C 也不是 C++ 问题,因为 ISO 标准没有规定它是如何完成的。最好的检查方法是生成汇编代码,gcc -S并详细检查这两种情况。

如果您远离这种微观优化,而更多地关注宏观层面,例如算法选择等,您还将获得更多的投资回报。

而且,与所有优化问题一样,衡量,不要猜测!有太多的变量会影响它,所以你应该在目标环境中对不同的方法进行基准测试,并使用真实的数据。

于 2010-10-21T11:25:27.407 回答
3

这不太可能是一个巨大的性能关键差异。您可以多次分析每个选项并查看。确保您在测试中设置了编译器优化。

关于存储双打,使用 const 可能会影响性能。你的阵列有多大?

关于使用指针算法,这可以更快,是的。

如果您知道矩形中的左 < 右(肯定是这样),您可以立即优化。如果 x < left 它也不能是 > right 所以你可以输入一个“else”。

如果有的话,您的大优化将来自不必遍历数组中的所有项目,也不必对所有项目执行 4 次检查。

例如,如果您在 x 和 y 上对数组进行索引或排序,您将能够使用二进制搜索找到所有 x < left 的值并循环遍历这些值。

于 2010-10-21T11:29:23.583 回答
1

我认为第二种情况可能会更快,因为您没有在每次循环迭代时取消引用指向 pRect 的指针。

实际上,进行优化的编译器可能会注意到这一点,并且生成的代码可能没有区别,但是 pRect 是 p[] 中项目的别名的可能性可以防止这种情况发生。

于 2010-10-21T11:23:20.623 回答
0

优化编译器将看到结构访问是循环不变的,循环不变代码运动也是如此,使您的两种情况看起来相同。

于 2010-10-21T11:29:01.277 回答
0

如果即使是完全非优化的编译(-O0)也会为所呈现的两种情况产生不同的代码,我会感到惊讶。为了在现代处理器上执行任何操作,需要将数据加载到寄存器中。因此,即使您声明了自动变量,这些变量也不会存在于主内存中,而是存在于处理器浮点寄存器之一中。即使您自己不声明变量也是如此,因此即使您在 C++ 代码中声明临时变量,我也希望生成的机器代码没有区别。

但是正如其他人所说,将代码编译成汇编程序并自己查看。

于 2010-10-21T13:43:26.727 回答