C ++ использует std :: chrono для правильного измерения выполнения функций-членов - PullRequest
0 голосов
/ 20 октября 2018

Я хочу оптимизировать свое приложение, особенно скорость выполнения определенных функций.

Представьте, что есть класс с некоторыми функциями-членами

class Test
{
public:
    Test();
    virtual ~Test();
    int init(int arg1, double arg2);

private:
    [...]

, и в моем конструкторе я вызываю одну из следующих функций:эти методы

Test::Test()
{
    [...]
    int value = init(1, 1.2);
}

Как мне измерить время выполнения моего метода init(...) красивым и чистым способом, не ломая мою программу?

В данный момент я использую следующий код

Test::Test()
{
    [...]
    auto start = std::chrono::high_resolution_clock::now();

    int value = init(1, 1.2);

    auto stop = std::chrono::high_resolution_clock::now();
    std::chrono::duration<double> duration = stop - start;
    std::cout << duration.count() * 1000 << "ms\n";
}

Это работает, как и ожидалось, но я думаю, что это довольно грязно, и я хочу иметь "более чистое" решение.

Есть ли способ иметь какую-то функцию, которая принимает функцию-члени другие параметры, такие как

int value = countTime(function, arg1, arg2);

Я не знаю, возможно ли передать возвращаемое значение от function() до countTime(), чтобы не прерывать рабочий процесс моего кода.

РЕДАКТИРОВАТЬ: Это мой класс TimeMeasure

namespace tools 
{
    class TimeMeasure 
    {
    public:
        TimeMeasure() 
        {
            m_start = std::chrono::high_resolution_clock::now();
        }

        virtual ~TimeMeasure()
        {
            m_stop = std::chrono::high_resolution_clock::now();
            std::chrono::duration<double, std::milli> duration = m_stop - m_start;
            std::cout << duration.count() << "ms\n";
        }

    public:
        typedef std::chrono::time_point<std::chrono::high_resolution_clock> HighResClock;

    private:
        HighResClock m_start;
        HighResClock m_stop;
    };

    template <typename T, typename F, typename... Args>
    auto measure(T *t, F &&fn, Args... args)
    {
        tools::TimeMeasure timeMeasure;
        return (t->*fn)(std::forward<Args>(args)...);
    }
}

и в моем конструкторе Test() Я использую функцию measure таким образом

Test()
{
    [...]
    tools::measure(this, Test::init, filepath);
}

int init(const std::string& filepath) const принимает здесь строку в файл.Так что в моем случае это всего лишь один аргумент

К сожалению, я получаю invalid use of non-static member function 'int init(const string&) const' ошибку

Мне было бы интересно, если конструктор не является функцией-членом.Так почему я получаю эту ошибку?

РЕДАКТИРОВАТЬ 2:

Согласно ответу OznOg, я просто забыл передать указатель на свою функцию.

Так что это будет правильный вызов функции

tools::measure(this, &Test::init, filepath);

Ответы [ 3 ]

0 голосов
/ 21 октября 2018

Вы можете использовать такую ​​реализацию:

template<typename Fn, typename... Args>
typename std::enable_if<!std::is_void<typename std::result_of<Fn&&(Args...)>::type>::value, 
    std::tuple<std::chrono::duration<double>, typename std::result_of<Fn&&(Args...)>::type>
>::type
countTime(Fn&& fn, Args&&... args) {
    auto start = std::chrono::high_resolution_clock::now();
    auto fnret = std::forward<Fn>(fn)(std::forward<Args>(args)...);
    auto stop = std::chrono::high_resolution_clock::now();
    return std::make_tuple(stop - start, fnret);
}

template<typename Fn, typename... Args>
typename std::enable_if<std::is_void<typename std::result_of<Fn&&(Args...)>::type>::value, 
    std::tuple<std::chrono::duration<double>>
>::type
countTime(Fn&& fn, Args&&... args) {
    auto start = std::chrono::high_resolution_clock::now();
    std::forward<Fn>(fn)(std::forward<Args>(args)...);
    auto stop = std::chrono::high_resolution_clock::now();
    return std::make_tuple(stop - start);
}

template<typename R, class C, typename... Args>
typename std::enable_if<!std::is_same<R, void>::value,
    std::tuple<std::chrono::duration<double>, R>
>::type
countTime(R (C::*fn)(Args...), C& obj, Args&&... args) {
    auto start = std::chrono::high_resolution_clock::now();
    auto fnret = (obj.*fn)(std::forward<Args>(args)...);
    auto stop = std::chrono::high_resolution_clock::now();
    return std::make_tuple(stop - start, fnret);
}

template<class C, typename... Args>
std::tuple<std::chrono::duration<double>>
countTime(void (C::*fn)(Args...), C& obj, Args&&... args) {
    auto start = std::chrono::high_resolution_clock::now();
    (obj.*fn)(std::forward<Args>(args)...);
    auto stop = std::chrono::high_resolution_clock::now();
    return std::make_tuple(stop - start);
}

Это вернет кортеж с длительностью одного члена в случае функций void (*) (...) и кортеж с продолжительностью и типом возврата в случаефункций, возвращающих что-л.Работает с std :: bind и функциями-членами.Возможно, перегрузка может быть объединена в одну или две реализации, но я понятия не имею, как это сделать.

Пример вызова:

auto ret = countTime([](int a) -> int { 
    std::this_thread::sleep_for(std::chrono::milliseconds(a)); 
    return a * 2;
}, 10);
std::cout << "function executed in: " <<
    std::chrono::duration_cast<std::chrono::milliseconds>(std::get<0>(ret)).count() <<
    " milliseconds." << std::endl;
std::cout << "function returned: " << std::get<1>(ret) << std::endl;

Упрощенный пример вызова:

auto ret = countTime(&Test::init, *this, 1, 1.2);
int value = std::get<1>(ret);

std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(std::get<0>(ret)).count() << "ms" << std::endl;

Простой простой пример вызова, при условии, что int init(int, int) функция:

auto ret = countTime(init, 1, 1.2);
int value = std::get<1>(ret);

std::cout << std::chrono::duration_cast<std::chrono::milliseconds>(std::get<0>(ret)).count() << "ms" << std::endl;

Доступная версия доступна на onlinegdb .

0 голосов
/ 25 октября 2018

Я всегда использую Boost.Timer .

#include <boost/timer/timer.hpp>

...
boost::timer::cpu_timer timer;

for(int i = 0; i < 1000; ++i)
  funct();

std::cout << timer.format();

Показывает стена , пользователь и система время.

0 голосов
/ 20 октября 2018

Вы можете создать класс вроде:

struct MeasureTime {
    MeasureTime() : _start(std::chrono::high_resolution_clock::now()) {}

    ~MeasureTime() {
        auto stop = std::chrono::high_resolution_clock::now();
        std::chrono::duration<double> duration = stop - _start;
        std::cout << duration.count() * 1000 << "ms\n";
    }
private:
    std::chrono::time_point<std::chrono::high_resolution_clock>  _start;
};

и просто использовать его в своем коде:

Test::Test()
{
    MeasureTime mt;
    [...]
    { //or even this for just the init call
    MeasureTime mt2;
    int value = init(1, 1.2);
    }
}

ИМХО, это менее навязчиво, чем вы предлагали.

Если вам действительно нужна функция, вы можете попробовать обертку вроде:

template <class T, class F, class... Args>
auto MeasureTimeFn(T *t, F &&fn, Args... args) {
    MeasureTime timer;
     return (t->*fn)(std::forward<Args>(args)...);
}

и назвать ее так:

int value = MeasureTimeFn(this, &Test::init, 1, 1.2);

, но не уверены, что она действительно намного лучше.

Вы можете попытаться скрыть что-то с помощью макроса:

#define MEASURE(f, ...) \
  MeasureTimeFn(this, &std::remove_reference_t<decltype(*this)>::f, __VA_ARGS__)

таким образом вы можете написать

int value = MEASURE(init, 1, 1.2);

, что очень похоже на то, что вы просили, но работает только внутрифункции-члены, с функциями-членами (не статичными).

В любом случае, вероятно, это хорошее место для начала.

* EDIT * Если вы можете изменить наследование своего класса,вы можете попробовать

template<class T>
struct MeasureTool {
    template <class F, class... Args>
    auto measure(F &&fn, Args... args) {
        tools::TimeMeasure timeMeasure;
        return (static_cast<T*>(this)->*fn)(std::forward<Args>(args)...);
    }
};

class Test : public MeasureTool<Test>
{
public:
    Test();
    virtual ~Test() {}
    int init(const std::string &filepath) { _path = filepath; return 0; }
    const auto &getPath() const { return _path; }
private:
    std::string _path;

};

Test::Test()
{
    std::string filepath("/some/where");
    int value = measure(&Test::init, filepath);
    measure(&Test::getPath);
}

И, на этот раз, кажется, соответствует вашему самому первому требованию (но довольно навязчиво ...)

теперь все в ваших руках:)

...