【问题标题】:performance of std::vector c++ size() inside loop in member function成员函数中循环内 std::vector c++ size() 的性能
【发布时间】:2011-12-23 06:13:44
【问题描述】:

类似的问题,但不太具体: 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


    【解决方案1】:

    不要养成这样做的习惯。

    你在(2)中做的优化的情况是:

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

    很少而且介于两者之间。

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

    现在,也就是说,我将尝试回答您的问题。函数SomeNotConstFunctionSomeConstFunction 的定义(大概)在同一个翻译单元中。所以如果这些函数真的不修改向量,编译器可以算出来,它只会“调用”一次size

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

    但是,我将“调用”放在引号中,因为它是一个微不足道的函数,几乎可以肯定它会被内联。此外,该函数的成本低得离谱——两次内存查找(几乎都保证命中缓存),以及减法和右移,或者甚至可能是一条专门的单指令同时完成这两种操作。

    即使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 循环。

      【讨论】:

      • 我想补充一点,size() for std::list 需要在 C++11 的 O(1) 中执行。
      【解决方案3】:

      微优化备注大概是因为vector::size()最常见的两个实现是

      return _Size;
      

      return _End - _Begin;
      

      将它们提升到循环之外可能不会显着提高性能。

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

      相信你的编译器!

      【讨论】:

      • 投票结束问题,它必须用准确的代码重写,这样才有可能得到准确的答案......
      【解决方案4】:

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

      我已经开始在我正在处理的一个特定项目中这样做,但它是针对面向性能的代码。除非您对深度优化某些东西感兴趣,否则我不会打扰。这些方式中的任何一种都可能非常快。在 Qt 中,它最多是一个指针解引用,并且是更多类型的。看起来它可以在 MSVC 中有所作为。

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

      我认为是 OP 的原作?真的可以用来给出数组的声明方式。

      【讨论】:

        猜你喜欢
        • 2011-04-23
        • 2010-10-28
        • 1970-01-01
        • 1970-01-01
        • 2015-05-03
        • 2022-01-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多