快速 C++ 符号函数

fast c++ sign function

在我的代码中,我在一个循环中多次对 double 进行符号检查,并且该循环在执行期间通常 运行 数百万次。

我的符号检查是使用 fabs() 进行的非常基本的计算,所以我想一定有其他方法可以更快地完成它,因为“除法很慢”。我遇到了一个模板函数和 copysign() 并创建了一个简单的程序来 运行 进行速度比较。我已经用下面的代码测试了三种可能的解决方案。

// C++ program to find out execution time of  of functions 
#include <chrono> 
#include <iostream> 
#include <math.h>

using namespace std; 
using namespace std::chrono; 

template<typename Clock>

void printResult(const std::string name, std::chrono::time_point<Clock> start, std::chrono::time_point<Clock> stop, const int iterations)
{
    // Get duration. 
    std::chrono::duration my_duration = duration_cast<nanoseconds>(stop - start); 
    my_duration /= iterations;

    cout << "Time taken by "<< name <<" function: " << my_duration.count() << " ns avg. for " << iterations << " iterations." << endl << endl; 
}


template <typename T> int sgn(T val) 
{
    return (T(0) < val) - (val < T(0));
}


int main() {

    // ***************************************************************** //
    int numiters = 100000000;
    double vel = -0.6574;
    double result = 0;
    
    // Get starting timepoint 
    auto start_1 = high_resolution_clock::now(); 
    for(int x = 0; x < numiters; x++) 
    {

        result = (vel/fabs(vel)) * 12.1;

    }

    // Get ending timepoint 
    auto stop_1 = high_resolution_clock::now(); 
    cout << "Result is: " << result << endl;
    printResult("fabs", start_1, stop_1, numiters);

    // Get starting timepoint 
    result = 0;
    auto start_2 = high_resolution_clock::now(); 
    for(int x = 0; x < numiters; x++) 
    {

        result = sgn(vel) * 12.1;

    }

    // Get ending timepoint 
    auto stop_2 = high_resolution_clock::now(); 
    cout << "Result is: " << result << endl;
    printResult("sgn", start_2, stop_2, numiters);


    // Get starting timepoint 
    result = 0;
    auto start_10 = high_resolution_clock::now(); 
    for(int x = 0; x < numiters; x++) 
    {

        result = copysign(12.1, vel);

    }

    // Get ending timepoint 
    auto stop_10 = high_resolution_clock::now(); 
    cout << "Result is: " << result << endl;
    printResult("copysign", start_10, stop_10, numiters);

    cout << endl;


}

当我 运行 程序时,我有点惊讶地发现 fabs() 解决方案和 copysign 解决方案在执行时间上几乎相同。此外,当我多次 运行 时,我发现结果可能变化很大。

我的时间正确吗?还有比我测试过的三个例子更好的方法吗?

更新

我已经在 quick-bench.com 上实施了测试,其中可以指定编译器设置,所有 3 个结果似乎在那里几乎相同。我想我可能做错了什么: https://quick-bench.com/q/PJiAmoC2NQIJyuvbdz5ZHUALu2M

您的测试无效,因为您在计时内进行阻塞 I/O。

不过,我们可以使用quick-bench来分析:https://quick-bench.com/q/gt2KzKOFP4iV3ajmqANL_MhnMZk。这表明时间几乎完全相同。编译器生成的汇编代码呢?

double result = (vel/fabs(vel)) * 12.1;
   movabs [=10=]xc028333333333333,%rax
   mov    %rax,0x8(%rsp)
   add    [=10=]xffffffffffffffff,%rbx


double result = sgn(vel) * 12.1;
   movabs [=10=]xc028333333333333,%rax
   mov    %rax,0x8(%rsp)
   add    [=10=]xffffffffffffffff,%rbx


double result = copysign(12.1, vel);
   movabs [=10=]xc028333333333333,%rax
   mov    %rax,0x8(%rsp)
   add    [=10=]xffffffffffffffff,%rbx

优化代码时:答案始终是首先进行测量,找出程序中实际上最慢的部分,然后重写它以不执行任何该代码完全没有。

因为我穿你你的测试不测量任何东西!

来自你的quick-bench.com link click godbolt icon and see this disassembly.

请注意,您的所有版本都已转换为此汇编代码:

        movabs  rax, -4600370724363619533 # compile time evaluated result move outside measurement loop
.LBB0_3:                                  # =>This Inner Loop Header: Depth=1
        mov     qword ptr [rsp + 8], rax
        add     rbx, -1                   # measurement loop counter
        jne     .LBB0_3

所以基本上编译器能够完全删除测试代码,因为它注意到所有代码都可以在编译时进行常量计算!

因此您必须提供一些值来测试编译器在编译时无法确定的值。

这里my attempt to fix your test and its assembly看看优化了什么。我不保证这个措施是你必须自己做的正确的事情。测量如此小而活泼的代码是很难的。事实上,在如此少的 CPU 周期内执行的任何事情都无法通过软件精确可靠地测量。