Performing a template function in all M * N combinations

Imagine that I have M-methods that I want to use in time, along with N synchronization methods (let me call them clock implementations) 1 . The exact details are not very important here, but I mention this, so I can give a concrete example.

Now let's say that I have a template synchronization method, for example:

typedef void (bench_f)(uint64_t);

template <bench_f METHOD, typename CLOCK>
uint64_t time_method(size_t loop_count) {
  auto t0 = CLOCK::now();
  METHOD(loop_count);
  auto t1 = CLOCK::now();
  return t1 - t0;
}

Basically it will copy the call METHODwith calls CLOCK::now()and return the difference. Also note that METHODit is not passed as a pointer to a function, but rather only as an argument to the template, so you get unique instances for each method, not one, and then an indirect call through the pointer.

This works well for my case, because both the clock calls and the test method are direct static calls (e.g., something like call <function address>assembly level).

Now I have N methods that I want to test (maybe 50) along with the M clock methods (maybe 5). I want to actually create an instance during compilation of all M * N methods so that I can call all test methods with a specific implementation of the clock.

"" - ( , ) , , time_method , . , , (, M * N = 250 ).

, , N .

N- M-, M * N (DRY ).


1 clock - "" , , - .

+4
2
template<bench_f* ...> struct method_list {};
template<class...> struct clock_list {};

using time_method_t = uint64_t (*)(size_t);

template<bench_f Method, class...Clocks>
constexpr auto make_single_method_table()
    -> std::array<time_method_t, sizeof...(Clocks)> {
    return { time_method<Method, Clocks>... };
}

template<bench_f*... Methods, class... Clocks>
constexpr auto make_method_table(method_list<Methods...>, clock_list<Clocks...>)
    -> std::array<std::array<time_method_t, sizeof...(Clocks)>, sizeof...(Methods)> {
    return { make_single_method_table<Methods, Clocks...>()... };
}
+8

, , , .

.

typedef uint64_t (*benchmark_runner)(size_t loop_count);

benchmark_runner all_runners[NMETHODS][NCLOCKS];

template <bench_f METHOD>
void fill_row(size_t bench_f_index)
{
    benchmark_runner* it = &all_runners[bench_f_index][0];
    *(it++) = &time_method<METHOD, FIRST_CLOCK>;
    *(it++) = &time_method<METHOD, SECOND_CLOCK>;
    *(it++) = &time_method<METHOD, THIRD_CLOCK>;
    *(it++) = &time_method<METHOD, LAST_CLOCK>;
}

void fill_all()
{
    int row = 0;
    fill_row<BENCH_A>(row++);
    fill_row<BENCH_B>(row++);
    ...
    fill_row<BENCH_Z>(row++);
}
+3

Source: https://habr.com/ru/post/1678059/


All Articles