Skip to content

Commit 1ca47e7

Browse files
[benchmark] Add script to automate creation of new single-source benchmarks
Adds a `create_benchmark` script that automates the following three tasks: 1. Add a new Swift file (YourTestNameHere.swift), built according to the template below, to the {{single-source}}directory. 2. Add the filename of the new Swift file to CMakeLists.txt 3. Edit main.swift. Import and register your new Swift module. The process of adding new benchmarks is now automated and a lot less error-prone.
1 parent fecff67 commit 1ca47e7

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-0
lines changed

benchmark/scripts/create_benchmark.py

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
#!/usr/bin/env python
2+
3+
import argparse
4+
import os
5+
import re
6+
7+
benchmark_template = """//===--- {name}.swift -------------------------------------------===//
8+
//
9+
// This source file is part of the Swift.org open source project
10+
//
11+
// Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors
12+
// Licensed under Apache License v2.0 with Runtime Library Exception
13+
//
14+
// See https://swift.org/LICENSE.txt for license information
15+
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
import TestsUtils
20+
21+
public let {name} = [
22+
BenchmarkInfo(name: "{name}", runFunction: run_{name}, tags: [.validation, .api]),
23+
]
24+
25+
@inline(never)
26+
public func run_{name}(N: Int) {{
27+
// TODO
28+
}}"""
29+
30+
def main():
31+
p = argparse.ArgumentParser()
32+
p.add_argument('name', help='The name of the new benchmark to be created')
33+
args = p.parse_args()
34+
35+
# adds benchmark to `CMakeLists.txt`
36+
update_cmakelists(args.name)
37+
# create benchmark Swift file
38+
create_benchmark_file(args.name)
39+
# imports the benchmark module in `main.swift`
40+
add_import_benchmark(args.name)
41+
# registers the benchmark with the driver in `main.swift`
42+
add_register_benchmark(args.name)
43+
44+
def update_cmakelists(name):
45+
"""Adds a new entry to the `CMakeLists.txt` file with the given benchmark name."""
46+
relative_path = create_relative_path('../CMakeLists.txt')
47+
48+
file_contents = []
49+
with open(relative_path, 'r') as f:
50+
file_contents = f.readlines()
51+
52+
file_new_contents = insert_line_alphabetically(name, ' single-source/' + name + '\n', file_contents, r" single-source\/([a-zA-Z]+)")
53+
with open(relative_path, 'w') as f:
54+
for line in file_new_contents:
55+
f.write(line)
56+
57+
def create_benchmark_file(name):
58+
"""Creates a new Swift file with the given name based on the template and places it in the `single-source` directory."""
59+
relative_path = create_relative_path('../single-source/')
60+
61+
formatted_template = benchmark_template.format(name=name)
62+
source_file_path = os.path.join(relative_path, name + '.swift')
63+
with open(source_file_path, 'w') as f:
64+
f.write(formatted_template)
65+
66+
def add_import_benchmark(name):
67+
"""Adds an `import` statement to the `main.swift` file for the new benchmark."""
68+
relative_path = create_relative_path('../utils/main.swift')
69+
70+
# read current contents into an array
71+
file_contents = []
72+
with open(relative_path, 'r') as f:
73+
file_contents = f.readlines()
74+
75+
inserted = False
76+
# the test dependencies are placed before all benchmarks, so we have to insert the benchmark in the right alphabetical order after we have seen all test dependencies.
77+
read_test_dependencies = False
78+
previous_benchmark_name = None
79+
file_new_contents = []
80+
for line in file_contents:
81+
# check if this line is a definition of a benchmark and get its name
82+
match = re.search(r"import ([a-zA-Z]+)", line)
83+
if match and match.group(1):
84+
benchmark_name = match.group(1)
85+
# find where to insert the new benchmark in the right alphabetical order
86+
if (name < benchmark_name and previous_benchmark_name == None) or (name < benchmark_name and name > previous_benchmark_name):
87+
if read_test_dependencies:
88+
file_new_contents.append('import ' + name + '\n' + line)
89+
else:
90+
# all test dependencies are first specified, so from now on we can look where to insert the new benchmark.
91+
read_test_dependencies = True
92+
file_new_contents.append(line)
93+
else:
94+
file_new_contents.append(line)
95+
previous_benchmark_name = benchmark_name
96+
else:
97+
file_new_contents.append(line)
98+
99+
with open(relative_path, 'w') as f:
100+
for line in file_new_contents:
101+
f.write(line)
102+
103+
def add_register_benchmark(name):
104+
"""Adds an `import` statement to the `main.swift` file for the new benchmark."""
105+
relative_path = create_relative_path('../utils/main.swift')
106+
107+
file_contents = []
108+
with open(relative_path, 'r') as f:
109+
file_contents = f.readlines()
110+
111+
file_new_contents = insert_line_alphabetically(name, 'registerBenchmark(' + name + ')\n', file_contents, r"registerBenchmark\(([a-zA-Z]+)\)")
112+
with open(relative_path, 'w') as f:
113+
for line in file_new_contents:
114+
f.write(line)
115+
116+
def insert_line_alphabetically(name, new_line, lines, regex):
117+
"""Iterates through the given lines and executes the regex on each line to find where the new benchmark should be inserted with the given `new_line`."""
118+
# the name of the previous seen benchmark in order to insert the new one at the correct position
119+
previous_benchmark_name = None
120+
# the new contents of the file
121+
updated_lines = []
122+
for line in lines:
123+
# apply regex and get name of benchmark on this line
124+
match = re.search(regex, line)
125+
if match and match.group(1):
126+
benchmark_name = match.group(1)
127+
# check if we're at the line where we have to insert the new benchmark in the correct alphabetical order
128+
if (name < benchmark_name and previous_benchmark_name == None) or (name < benchmark_name and name > previous_benchmark_name):
129+
updated_lines.append(new_line + line)
130+
else:
131+
updated_lines.append(line)
132+
previous_benchmark_name = benchmark_name
133+
else:
134+
updated_lines.append(line)
135+
return updated_lines
136+
137+
def create_relative_path(file_path):
138+
return os.path.join(os.path.dirname(__file__), file_path)
139+
140+
if __name__ == "__main__":
141+
main()

0 commit comments

Comments
 (0)