如果没有if,std :: count_if会更快吗?

时间:2014-10-07 21:56:25

标签: c++ performance gcc stl

这是gcc std::count_if代码

template<typename _InputIterator, typename _Predicate>
  typename iterator_traits<_InputIterator>::difference_type
  count_if(_InputIterator __first, _InputIterator __last, _Predicate __pred)
 {
  [snip]
  typename iterator_traits<_InputIterator>::difference_type __n = 0;
  for (; __first != __last; ++__first)
    if (__pred(*__first))
      ++__n;
  return __n;
}

我的问题:使用

会更好(即更快)吗?
__n += __pred(*__first); // instead of the if statement

这个版本总是添加,但不做分支。

4 个答案:

答案 0 :(得分:13)

您提供的替换不等同,因为对谓词的限制远远少于您的想法:

  • 可以在条件上下文中使用的任何内容(可以在上下文中转换为bool)是谓词的有效返回类型(explicitbool的转换就足够了)。
  • 返回类型可以很有趣地添加到迭代器差异类型。
  

25算法库[algorithms]

     

25.1一般[algorithms.general]

     

8只要算法需要一个函数对象(20.9),当应用于解引用相应迭代器的结果时,就会返回一个可测试为Predicate的值,从而使用true参数。换句话说,如果算法将Predicate pred作为其参数并将first作为其迭代器参数,则应该在上下文转换为pred(*first)的构造bool中正常工作 (第4条)。函数对象pred不应通过解引用的迭代器应用任何非常量函数。

给予替代消化不良的最可能的回报是标准的整数类型,并且值既不是0也不是1.

另外,请记住,编译器现在可以真正优化(特别是C ++需要,所有模板的东西都是深层次的。)

答案 1 :(得分:10)

首先,您建议的代码是不同的。因此,让我们看看两个等效的代码:

template<typename _InputIterator, typename _Predicate>
typename iterator_traits<_InputIterator>::difference_type
count_if(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
    typename iterator_traits<_InputIterator>::difference_type __n = 0;
    for (; __first != __last; ++__first)
        if (__pred(*__first))
            ++__n;
    return __n;
}

template<typename _InputIterator, typename _Predicate>
typename iterator_traits<_InputIterator>::difference_type
count_if(_InputIterator __first, _InputIterator __last, _Predicate __pred) {
    typename iterator_traits<_InputIterator>::difference_type __n = 0;
    for (; __first != __last; ++__first)
        __n += (bool) __pred(*__first);
    return __n;
}

然后,我们可以使用编译器编译它并查看程序集。在我尝试的一个编译器(在os x上铿锵),这些生成了相同的代码

也许你的编译器也会生成相同的代码,或者它可能产生不同的代码。

答案 2 :(得分:6)

从技术上讲,它会,但请记住,所有高于0的值都会评估为true。因此被调用的函数可能会返回1以外的值,这会使结果产生偏差。此外,编译器还可以将分支优化为条件移动。

要扩展,有一些方法可以优化代码中的分支,但这会降低可读性和可维护性,以及通过例如调试代码的能力。因为编译器非常善于自己优化这些东西,所以放下断点并获得很少的收益。

答案 3 :(得分:4)

编译器生成的代码不一定字面上在机器代码中重现C ++语言结构。仅仅因为您的C ++代码中包含if语句并不意味着机器代码将基于分支指令。现代编译器不需要也不会在生成的机器代码中实现抽象C ++机器的行为。

由于这个原因,不可能说它是否会更快。 C ++代码没有任何固有的&#34;速度&#34;与之相关联。 C ++代码永远不会直接执行。它不能更快​​&#34;或者&#34;慢一些&#34;从抽象的角度来看。如果要通过查看代码来分析代码的性能,则必须查看编译器生成的机器代码,而不是C ++代码。但是更好的方法是尝试两种变体并通过在各种典型输入数据上实际运行它们来对它们进行分析。

智能编译器很可能会为您的两种变体生成相同的代码。