File: benchmark_session_test.cpp

package info (click to toggle)
libfplus 0.2.13-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,904 kB
  • sloc: cpp: 27,543; javascript: 634; sh: 105; python: 103; makefile: 6
file content (166 lines) | stat: -rw-r--r-- 6,571 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// Copyright 2015, Tobias Hermann and the FunctionalPlus contributors.
// https://github.com/Dobiasd/FunctionalPlus
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt)

#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest/doctest.h"

#include <vector>
#include <fplus/fplus.hpp>
#include <fplus/benchmark_session.hpp>


// This is an example on how to use benchmark_session in order to bench separate parts of an algorithm

// We need to instantiate a session into which the stats will be collected
fplus::benchmark_session my_benchmark_session;

// antic C style qsort (will be benchmarked against std::sort)
void qsort_vec_int(std::vector<int> & v)
{
    auto cmp = [](const void * a, const void * b) {
        return ( *(static_cast<const int*>(a)) - *(static_cast<const int*>(b)) );
    };
    qsort (v.data(), v.size(), sizeof(int), cmp);
}

// Benchmarked function : several sub parts of this function are benchmarked separately
void benchmark_example()
{
    using Ints = std::vector<int>;

    // Example 1 : benchmark by replacing a function
    //
    // We want to benchmark the following code :
    //    Ints ascending_numbers = fplus::numbers(0, 1000);
    //
    // So, first we make an alternate version of the function "fplus::numbers"
    // Since fplus::numbers is a template function, we need to specify
    // that the actual version we want to benchmark
    // is "fplus::numbers<int, std::vector<int>>"
    //
    // numbers_bench will our alternate version and it
    // has the same signature as fplus::numbers<int, std::vector<int>>,
    // except that it also stores stats into the benchmark session,
    // under the name "numbers"
    //
    // Note that make_benchmark_function *will add side effects* to the function
    // (since it stores data into the benchmark session at each call)
    auto numbers_bench = make_benchmark_function(
        my_benchmark_session,
        "numbers",
        fplus::numbers<int, std::vector<int>>
    );
    // Then, we replace the original code "Ints ascending_numbers = fplus::numbers(0, 1000);"
    // by a code that uses the benchmarked function
    Ints ascending_numbers = numbers_bench(0, 100000);

    // Example 2: benchmark by replacing an expression
    // Below, we will benchmark an expression
    // The original expression we want to benchmark was:
    //     Ints shuffled_numbers = fplus::shuffle(std::mt19937::default_seed, ascending_numbers);
    //
    // In order to do so, we just copy/paste this expression
    // into "bench_expression" like shown below.
    // This expression will then be benchmarked with the name "shuffle"
    //
    // Notes :
    //  - benchmark_expression is a preprocessor macro that uses an immediately invoked lambda (IIL)
    // - the expression can be paster as-is, and it is possible to not remove the ";"
    //   (although it also works if it is not present)
    Ints shuffled_numbers = benchmark_expression(
        my_benchmark_session,
        "shuffle",
        fplus::shuffle(std::mt19937::default_seed, ascending_numbers);
    );

    // Example 3: also benchmark by replacing an expression
    // The original expression was
    //    const auto sorted_numbers = fplus::sort(shuffled_numbers);
    const auto sorted_numbers = benchmark_expression(
        my_benchmark_session,
        "sort_shuffled_sequence",
        fplus::sort(shuffled_numbers);
    );
    // Verify that the sort has worked
    assert(sorted_numbers == ascending_numbers);

    // In this toy example, we will compare the performance
    // of sorting a shuffled sequence versus sorting a reversed sequence

    Ints descending_numbers = fplus::reverse(ascending_numbers); // this call is not benchmarked

    // here we benchmark the call to fplus::sort(descending_numbers)
    const auto sorted_numbers2 = benchmark_expression(
        my_benchmark_session,
        "sort_reverse_sequence",
        fplus::sort(descending_numbers);
    );
    // Verify that the sort has worked
    assert(sorted_numbers2 == ascending_numbers);

    // benchmark qsort
    benchmark_void_expression(my_benchmark_session, "qsort_reverse_sequence",  qsort_vec_int(descending_numbers) );
}


TEST_CASE("benchmark_example")
{
    // Example 4 : benchmark by replacing a function
    // We also want to benchmark the "benchmark_example" in its entirety
    auto benchmark_example_bench = make_benchmark_void_function(
        my_benchmark_session,
        "benchmark_example",
        benchmark_example);

    // For the sake of this test, we will run the benchmarked function several times
    fplus::execute_n_times(10, [&]() { benchmark_example_bench(); });

    // A call to :
    //
    //     std::cout << fplus::show(my_benchmark_session.report());
    //
    // Would output something like
    //
    // Function              |Nb calls|Total time|Av. time   |Deviation |
    // ----------------------+--------+----------+-----------+----------+
    // benchmark_example     |      10| 136.393ms|13639.255ns|2209.289ns|
    // sort_shuffled_sequence|      10|  57.006ms| 5700.557ns| 855.817ns|
    // shuffle               |      10|  49.040ms| 4903.998ns| 785.540ns|
    // qsort_reverse_sequence|      10|  24.777ms| 2477.678ns| 343.918ns|
    // sort_reverse_sequence |      10|   2.308ms|  230.782ns|  87.104ns|
    // numbers               |      10|   2.000ms|  199.965ns| 103.334ns|


    //////////// Unit tests assertions below ////////////////////////////

    // test report_list()
    {
        const auto reports = my_benchmark_session.report_list();
        REQUIRE_EQ(reports.size(), 6);
        const auto & one_report = reports.at("benchmark_example");
        REQUIRE_EQ(one_report.nb_calls, 10);
        REQUIRE(one_report.average_time == doctest::Approx(
            one_report.total_time / static_cast<double>(one_report.nb_calls)));
    }

    // test report()
    {
        const auto & report = my_benchmark_session.report();

        const auto & lines  = fplus::split_lines(false, report);
        REQUIRE_EQ(lines.size(), 8);

        const auto & lines_sizes = fplus::transform([](const std::string & s) {
            return s.size();
        }, lines );
        REQUIRE( fplus::all_the_same(lines_sizes) );

        const auto & check_nb_columns = fplus::transform([](const std::string & s) {
            return (fplus::count('|', s) + fplus::count('+', s) ) == 5;
        }, lines );
        REQUIRE(fplus::all(check_nb_columns));
    }
}