寻找一种单一的计时方法来测试各种算法(不包括其输入)

时间:2019-04-12 12:28:17

标签: c++ design-patterns

假设我要测试以下几种算法。

void AlgoA(int x)
{
    // critical operations go here 
}

void AlgoB(int x, int y)
{
    // critical operations go here
}

第一种方法

我定义了Timer,它接受​​指向函数的无参数指针。

void Timer(void (*f)(), unsigned short N = 1)
{
    vector<unsigned long long> results;
    for (unsigned short i = 0; i < N; i++)
    {
        chrono::steady_clock::time_point begin = chrono::steady_clock::now();
        f();
        chrono::steady_clock::time_point end = chrono::steady_clock::now();

        unsigned long long interval = chrono::duration_cast<chrono::microseconds>(end - begin).count();
        results.push_back(interval);
        cout << "Elapsed time: " << interval << std::endl;
    }

    unsigned long long sum = 0;
    for (unsigned long long x : results)
        sum += x;
    cout << "Average: " << sum / results.size() << endl;
}

需要包装器来准备输入。

void DoA()
{
    int x;
    // preparing  x goes here

    AlgoA(x);
}
void DoB()
{
    int x, y;
    // preparing  x and y goes here

    AlgoB(x, y);
}
void main()
{
    Timer(DoA);
    Timer(DoB);
}

缺点:Timer还计算准备输入所用的时间。

优点:许多算法测试的通用Timer

第二种方法

我必须编写2个计时器,每个计时器用于算法测试。

void TimerA(void (*f)(int), int x, unsigned short N = 1)
{
    vector<unsigned long long> results;
    for (unsigned short i = 0; i < N; i++)
    {
        chrono::steady_clock::time_point begin = chrono::steady_clock::now();
        f(x);
        chrono::steady_clock::time_point end = chrono::steady_clock::now();

        unsigned long long interval = chrono::duration_cast<chrono::microseconds>(end - begin).count();
        results.push_back(interval);
        cout << "Elapsed time: " << interval << std::endl;
    }

    unsigned long long sum = 0;
    for (unsigned long long x : results)
        sum += x;
    cout << "Average: " << sum / results.size() << endl;
}
void TimerB(void (*f)(int, int), int x, int y, unsigned short N = 1)
{
    vector<unsigned long long> results;
    for (unsigned short i = 0; i < N; i++)
    {
        chrono::steady_clock::time_point begin = chrono::steady_clock::now();
        f(x, y);
        chrono::steady_clock::time_point end = chrono::steady_clock::now();

        unsigned long long interval = chrono::duration_cast<chrono::microseconds>(end - begin).count();
        results.push_back(interval);
        cout << "Elapsed time: " << interval << std::endl;
    }

    unsigned long long sum = 0;
    for (unsigned long long x : results)
        sum += x;
    cout << "Average: " << sum / results.size() << endl;
}



void main()
{
    int x;
    // preparing x goes here.
    TimerA(AlgoA, x);

    int y;
    // preparing y goes here.
    TimerB(AlgoB, x, y);
}

优点:计时器仅计算关键操作。

缺点:多个计时器,每个计时器都供算法测试。

问题

有什么方法可以只创建一个Timer,但不能计算准备输入所需的时间?

编辑:

实际上,输入不仅是int,还可以是struct等,可以从与时间有关的IO中检索到。

3 个答案:

答案 0 :(得分:2)

我必须承认,我没有看到问题。也许问题是您过度指定了要执行的操作。如果您要将可调用对象传递给方法并对其进行调用,则应为:

template <typename F>
void call_it(F f) {
    // start timer
    f();
    // stop timer
}

现在您可以将几乎所有内容传递给它,例如:

int x = some_expensive_precalculation();
call_it( [&]() { method_to_time(x); });

请注意,由于未直接调用该函数,您可能会遇到少量开销。但是,与任何值得衡量的东西相比,我希望这可以忽略不计,而且通过编译器优化,可能根本没有开销。

答案 1 :(得分:2)

如果您想始终在计时器之外准备输入,可以将std::functionstd::bind一起使用

void timer(std::function<void()> algorithm, unsigned short N = 1) {
    // your timer code here
}

void algoA(int x)
{
    // critical operations go here 
}

void algoB(int x, int y)
{
    // critical operations go here
}

int main() {
    int x, y;  // prepare input
    timer(std::bind(algoA, x));
    timer(std::bind(algoB, x, y));
}

答案 2 :(得分:2)

您可以使用可变参数模板并将参数转发给测试对象。<​​/ p>

#include <chrono>
#include <iostream>
#include<vector>

using namespace std;

template<typename ... Args>
void benchmark(void (*f)(Args...), unsigned short N, Args&&... args)
{
    vector<unsigned long long> results;
    for (unsigned short i = 0; i < N; i++)
    {
    chrono::steady_clock::time_point begin = chrono::steady_clock::now();
    f(std::forward<Args>(args)...);
    chrono::steady_clock::time_point end = chrono::steady_clock::now();

    unsigned long long interval = chrono::duration_cast<chrono::microseconds>(end - begin).count();
    results.push_back(interval);
    cout << "Elapsed time: " << interval << std::endl;
    }

    unsigned long long sum = 0;
    for (unsigned long long x : results)
    sum += x;
    cout << "Average: " << sum / results.size() << endl;
}

void fun(int a, float b, char c) {

}

int main() {

    benchmark(fun, 500, 42, 3.1415f, 'A');

    return 0;
}

认为,这种方法无法为N设置默认参数。但这也许对您而言并不重要。