内置mod('%')vs自定义mod功能:提高模数运算的性能

mad*_*MDT 6 c++ modular-arithmetic

最近我发现mod('%')运算符非常慢.所以我创建了一个像%b一样工作的函数.但它比mod运算符更快吗?

这是我的功能

int mod(int a, int b)
{
    int tmp = a/b;
    return a - (b*tmp);
}
Run Code Online (Sandbox Code Playgroud)

mad*_*uri 20

根据Chandler Carruth在CppCon 2015上的基准测试,最快的模运算符(在x86上,与Clang编译时)是:

int fast_mod(const int input, const int ceil) {
    // apply the modulo operator only when needed
    // (i.e. when the input is greater than the ceiling)
    return input >= ceil ? input % ceil : input;
    // NB: the assumption here is that the numbers are positive
}
Run Code Online (Sandbox Code Playgroud)

我建议你观看整个演讲,他会详细介绍为什么这种方法比%无条件使用更快.

  • AFAIK Chandler Carruth 致力于编译器构造。我理解他的演讲,他试图解释编译器构建者如何找到和处理改进的优化。抵制对基本内容进行手工“优化”实现的诱惑将帮助您在编译器拥有该工作后立即从该工作中获利 - 当然还有所有您没有想到的优化。注意:我没有说“不优化”,而是“抵制微优化”。 (2认同)
  • 我也没有看过视频,但`fast_mod` 似乎因负输入而损坏:`-5 % 5 == 0` 但`fast_mod(-5, 5) == -5`。 (2认同)
  • 是的。正如最后一条评论中提到的:_“注意:这里的假设是数字是正数”_。(“数字”,我的意思是“输入”和“ceil”) (2认同)
  • 哦,对不起,我没有看到这个.仍然似乎值得指出,优化仅适用于非常特殊的情况.C的`%`不仅定义为`int`而且定义为正数. (2认同)

Gal*_*lik 8

这可能是编译器和平台相关的.

但我感兴趣的是,在我的系统中,你的基准测试似乎是正确的.然而@ 865719答案的方法最快:

#include <chrono>
#include <iostream>

class Timer
{
    using clk = std::chrono::steady_clock;
    using microseconds = std::chrono::microseconds;

    clk::time_point tsb;
    clk::time_point tse;

public:

    void clear() { tsb = tse = clk::now(); }
    void start() { tsb = clk::now(); }
    void stop() { tse = clk::now(); }

    friend std::ostream& operator<<(std::ostream& o, const Timer& timer)
    {
        return o << timer.secs();
    }

    // return time difference in seconds
    double secs() const
    {
        if(tse <= tsb)
            return 0.0;
        auto d = std::chrono::duration_cast<microseconds>(tse - tsb);
        return d.count() / 1000000.0;
    }
};

int mod(int a, int b)
{
    int tmp=a/b;
    return a-(b*tmp);
}

int fast_mod(const int input, const int ceil) {
    // apply the modulo operator only when needed
    // (i.e. when the input is greater than the ceiling)
    return input > ceil ? input % ceil : input;
    // NB: the assumption here is that the numbers are positive
}

int main()
{
    auto N = 1000000000U;
    unsigned sum = 0;

    Timer timer;

    for(auto times = 0U; times < 3; ++times)
    {
        std::cout << "     run: " << (times + 1) << '\n';

        sum = 0;
        timer.start();
        for(decltype(N) n = 0; n < N; ++n)
            sum += n % (N - n);
        timer.stop();

        std::cout << "       %: " << sum << " " << timer << "s" << '\n';

        sum = 0;
        timer.start();
        for(decltype(N) n = 0; n < N; ++n)
            sum += mod(n, N - n);
        timer.stop();

        std::cout << "     mod: " << sum << " " << timer << "s" << '\n';

        sum = 0;
        timer.start();
        for(decltype(N) n = 0; n < N; ++n)
            sum += fast_mod(n, N - n);
        timer.stop();

        std::cout << "fast_mod: " << sum << " " << timer << "s" << '\n';
    }
}
Run Code Online (Sandbox Code Playgroud)

构建: GCC 5.1.1(x86_64)

g++ -std=c++14 -march=native -O3 -g0 ...
Run Code Online (Sandbox Code Playgroud)

输出:

     run: 1
       %: 3081207628 5.49396s
     mod: 3081207628 4.30814s
fast_mod: 3581207628 2.51296s
     run: 2
       %: 3081207628 5.5522s
     mod: 3081207628 4.25427s
fast_mod: 3581207628 2.52364s
     run: 3
       %: 3081207628 5.4947s
     mod: 3081207628 4.29646s
fast_mod: 3581207628 2.56916s
Run Code Online (Sandbox Code Playgroud)