Поиск единого метода синхронизации для тестирования различных алгоритмов, исключая их входные данные - PullRequest
1 голос
/ 12 апреля 2019

Предположим, что у меня есть несколько алгоритмов для проверки следующим образом.

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);
}

Плюсы: таймеры учитывают только критические операции.

Минусы: несколько таймеров, каждый для одногоalgo to test.

Вопрос

Можно ли просто создать один Timer, но он не должен считать время, необходимое для подготовки входных данных?

Редактировать:

Входные данные на самом деле не просто int, а struct и т. Д., Которые могут быть получены из зависимого от времени ввода-вывода.

Ответы [ 3 ]

2 голосов
/ 12 апреля 2019

Если вы хотите всегда готовить свои входы вне таймера, вы можете использовать std::function с std::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 голосов
/ 12 апреля 2019

Вы можете использовать шаблоны с переменным числом аргументов и передавать аргументы вашему объекту тестирования.

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

I думаю, нет способа получить аргумент по умолчанию для N при таком подходе,Но, возможно, это не так важно для вас.

2 голосов
/ 12 апреля 2019

Я должен признать, я не вижу проблемы. Может быть, проблема в том, что ты переоценил то, что хочешь сделать. Если вы хотите передать вызываемый метод и вызвать его, это будет:

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

Теперь вы можете передать ему почти все, например:

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

Обратите внимание, что вы можете столкнуться с незначительными накладными расходами из-за того, что не вызываете функцию напрямую. Тем не менее, я ожидаю, что это будет незначительным по сравнению с чем-либо, что стоит измерять, и с оптимизацией компилятора может быть вообще никаких издержек.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...