N функций x M наборов данных с бенчмарком Google

Представьте, что я хотел бы протестировать процедуры сериализации / десериализации для трех разных наборов данных. Это приводит к 2x3=6 тестам.

В идеале я хотел бы добиться следующего:

  • избежать дублирования кода
  • вызывать функции генератора наборов данных только один раз за исполняемый вызов и только если они не исключены --benchmark_filter=... (функции генератора стоят дорого)
  • значимые имена тестов (например, "Serialize/DatasetAlpha")

Кажется, ни одна из функций, упомянутых в руководстве, не соответствует цели. Ближайшее решение, которое я нашел до сих пор, это использование параметризованного vararg Serialize()/Deserialize() функции вместе с функциями генератора, которые будут возвращать сгенерированные данные в виде синглетонов.

Есть ли способ лучше?

Вот чего я хотел бы избежать:

#include <benchmark/benchmark.h>

/* library */
std::string serialize(const std::string& data) {
  return data;
}
std::string deserialize(const std::string& data) {
  return data;
}

/* helpers */
void SerializeHelper(benchmark::State& state, const std::string& data) {
  for (auto _ : state) {
    std::string bytes = serialize(data);
    benchmark::DoNotOptimize(bytes);
  }
}

void DeserializeHelper(benchmark::State& state, const std::string& data) {
  std::string bytes = serialize(data);
  for (auto _ : state) {
    std::string data_out = deserialize(data);
    benchmark::DoNotOptimize(data_out);
  }
}

std::string GenerateDatasetAlpha() {
  return "";
}
std::string GenerateDatasetBeta() {
  return "";
}
std::string GenerateDatasetGamma() {
  return "";
}


/* oh, my... */
void SerializeAlpha(benchmark::State& state) {
  SerializeHelper(state, GenerateDatasetAlpha());
}
void DeserializeAlpha(benchmark::State& state) {
  DeserializeHelper(state, GenerateDatasetAlpha());
}
void SerializeBeta(benchmark::State& state) {
  SerializeHelper(state, GenerateDatasetBeta());
}
void DeserializeBeta(benchmark::State& state) {
  DeserializeHelper(state, GenerateDatasetBeta());
}
void SerializeGamma(benchmark::State& state) {
  SerializeHelper(state, GenerateDatasetGamma());
}
void DeserializeGamma(benchmark::State& state) {
  DeserializeHelper(state, GenerateDatasetGamma());
}

BENCHMARK(SerializeAlpha);
BENCHMARK(DeserializeAlpha);
BENCHMARK(SerializeBeta);
BENCHMARK(DeserializeBeta);
BENCHMARK(SerializeGamma);
BENCHMARK(DeserializeGamma);

BENCHMARK_MAIN();

//g++ wtf.cc -o wtf -I benchmark/include/ -lbenchmark -L benchmark/build/src -lpthread -O3

1 ответ

Самое близкое решение, которое я нашел до сих пор, состоит в использовании шаблонных тестов с классами генератора для набора данных:

#include <benchmark/benchmark.h>

/* library */
std::string serialize(const std::string& data) {
  return data;
}
std::string deserialize(const std::string& data) {
  return data;
}

/* benchmarks routines */
template<typename Dataset>
void SerializeBenchmark(benchmark::State& state) {
  std::string data = Dataset()();
  for (auto _ : state) {
    std::string bytes = serialize(data);
    benchmark::DoNotOptimize(bytes);
  }
}

template<typename Dataset>
void DeserializeBenchmark(benchmark::State& state) {
  std::string data = Dataset()();
  std::string bytes = serialize(data);
  for (auto _ : state) {
    std::string data_out = deserialize(data);
    benchmark::DoNotOptimize(data_out);
  }
}

/* datasets generators and benchmark registration */

struct Dataset1 {
  std::string operator()() {
    return ""; // load from file, generate random data, etc
  }
};
BENCHMARK_TEMPLATE(SerializeBenchmark, Dataset1);
BENCHMARK_TEMPLATE(DeserializeBenchmark, Dataset1);

struct Dataset2 {
  std::string operator()() { return ""; }
};
BENCHMARK_TEMPLATE(SerializeBenchmark, Dataset2);
BENCHMARK_TEMPLATE(DeserializeBenchmark, Dataset2);

struct Dataset3 {
  std::string operator()() { return ""; }
};
BENCHMARK_TEMPLATE(SerializeBenchmark, Dataset3);
BENCHMARK_TEMPLATE(DeserializeBenchmark, Dataset3);

BENCHMARK_MAIN();

Это сохраняет объем кода на достаточно низком уровне. Имена тестов тоже хороши, например SerializeBenchmark<Dataset2>, Функции генерации наборов данных по-прежнему вызываются несколько раз, поэтому, если вы хотите избежать этого, вам придется хранить их в одиночных загрузках с отложенной загрузкой.

Другие вопросы по тегам