|
1 |
| -# The pyperformance File Formats |
2 |
| - |
3 |
| -`pyperformance` uses two file formats to identify benchmarks: |
4 |
| - |
5 |
| -* manifest - a set of benchmarks |
6 |
| -- metadata - a single benchmark |
7 |
| - |
8 |
| -For each benchmark, there are two required files and several optional |
9 |
| -ones. Those files are expected to be in a specific directory structure |
10 |
| -(unless customized in the metadata). |
11 |
| - |
12 |
| -The structure (see below) is such that it's easy to maintain |
13 |
| -a benchmark (or set of benchmarks) on GitHub and distribute it on PyPI. |
14 |
| -It also simplifies publishing a Python project's benchmarks. |
15 |
| -The alternative is pointing people at a repo. |
16 |
| - |
17 |
| -Benchmarks can inherit metadata from other metadata files. |
18 |
| -This is useful for keeping common metadata for a set of benchmarks |
19 |
| -(e.g. "version") in one file. Likewise, benchmarks for a Python |
20 |
| -project can inherit metadata from the project's pyproject.toml. |
21 |
| - |
22 |
| -Sometimes a benchmark will have one or more variants that run using |
23 |
| -the same script. Variants like this are supported by `pyperformance` |
24 |
| -without requiring much extra effort. |
25 |
| - |
26 |
| - |
27 |
| -## Benchmark Directory Structure |
28 |
| - |
29 |
| -Normally a benchmark is structured like this: |
30 |
| - |
31 |
| -``` |
32 |
| -bm_NAME/ |
33 |
| - data/ # if needed |
34 |
| - requirements.txt # lock file, if any |
35 |
| - pyproject.toml |
36 |
| - run_benchmark.py |
37 |
| -``` |
38 |
| - |
39 |
| -(Note the "bm\_" prefix on the directory name.) |
40 |
| - |
41 |
| -"pyproject.toml" holds the metadata. "run_benchmark.py" holds |
42 |
| -the actual benchmark code. Both are necessary. |
43 |
| - |
44 |
| -`pyperformance` treats the metadata file as the fundamental source of |
45 |
| -information about a benchmark. A manifest for a set of benchmarks is |
46 |
| -effectively a mapping of names to metadata files. So a metadata file |
47 |
| -is essential. It can be located anywhere on disk. However, if it |
48 |
| -isn't located in the structure described above then the metadata must |
49 |
| -identify where to find the other files. |
50 |
| - |
51 |
| -Other than that, only a benchmark script (e.g. "run_benchmark.py" above) |
52 |
| -is required. All other files are optional. |
53 |
| - |
54 |
| -When a benchmark has variants, each has its own metadata file next to |
55 |
| -the normal "pyproject.toml", named "bm_NAME.toml". (Note the "bm\_" prefix.) |
56 |
| -The format of variant metadata files is exactly the same. `pyperformance` |
57 |
| -treats them the same, except that the sibling "pyproject.toml" is |
58 |
| -inherited by default. |
59 |
| - |
60 |
| - |
61 |
| -## Manifest Files |
62 |
| - |
63 |
| -A manifest file identifies a set of benchmarks, as well as (optionally) |
64 |
| -how they should be grouped. `pyperformance` uses the manifest to |
65 |
| -determine which benchmarks are available to run (and thus which to run |
66 |
| -by default). |
67 |
| - |
68 |
| -A manifest normally looks like this: |
69 |
| - |
70 |
| -``` |
71 |
| -[benchmarks] |
72 |
| -
|
73 |
| -name metafile |
74 |
| -bench1 somedir/bm_bench1/pyproject.toml |
75 |
| -bench2 somedir/pyproject.toml |
76 |
| -bench3 ../anotherdir |
77 |
| -``` |
78 |
| - |
79 |
| -The "benchmarks" section is a table with rows of tab-separated-values. |
80 |
| -The "name" value is how `pyperformance` will identify the benchmark. |
81 |
| -The "metafile" value is where `pyperformance` will look for the |
82 |
| -benchmark's metadata. If a metafile is a directory then it looks |
83 |
| -for "pyproject.toml" in that directory. |
84 |
| - |
85 |
| - |
86 |
| -### Benchmark Groups |
87 |
| - |
88 |
| -The other sections in the manifest file relate to grouping: |
89 |
| - |
90 |
| -``` |
91 |
| -[benchmarks] |
92 |
| -
|
93 |
| -name metafile |
94 |
| -bench1 somedir/bm_bench1 |
95 |
| -bench2 somedir/bm_bench2 |
96 |
| -bench3 anotherdir/mybench.toml |
97 |
| -
|
98 |
| -[groups] |
99 |
| -tag1 |
100 |
| -tag2 |
101 |
| -
|
102 |
| -[group default] |
103 |
| -bench2 |
104 |
| -bench3 |
105 |
| -
|
106 |
| -[group tricky] |
107 |
| -bench2 |
108 |
| -``` |
109 |
| - |
110 |
| -The "groups" section specifies available groups that may be identified |
111 |
| -by benchmark tags (see about tags in the metadata section below). Any |
112 |
| -other group sections in the manifest are automatically added to the list |
113 |
| -of available groups. |
114 |
| - |
115 |
| -If no "default" group is specified then one is automatically added with |
116 |
| -all benchmarks from the "benchmarks" section in it. If there is no |
117 |
| -"groups" section and no individual group sections (other than "default") |
118 |
| -then the set of all tags of the known benchmarks is treated as "groups". |
119 |
| -A group named "all" as also automatically added which has all known |
120 |
| -benchmarks in it. |
121 |
| - |
122 |
| -Benchmarks can be excluded from a group by using a `-` (minus) prefix. |
123 |
| -Any benchmark alraedy in the list (at that point) that matches will be |
124 |
| -dropped from the list. If the first entry in the section is an |
125 |
| -exclusion then all known benchmarks are first added to the list |
126 |
| -before the exclusion is applied. |
127 |
| - |
128 |
| -For example: |
129 |
| - |
130 |
| -``` |
131 |
| -[benchmarks] |
132 |
| -
|
133 |
| -name metafile |
134 |
| -bench1 somedir/bm_bench1 |
135 |
| -bench2 somedir/bm_bench2 |
136 |
| -bench3 anotherdir/mybench.toml |
137 |
| -
|
138 |
| -[group default] |
139 |
| --bench1 |
140 |
| -``` |
141 |
| - |
142 |
| -This means by default only "bench2" and "bench3" are run. |
143 |
| - |
144 |
| - |
145 |
| -### Merging Manifests |
146 |
| - |
147 |
| -To combine manifests, use the `[includes]` section in the manifest: |
148 |
| - |
149 |
| -``` |
150 |
| -[includes] |
151 |
| -project1/benchmarks/MANIFEST |
152 |
| -project2/benchmarks/MANIFEST |
153 |
| -<default> |
154 |
| -``` |
155 |
| - |
156 |
| -Note that `<default>` is the same as including the manifest file |
157 |
| -for the default pyperformance benchmarks. |
158 |
| - |
159 |
| - |
160 |
| -### A Local Benchmark Suite |
161 |
| - |
162 |
| -Often a project will have more than one benchmark that it will treat |
163 |
| -as a suite. `pyperformance` handles this without any extra work. |
164 |
| - |
165 |
| -In the dirctory holding the manifest file put all the benchmarks. Then |
166 |
| -put `<local>` in the "metafile" column, like this: |
167 |
| - |
168 |
| -``` |
169 |
| -[benchmarks] |
170 |
| -
|
171 |
| -name metafile |
172 |
| -bench1 <local> |
173 |
| -bench2 <local> |
174 |
| -bench3 <local> |
175 |
| -bench4 <local> |
176 |
| -bench5 <local> |
177 |
| -``` |
178 |
| - |
179 |
| -It will look for `DIR/bm_NAME/pyproject.toml`. |
180 |
| - |
181 |
| -If there are also variants, identify the main benchmark |
182 |
| -in the "metafile" value, like this: |
183 |
| - |
184 |
| -``` |
185 |
| -[benchmarks] |
186 |
| -
|
187 |
| -name metafile |
188 |
| -bench1 <local> |
189 |
| -bench2 <local> |
190 |
| -bench3 <local> |
191 |
| -variant1 <local:bench3> |
192 |
| -variant2 <local:bench3> |
193 |
| -``` |
194 |
| - |
195 |
| -`pyperformance` will look for `DIR/bm_BASE/bm_NAME.toml`, where "BASE" |
196 |
| -is the part after "local:". |
197 |
| - |
198 |
| - |
199 |
| -### A Project's Benchmark Suite |
200 |
| - |
201 |
| -A Python project can identify its benchmark suite by putting the path |
202 |
| -to the manifest file in the project's top-level pyproject.toml. |
203 |
| -Additional manifests can be identified as well. |
204 |
| - |
205 |
| -``` |
206 |
| -[tool.pyperformance] |
207 |
| -manifest = "..." |
208 |
| -manifests = ["...", "..."] |
209 |
| -``` |
210 |
| - |
211 |
| -(Reminder: that is the pyproject.toml, not the manifest file.) |
212 |
| - |
213 |
| - |
214 |
| -## Benchmark Metadata Files |
215 |
| - |
216 |
| -A benchmark's metadata file (usually pyproject.toml) follows the format |
217 |
| -specified in [PEP 621](https://www.python.org/dev/peps/pep-0621) and |
218 |
| -[PEP 518](https://www.python.org/dev/peps/pep-0518). So there are two |
219 |
| -supported sections in the file: "project" and "tool.pyperformance". |
220 |
| - |
221 |
| -A typical metadata file will look something like this: |
222 |
| - |
223 |
| -``` |
224 |
| -[project] |
225 |
| -version = "0.9.1" |
226 |
| -dependencies = ["pyperf"] |
227 |
| -dynamic = ["name"] |
228 |
| -
|
229 |
| -[tool.pyperformance] |
230 |
| -name = "my_benchmark" |
231 |
| -``` |
232 |
| - |
233 |
| -A highly detailed one might look like this: |
234 |
| - |
235 |
| -``` |
236 |
| -[project] |
237 |
| -name = "pyperformance_bm_json_dumps" |
238 |
| -version = "0.9.1" |
239 |
| -description = "A benchmark for json.dumps()" |
240 |
| -requires-python = ">=3.8" |
241 |
| -dependencies = ["pyperf"] |
242 |
| -urls = {repository = "https://github.com/python/pyperformance"} |
243 |
| -dynamic = ["version"] |
244 |
| -
|
245 |
| -[tool.pyperformance] |
246 |
| -name = "json_dumps" |
247 |
| -tags = "serialize" |
248 |
| -runscript = "bench.py" |
249 |
| -datadir = ".data-files/extras" |
250 |
| -extra_opts = ["--special"] |
251 |
| -``` |
252 |
| - |
253 |
| - |
254 |
| -### Inheritance |
255 |
| - |
256 |
| -For one benchmark to inherit from another (or from common metadata), |
257 |
| -the "inherits" field is available: |
258 |
| - |
259 |
| -``` |
260 |
| -[project] |
261 |
| -dependencies = ["pyperf"] |
262 |
| -dynamic = ["name", "version"] |
263 |
| -
|
264 |
| -[tool.pyperformance] |
265 |
| -name = "my_benchmark" |
266 |
| -inherits = "../common.toml" |
267 |
| -``` |
268 |
| - |
269 |
| -All values in either section of the inherited metadata are treated |
270 |
| -as defaults, on top of which the current metadata is applied. In the |
271 |
| -above example, for instance, a value for "version" in common.toml would |
272 |
| -be used here. |
273 |
| - |
274 |
| -If the "inherits" value is a directory (even for "..") then |
275 |
| -"base.toml" in that directory will be inherited. |
276 |
| - |
277 |
| -For variants, the base pyproject.toml is the default value for "inherits". |
278 |
| - |
279 |
| - |
280 |
| -### Inferred Values |
281 |
| - |
282 |
| -In some situations, omitted values will be inferred from other available |
283 |
| -data (even for required fields). |
284 |
| - |
285 |
| -* `project.name` <= `tool.pyperformance.name` |
286 |
| -* `project.*` <= inherited metadata (except for "name" and "dynamic") |
287 |
| -* `tool.pyperformance.name` <= metadata filename |
288 |
| -* `tool.pyperformance.*` <= inherited metadata (except for "name" and "inherits") |
289 |
| - |
290 |
| -When the name is inferred from the filename for a regularly structured |
291 |
| -benchmark, the "bm\_" prefix is removed from the benchmark's directory. |
292 |
| -If it is a variant that prefix is removed from the metadata filename, |
293 |
| -as well as the .toml suffix. |
294 |
| - |
295 |
| - |
296 |
| -### The `[project]` Section |
297 |
| - |
298 |
| -| field | type | R | T | B | D | |
299 |
| -|----------------------|-------|---|---|---|---| |
300 |
| -| project.name | str | X | X | | | |
301 |
| -| project.version | ver | X | | X | X | |
302 |
| -| project.dependencies | [str] | | | X | | |
303 |
| -| project.dynamic | [str] | | | | | |
304 |
| - |
305 |
| -"R": required |
306 |
| -"T": inferred from the tool section |
307 |
| -"B": inferred from the inherited metadata |
308 |
| -"D": for default benchmarks, inferred from pyperformance |
309 |
| - |
310 |
| -"dynamic" is required by PEP 621 for when a field will be filled in |
311 |
| -dynamically by the tool. This is especially important for required |
312 |
| -fields. |
313 |
| - |
314 |
| -All other PEP 621 fields are optional (e.g. `requires-python = ">=3.8"`, |
315 |
| -`{repository = "https://github.com/..."}`). |
316 |
| - |
317 |
| - |
318 |
| -### The `[tool.pyperformance]` Section |
319 |
| - |
320 |
| -| field | type | R | B | F | |
321 |
| -|-----------------|-------|---|---|---| |
322 |
| -| tool.name | str | X | | X | |
323 |
| -| tool.tags | [str] | | X | | |
324 |
| -| tool.extra_opts | [str] | | X | | |
325 |
| -| tool.inherits | file | | | | |
326 |
| -| tool.runscript | file | | X | | |
327 |
| -| tool.datadir | file | | X | | |
328 |
| - |
329 |
| -"R": required |
330 |
| -"B": inferred from the inherited metadata |
331 |
| -"F": inferred from filename |
332 |
| - |
333 |
| -* tags: optional list of names to group benchmarks |
334 |
| -* extra_opts: optional list of args to pass to `tool.runscript` |
335 |
| -* runscript: the benchmark script to use instead of run_benchmark.py. |
| 1 | +See doc/custom_benchmarks.rst. |
0 commit comments