Bazel工程化集成google/benchmark

Bazel工程化集成google/benchmark

这是一个开源实践

使用 Bazel 集成 Google Benchmark 到你的项目中:

1. 添加依赖

在项目根目录下的WORKSPACE文件里添加 Google Benchmark 的依赖。你可以通过http_archive规则从 GitHub 下载源码。示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
workspace(name = "fury")

load("//bazel:fury_deps_setup.bzl", "setup_deps")
setup_deps()

load("@bazel_skylib//:workspace.bzl", "bazel_skylib_workspace")
load("@com_github_grpc_grpc//bazel:grpc_deps.bzl", "grpc_deps")
load("@com_github_grpc_grpc//third_party/py:python_configure.bzl", "python_configure")
load("//bazel/arrow:pyarrow_configure.bzl", "pyarrow_configure")
load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository")

# Add Benchmark
git_repository(
name = "com_google_benchmark",
remote = "https://github.com/google/benchmark.git",
tag = "v1.9.1",
)

bazel_skylib_workspace()
python_configure(name="local_config_python")
pyarrow_configure(name="local_config_pyarrow")
grpc_deps()

上述代码从 GitHub 下载了 Google Benchmark 1.8.3 版本和 Google Test 1.13.0 版本。可根据需求调整版本号。

2. 创建 BUILD 文件

在包含基准测试代码的目录下创建BUILD文件,以定义构建规则。以下是一个示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")

cc_library(
name = "fury_benchmark",
srcs = glob(["*.cc"], exclude=["benchmark*.cc"]),
hdrs = glob(["*.h"]),
strip_include_prefix = "/cpp",
alwayslink = True,
linkstatic = True,
deps = [
"//cpp/fury/util:fury_util",
"@com_google_benchmark//:benchmark",
],
visibility = ["//visibility:public"],
)


cc_test(
name = "benchmark_string_util",
srcs = ["benchmark_string_util.cc"],
deps = [
":fury_benchmark",
],
)
  • cc_binary规则用于构建可执行的基准测试程序。
  • benchmark规则是 Google Benchmark 提供的,用于运行基准测试并展示结果。

3. 编写基准测试代码

benchmark_string_util.cc文件中编写基准测试代码,示例如下:

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
const size_t num_tests = 1000;
const size_t string_length = 1000;

/*
* TEST Strings
*/
// Generate a vector of Ascii strings for testing
std::vector<std::string> generateAsciiString(size_t num_tests,
size_t string_length) {
std::vector<std::string> test_strings;
for (size_t i = 0; i < string_length; ++i) {
test_strings.push_back(generateUtf8(num_tests));
}
return test_strings;
}

const std::vector<std::string> test_ascii_strings =
generateAsciiString(num_tests, string_length);

// Generate a vector of Latin-1 strings for testing
std::vector<std::u16string> generateLatin1String(size_t num_tests,
size_t string_length) {
std::vector<std::u16string> test_strings;
for (size_t i = 0; i < num_tests; ++i) {
test_strings.push_back(generateLatin1(string_length));
}
return test_strings;
}

const std::vector<std::u16string> test_latin1_strings =
generateLatin1String(num_tests, string_length);

// Generate random UTF-16 string
std::vector<std::u16string> generateUTF16String(size_t num_tests,
size_t string_length) {
std::vector<std::u16string> test_strings;
for (size_t i = 0; i < string_length; ++i) {
test_strings.push_back(generateUtf16(num_tests));
}
return test_strings;
}

const std::vector<std::u16string> test_utf16_strings =
generateUTF16String(num_tests, string_length);

// Generate random UTF-8 string
std::vector<std::string> generateUTF8String(size_t num_tests,
size_t string_length) {
std::vector<std::string> test_strings;
for (size_t i = 0; i < string_length; ++i) {
test_strings.push_back(generateUtf8(num_tests));
}
return test_strings;
}

const std::vector<std::string> test_utf8_strings =
generateUTF8String(num_tests, string_length);

/*
* TEST IsAscii
*/

// Check if a string is ASCII (all characters <= 0x7F)
bool isAscii_BaseLine(const std::string &str) {
for (char c : str) {
if (static_cast<uint8_t>(c) > 0x7F) {
return false;
}
}
return true;
}

// Benchmark function for Baseline ASCII check
static void BM_IsAscii_BaseLine(benchmark::State &state) {
for (auto _ : state) {
for (const auto &str : test_ascii_strings) {
bool result = isAscii_BaseLine(str);
benchmark::DoNotOptimize(result); // Prevent compiler optimization
}
}
}

// Benchmark function for SIMD ASCII check
static void BM_IsAscii_SIMD(benchmark::State &state) {
for (auto _ : state) {
for (const auto &str : test_ascii_strings) {
bool result = fury::isAscii(str);
benchmark::DoNotOptimize(result); // Prevent compiler optimization
}
}
}

BENCHMARK(BM_IsAscii_BaseLine);
BENCHMARK(BM_IsAscii_SIMD);
//...

BENCHMARK(BM_Utf8ToUtf16_StandardLibrary);
BENCHMARK(BM_Utf8ToUtf16_BaseLine);
BENCHMARK(BM_Utf8ToUtf16_SIMD);

BENCHMARK_MAIN();
  • 引入benchmark/benchmark.h头文件。
  • 定义BM_EmptyLoop函数,该函数接受benchmark::State& state参数,函数内部是一个空循环。
  • 利用BENCHMARK(BM_EmptyLoop)注册基准测试函数。
  • BENCHMARK_MAIN()宏定义程序入口点。

4. 构建并运行基准测试

在项目根目录下使用以下命令构建并运行基准测试:

1
2
3
4
5
bazel build //cpp/fury/benchmark:all
bazel test //cpp/fury/benchmark:all
# You can also run a single benchmark to see how efficient it is.
# For example
bazel run //cpp/fury/benchmark:benchmark_string_util

运行命令后,Bazel 会构建基准测试程序并执行,最后输出基准测试结果。