【问题标题】:is cvCeil() faster than standard library?cvCeil() 比标准库快吗?
【发布时间】:2020-03-24 05:43:34
【问题描述】:

我看到OpenCV实现了cvCeil函数:

CV_INLINE  int  cvCeil( double value )
{
#if defined _MSC_VER && defined _M_X64 || (defined __GNUC__ && defined __SSE2__&& !defined __APPLE__)
    __m128d t = _mm_set_sd( value );
    int i = _mm_cvtsd_si32(t);
    return i + _mm_movemask_pd(_mm_cmplt_sd(_mm_cvtsi32_sd(t,i), t));
#elif defined __GNUC__
    int i = (int)value;
    return i + (i < value);
#else
    int i = cvRound(value);
    float diff = (float)(i - value);
    return i + (diff < 0);
#endif
}

我很好奇这个实现的第一部分,即_mm_set_sd 相关的调用。它们会比 MSVCRT / libstdc++ / libc++ 更快吗?为什么?

【问题讨论】:

    标签: c++ optimization c++-standard-library


    【解决方案1】:

    下面的一个简单基准告诉我,std::round 在启用 SSE4 的机器上运行速度提高了 3 倍以上,但在未启用 SSE4 时大约慢了 2 倍。

    #include <cmath>
    #include <chrono>
    #include <sstream>
    #include <iostream>
    #include <opencv2/core/fast_math.hpp>
    
    auto currentTime() { return std::chrono::steady_clock::now(); }
    
    template<typename T, typename P>
    std::string toString(std::chrono::duration<T,P> dt)
    {
        std::ostringstream str;
        using namespace std::chrono;
        str << duration_cast<microseconds>(dt).count()*1e-3 << " ms";
        return str.str();
    }
    
    int main()
    {
        volatile double x=34.234;
        volatile double y;
        constexpr auto MAX_ITER=100'000'000;
        const auto t0=currentTime();
        for(int i=0;i<MAX_ITER;++i)
            y=std::ceil(x);
        const auto t1=currentTime();
        for(int i=0;i<MAX_ITER;++i)
            y=cvCeil(x);
        const auto t2=currentTime();
        std::cout << "std::ceil: " << toString(t1-t0) << "\n"
                     "cvCeil   : " << toString(t2-t1) << "\n";
    }
    

    我在 GCC 8.3.0、glibc-2.27、Ubuntu 18.04.1 x86_64 上使用 -O3 选项在 Intel Core i7-3930K 3.2 GHz 上进行测试。

    使用-msse4编译时的输出:

    std::ceil: 39.357 ms
    cvCeil   : 143.224 ms
    

    不使用-msse4编译时的输出:

    std::ceil: 274.945 ms
    cvCeil   : 146.218 ms
    

    很容易理解:SSE4.1 引入了ROUNDSD 指令,基本上就是std::round 所做的。在此之前,编译器必须执行一些比较/条件移动技巧,并且还必须确保这些技巧不会溢出。因此,cvCeil 版本牺牲了value&gt;INT_MAXvalue&lt;INT_MIN 的明确定义,获得了明确定义的值的加速。对于其他人,它具有未定义的行为(或者,对于内在函数,只会给出错误的结果)。

    【讨论】:

      猜你喜欢
      • 2016-12-12
      • 2011-04-17
      • 1970-01-01
      • 2010-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多