cvCeil () быстрее стандартной библиотеки? - PullRequest
1 голос
/ 24 марта 2020

Я вижу, что 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 ++? И почему?

1 Ответ

2 голосов
/ 24 марта 2020

Простой тест ниже говорит мне, что std::round работает более чем в 3 раза быстрее на моем компьютере с SSE4, но примерно в 2 раза медленнее, когда SSE4 не включен.

#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";
}

Я тестирую с * Опция 1005 * на G CC 8.3.0, glib c -2.27, Ubuntu 18.04.1 x86_64 на Intel Core i7-3930K 3,2 ГГц.

Вывод при компиляции с -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>INT_MAX и для value<INT_MIN, получает ускорение для значений, для которых она четко определена. Для других он имеет неопределенное поведение (или, по сути, просто дает неправильные результаты).

...