【问题标题】:C structure pointer dereferencing speedC结构指针解引用速度
【发布时间】:2011-04-28 13:28:42
【问题描述】:

我有一个关于指针解引用速度的问题。我有这样的结构:

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) ...
}

案例 2:

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 增加一个元素并直接取消引用而不使用索引。

有什么想法吗?谢谢:)

【问题讨论】:

  • 不要浪费时间进行过早的优化,因为这种优化很可能不会产生任何影响。
  • 也许一小部分很重要,但如果确实如此,为什么不测量它呢?
  • 对于Win32,是否可以使用GetTickCount()来测量调用循环测量速度前后的时间,或者有没有更好的方法?
  • @user482910 可以,但要确保多次(例如一百万次以上)运行的平均值,因为在程序运行时上下文切换到另一个进程的可能性适中。
  • “我想念 MSDOS” - 现在有三个我从未想过我会再次听到的词 :-)

标签: c++ c pointers local dereference


【解决方案1】:

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

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

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

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

【讨论】:

  • 我问是因为我正在编写的函数正在为包含数百万个顶点的矢量地图裁剪多边形......我可以挤出的任何一点速度都会有所帮助,因为我需要裁剪每个部分到 1 度区域。
  • 没关系。正确的做法是运行真实世界的基准测试,因为它取决于大量因素,而我们知道的因素很少。至少,您的目标机器、编译器、CPU、内存和 I/O 子系统等其他架构问题、数据的组成、优化级别等等。
  • 你有数以百万计的,看看我下面的评论。在矢量图上创建一个索引(即 p?),按 x 和 y 排序。 (它是否保持相当静态?)。或者在 x 上排序并在 y 上有一个索引。使用二分搜索查找所有 x 右、所有 y 下。因此,如果您说 400 万,则每个比较 22 次,总共 88 次,而不是现在的 1600 万!
  • 好吧,矢量图的每个部分都在特定区域(ESRI shapefile)中自行构建......所以我有一个大致区域供每个区域快速调用......但后来我需要将这些部分中的每一个划分为 1 度正方形区域。
  • +1 表示“测量,不要猜测!”顺便说一句,ESRI shapefile?我感觉到你的痛苦。
【解决方案2】:

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

关于存储双精度数,使用 const 可能会影响性能。你的数组有多大?

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

如果你知道左 right 所以你可以输入一个“else”。

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

例如,如果您在 x 和 y 上对数组进行索引或排序,则可以使用二分搜索找到所有 x

【讨论】:

    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

      优化编译器将看到结构访问是循环不变的,Loop-invariant code motion 也是如此,使您的两种情况看起来相同。

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2020-08-19
          • 1970-01-01
          • 2015-07-13
          • 1970-01-01
          • 2020-06-12
          • 1970-01-01
          • 1970-01-01
          • 2021-05-03
          相关资源
          最近更新 更多