在成员函数中循环内部的std :: vector c ++ size()的性能

问题描述 投票:2回答:4

类似的问题,但不太具体:Performance issue for vector::size() in a loop

假设我们在一个成员函数中:

void  Object::DoStuff()  {

   for( int k = 0; k < (int)this->m_Array.size(); k++ )
   {  
       this->SomeNotConstFunction();
       this->ConstFunction();

       double x = SomeExternalFunction(i);
   }
}

1)我愿意相信,如果仅调用“SomeExternalFunction”,编译器将优化而不是冗余地调用m_Array上的size()......是这样吗?

2)你几乎肯定不会从速度上提高速度

  int N = m_Array.size()
  for( int k = 0; k < N; k++ ) { ... } 

如果你正在调用一些非const的成员函数?

编辑不确定这些关于微优化的低票和讽刺评论来自哪里,也许我可以澄清一下:

首先,它不是要优化本身,而是要了解编译器将要修复的内容。通常我使用size()函数,但我现在问,因为这里数组可能有数百万个数据点。

其次,情况是“SomeNotConstFunction”可能极有可能改变数组的大小,或者它的能力可能取决于其他一些被切换的变量。所以,我问的是编译器会在什么时候失败,以及当数组确实可能发生变化时size()的时间成本到底是多少,尽管人为已知的原因不会?

第三,循环中的操作非常简单,只有数百万,但它们是令人尴尬的并行。我希望通过外部放置值可以让编译器矢量化一些工作。

c++ stl vector compiler-optimization
4个回答
9
投票

不要养成这样做的习惯。

您在(2)中进行优化的情况是:

  • 安全
  • 有明显的区别
  • 你的编译器无法自己解决的问题

很少,而且介于两者之间。

如果只是后两点,我只是建议你担心一些不重要的事情。然而,第一点是真正的杀手:你不想养成给自己犯错的机会。加速缓慢,正确的代码比调试快速,错误的代码要容易得多。

现在,那说,我会尝试回答你的问题。函数SomeNotConstFunctionSomeConstFunction的定义(推测)在相同的翻译单元中。因此,如果这些函数确实不修改向量,编译器可以解决它,它只会“调用”size一次。

但是,编译器无法访问SomeExternalFunction的定义,因此必须假设每次调用该函数都有可能修改向量。循环中存在该函数可确保每次都调用“大小”。

然而,我把“被叫”放在引号中,因为它是如此微不足道的功能,它几乎肯定会被内联。此外,该功能非常便宜 - 两个内存查找(几乎都保证是高速缓存命中),以及减法和右移,或者甚至可以执行两者的专用单个指令。

即使SomeExternalFunction绝对没有做任何事情,很可能每次“调用”size仍然只是循环运行时间的一小部分甚至可以忽略不计。

编辑:响应编辑....

what exactly is the time cost incurred by size() when the array really might change

您在计算两个不同版本的代码时所看到的时间差异。如果你正在做这样的非常低级别的优化,你就无法通过“纯粹的理由”得到答案 - 你必须凭经验测试结果。

如果你真的在做这样的低级优化(并且你可以保证向量不会调整大小),你应该更担心的是编译器不知道数组的基指针是不变的,而不是它不知道大小是不变的。

如果SomeExternalFunction确实在编译单元的外部,那么无论你做什么,你几乎都没有机会对编译器进行矢量化。 (我想它可能在链接时,虽然......)并且它也不太可能是“微不足道的”,因为它需要函数调用开销 - 至少如果“琐碎”对你来说意味着和我一样。 (再次,我不知道链接时间优化有多好......)

如果你真的可以保证某些操作不会调整向量的大小,你可以考虑改进你的类的API(或者至少是它的protectedprivate部分)来包含那些自然不会调整向量大小的函数。


2
投票

size方法通常由编译器内联,因此会有最小的性能损失,尽管通常会有一些。

另一方面,这通常仅适用于矢量。例如,如果您使用的是std :: list,则size方法可能非常昂贵。

如果你关心性能,你应该养成使用迭代器和/或std :: for_each等算法的习惯,而不是基于大小的for循环。


2
投票

微优化评论可能是因为vector::size()的两个最常见的实现

return _Size;

return _End - _Begin;

将它们从环路中吊出可能不会显着提高性能。

如果每个人都明白它可以做到,编译器也可能会注意到。使用现代编译器,如果SomeExternalFunction静态链接,编译器通常能够查看调用是否可能影响向量的大小。

相信你的编译器!


0
投票

在MSVC 2015中,它有一个return (this->_Mylast() - this->_Myfirst())。我不能随便告诉你优化器如何处理这个问题;但除非您的数组是const,否则优化器必须允许您修改其元素数量;使其难以优化。在Qt中,它等同于内联函数,它执行return d->size;;也就是说,对于QVector。

我已经开始在我正在开发的一个特定项目中完成它,但它适用于面向性能的代码。除非你有兴趣深入优化某些东西,否则我不会打扰。任何这些方式都可能非常快。在Qt中,最多只有一个指针解除引用,并且更多的是打字。看起来它可能会对MSVC产生影响。

到目前为止,我认为没有人提供明确的答案;但如果你真的想测试它,让编译器发出汇编源代码,并检查它两种方式。如果发现高度优化没有区别,我不会感到惊讶。但是,我们不要忘记,在调试期间未经优化的性能也是一个可以考虑的因素,当很多例如涉及数字运算。

我认为OP的原创?真的可以用来给出如何声明数组。

© www.soinside.com 2019 - 2024. All rights reserved.