【问题标题】:Force all && to be executed?强制所有 && 被执行?
【发布时间】:2013-03-13 08:47:22
【问题描述】:

考虑以下可变参数函数

template <typename Type, typename... Types>
bool f(Type& arg, Types&... args)
{
    return f(arg) && f(args...);
}

template <typename Type>
bool f(Type& arg)
{
    // Do something
} 

如果一级递归是false,那么我怀疑下面不会执行。是否有一种技巧可以强制对所有参数进行递归,即使其中一个返回 false

【问题讨论】:

  • &amp;替换&amp;&amp;怎么样?

标签: c++ templates c++11 logic variadic-templates


【解决方案1】:

这应该不会太难:

template <typename Type, typename... Types>
bool f(Type& arg, Types&... args)
{
    bool b1 = f(arg);
    bool b2 = f(args...);
    return b1 && b2;
}

【讨论】:

  • 我认为只需摆脱对f 的第二次调用。
  • @sftrabbit: 剩下的 :) 谢谢
  • 为什么需要f(arg)?不应该只是bool b1 = arg吗?
【解决方案2】:

随着争论在 AndyProwl 和 Alon 解决方案的比较中不断发展,我对这两种解决方案进行了基准测试,结果...取决于参数的数量。

编译:

g++-4.7 -std=c++11 -Wall -Wextra -O3 main.cpp -o main -D_FIRST

对 AndyProwl 解决方案进行基准测试并使用:

g++-4.7 -std=c++11 -Wall -Wextra -O3 main.cpp -o main -D_SECOND

对 Alon 解决方案进行基准测试。

这是 10 个参数的基准测试程序。

#include <iostream>
#include <chrono>

// Function 1 : with &&
template <typename Type>
inline bool f1(const Type& arg)
{
   return arg;
}
template <typename Type, typename... Types>
inline bool f1(const Type& arg, const Types&... args)
{
    bool arg1 = f1(arg);
    bool arg2 = f1(args...);
    return arg1 && arg2;
}

// Function 2 : with &
template <typename Type>
inline bool f2(const Type& arg)
{
   return arg;
}
template <typename Type, typename... Types>
inline bool f2(const Type& arg, const Types&... args)
{
    return f2(arg) & f2(args...);
}

// Benchmark
int main(int argc, char* argv[])
{
    // Variables
    static const unsigned long long int primes[10] = {11, 13, 17, 19, 23, 29, 31, 37, 41, 43};
    static const unsigned long long int nbenchs = 50;
    static const unsigned long long int ntests = 10000000;
    unsigned long long int sum = 0;
    double result = 0;
    double mean = 0;
    std::chrono::high_resolution_clock::time_point t0 = std::chrono::high_resolution_clock::now();

    // Loop of benchmarks
    for (unsigned long long int ibench = 0; ibench < nbenchs; ++ibench) {

        // Initialization
        t0 = std::chrono::high_resolution_clock::now();
        sum = 0;

        // Loop of tests
        for (unsigned long long int itest = 1; itest <= ntests; ++itest) {
#ifdef _FIRST
            sum += f1((itest+sum)%primes[0], (itest+sum)%primes[1], (itest+sum)%primes[2], (itest+sum)%primes[3], (itest+sum)%primes[4], (itest+sum)%primes[5], (itest+sum)%primes[6], (itest+sum)%primes[7], (itest+sum)%primes[8], (itest+sum)%primes[9]);
#endif
#ifdef _SECOND
            sum += f2((itest+sum)%primes[0], (itest+sum)%primes[1], (itest+sum)%primes[2], (itest+sum)%primes[3], (itest+sum)%primes[4], (itest+sum)%primes[5], (itest+sum)%primes[6], (itest+sum)%primes[7], (itest+sum)%primes[8], (itest+sum)%primes[9]);
#endif
        }

        // Finalization
        result = std::chrono::duration_cast<std::chrono::duration<double>>(std::chrono::high_resolution_clock::now()-t0).count();
        mean += result;
        std::cout<<"time = "<<result<<" (sum = "<<sum<<")"<<std::endl;
    }

    // End
    std::cout<<"mean time = "<<mean/nbenchs<<std::endl;
    return 0;
}

对于具有给定参数数量的每个解决方案有 50 个基准,离散度非常小,这些基准的平均时间是一个可靠的指标。

我的第一个基准测试是使用“正确”数量的参数,其中 Alon 解决方案比 AndyProwl 解决方案更快。

最终结果在这里:

因此,AndyProwl 解决方案通常比 Alon 解决方案更快。所以,现在我可以验证你的答案。但我认为差异是如此之小,以至于它依赖于架构/编译器。

所以:

  • AndyProwl+1 为您提供通常更快的解决方案
  • Alon+1 用于您的 constexpr-ready 解决方案

【讨论】:

  • 因为我已经发布了代码,你可以运行它几次并计算分散。
  • 两种解决方案是可以互换的,因为编译器可以通过“as-if”原则执行一个而不是另一个。没有副作用,结果是一样的。因此,充其量,您的基准测试会检测到潜在的优化失误。 -1
  • 事实上,这两种方法都会产生完全相同的代码,例如对于两个参数的情况。见here
【解决方案3】:

你可以分别执行它们并返回一个布尔表达式:

bool b0 = f(arg);
bool b1 = f(args);
return b0 && b1;

【讨论】:

    【解决方案4】:

    没有递归:

    template <typename... Types>
    bool f(Types&&... args)
    {
      bool r=true;
      (void)std::initializer_list<bool>{(r = f(args)&&r)...};
      return r;
    }
    

    【讨论】:

      【解决方案5】:

      还有一个更好的技巧, 而不是在所有函数之间使用 &&,只需使用一个 &

      static_cast&lt;bool&gt;(f(arg)) &amp; static_cast&lt;bool&gt;(f2(args)) ...无论结果如何,都会运行所有操作:)

      【讨论】:

      • 这可以用static_cast&lt;bool&gt; 修复。我喜欢这种方法,因为它比带有辅助变量的方法更紧凑。
      • @Alon:在大多数的情况下?这只意味着这将是一个更危险的错误。但是好的,你“修复”了它,所以我把 -1 拿回来了。但它不再“好”了(恕我直言)。
      • 我不会称之为“更好的把戏”。它只会使代码更难阅读、调试和维护。 @Vincent:你为什么不只使用临时变量?没有性能损失,如果这是您所关心的,并且它使您的意图明确。这个没有。习惯阅读 C++ 的人实际上期望在阅读这样的一段代码时看到短路,因为这是通常的行为,因为&amp;不是 通常用于逻辑连词。你不必接受我的回答,只是请不要从中学习。
      • @Vincent:这样做不会给您带来一飞秒的性能提升,任何编译器都只会优化临时变量。如果编译器真的很笨,那么我认为它更有可能会为 this 版本引入性能损失,因为转换为 bool 和按位 &.
      • @AndyProwl:我将不得不检查程序集,但我刚刚对您的解决方案和这个(没有 static_cast,因为 f 返回布尔值)做了 4 个系列的 50 个基准测试,而这个一个比你的快 7.5%(g++ 4.7.2 with -O3)。
      猜你喜欢
      • 2017-04-14
      • 2020-02-20
      • 1970-01-01
      • 2013-09-10
      • 1970-01-01
      • 2010-10-22
      • 1970-01-01
      • 1970-01-01
      • 2018-09-30
      相关资源
      最近更新 更多