在所有M * N组合中实例化模板函数

时间:2017-05-28 06:31:13

标签: c++ performance templates

想象一下,我想要时间的M方法,以及N个时序方法(让我们称之为时钟实现 1 。具体细节在这里并不重要,但我提到它所以我可以举一个具体的例子。

现在让我说我有一个模板化的计时方法:

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

基本上它通过调用METHOD来约束对CLOCK::now()的调用并返回差异。另请注意,METHOD不是作为函数指针传递,而是仅作为模板参数传递 - 因此您获得每个方法的唯一实例化,而不是一个,然后通过一个间接调用指针。

这适用于我的情况,因为时钟调用和被测方法都是直接静态调用(即汇编级别的call <function address>)。

现在我想要测试N个方法(可能是50个)和M个时钟方法(可能是5个)。我想在编译时实例化所有M * N方法,以便我可以使用特定的时钟实现来调用所有测试方法。

现在,执行此操作的“标准”方法只是为被测方法和时钟实现传递函数指针(或实现虚函数的类),此时我只需要一个{{ 1}}方法,可以在运行时创建我想要的任何组合。在这种特殊情况下,间接调用的性能影响太大,所以我想要模板实例化,并且我愿意支付最终的二进制膨胀(例如,M * N = 250实例化我的数字组合)。

在运行时,我想获得一个N方法的列表,例如与特定时钟相结合。

我很好地明确列出了所有N方法和所有M个时钟,但我不想写出M * N实例(DRY和所有这些)。

1 我在这里使用 clock 这个词 - 一些“时钟”实际上可能测量与时间无关的方面,例如堆内存使用,或某些特定于应用程序的指标。

2 个答案:

答案 0 :(得分:8)

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

答案 1 :(得分:3)

要制作代码,您必须使用选项数量而不是产品的总和来编写线性,编写一次删除一层选项的模板函数。

e.g。

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