Skip to content

Commit fc8bd83

Browse files
committed
Support building C extensions in benchmarks
Add ctypes and ctypes_argtypes benchmarks
1 parent 259edee commit fc8bd83

File tree

16 files changed

+228
-12
lines changed

16 files changed

+228
-12
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# Created by setup.py sdist
1111
build/
1212
dist/
13-
pyperformance.egg-info/
13+
*.egg-info/
1414

1515
# Created by the pyperformance script
1616
venv/

MANIFEST.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ include pyperformance/data-files/benchmarks/MANIFEST
1515
include pyperformance/data-files/benchmarks/bm_*/*.toml
1616
include pyperformance/data-files/benchmarks/bm_*/*.py
1717
include pyperformance/data-files/benchmarks/bm_*/requirements.txt
18+
include pyperformance/data-files/benchmarks/bm_*/*.c
1819
recursive-include pyperformance/data-files/benchmarks/bm_*/data *
1920
recursive-exclude pyperformance/tests *

doc/benchmarks.rst

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,16 @@ deepcopy
130130
Benchmark the Python `copy.deepcopy` method. The `deepcopy` method is
131131
performed on a nested dictionary and a dataclass.
132132

133+
ctypes
134+
------
135+
136+
Benchmark to measure the function call overhead of calling C functions using ctypes.
137+
138+
The ``ctypes`` benchmark lets ``ctypes`` infer the argument types from the passed in
139+
values. The ``ctypes_argtypes`` benchmark `explicitly specifies the argument types
140+
<https://docs.python.org/3.10/library/ctypes.html?highlight=ctypes#specifying-the-required-argument-types-function-prototypes>`_,
141+
which is slower than inferred argument types.
142+
133143
deltablue
134144
---------
135145

doc/custom_benchmarks.rst

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -324,16 +324,17 @@ All other PEP 621 fields are optional (e.g. ``requires-python = ">=3.8"``,
324324
The ``[tool.pyperformance]`` Section
325325
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
326326

327-
=============== ===== === === ===
328-
field type R B F
329-
=============== ===== === === ===
330-
tool.name str X X
331-
tool.tags [str] X
332-
tool.extra_opts [str] X
333-
tool.inherits file
334-
tool.runscript file X
335-
tool.datadir file X
336-
=============== ===== === === ===
327+
================== ===== === === ===
328+
field type R B F
329+
================== ===== === === ===
330+
tool.name str X X
331+
tool.tags [str] X
332+
tool.extra_opts [str] X
333+
tool.inherits file
334+
tool.runscript file X
335+
tool.datadir file X
336+
tool.install_setup bool
337+
================== ===== === === ===
337338

338339
"R": required
339340
"B": inferred from the inherited metadata
@@ -342,3 +343,6 @@ tool.datadir file X
342343
* tags: optional list of names to group benchmarks
343344
* extra_opts: optional list of args to pass to ``tool.runscript``
344345
* runscript: the benchmark script to use instead of run_benchmark.py.
346+
* install_setup: when ``true``, run ``pip install -e .`` in the
347+
benchmark directory to install it in the virtual environment. This has the
348+
effect of running a ``setup.py`` file, if present.

pyperformance/_benchmark.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,13 @@ def runscript(self):
164164
def extra_opts(self):
165165
return self._get_metadata_value('extra_opts', ())
166166

167+
@property
168+
def setup_py(self):
169+
if not self._get_metadata_value('install_setup', False):
170+
return None
171+
filename = os.path.join(os.path.dirname(self.metafile), 'setup.py')
172+
return filename if os.path.exists(filename) else None
173+
167174
# Other metadata keys:
168175
# * base
169176
# * python

pyperformance/_benchmark_metadata.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
'datadir': None,
3333
'runscript': None,
3434
'extra_opts': None,
35+
'install_setup': None,
3536
}
3637

3738

@@ -228,6 +229,9 @@ def _resolve_value(field, value, rootdir):
228229
for opt in value:
229230
if not opt or not isinstance(opt, str):
230231
raise TypeError(f'extra_opts should be a list of strings, got {value!r}')
232+
elif field == 'install_setup':
233+
if not isinstance(value, bool):
234+
raise TypeError(f'install_setup should be a bool, got {value!r}')
231235
else:
232236
raise NotImplementedError(field)
233237
return value

pyperformance/_pip.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def install_requirements(reqs, *extra,
150150
if upgrade:
151151
args.append('-U') # --upgrade
152152
for reqs in [reqs, *extra]:
153-
if os.path.exists(reqs):
153+
if os.path.isfile(reqs):
154154
args.append('-r') # --requirement
155155
args.append(reqs)
156156
return run_pip('install', *args, **kwargs)

pyperformance/data-files/benchmarks/MANIFEST

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ generators <local>
1111
chameleon <local>
1212
chaos <local>
1313
crypto_pyaes <local>
14+
ctypes <local>
15+
ctypes_argtypes <local:ctypes>
1416
deepcopy <local>
1517
deltablue <local>
1618
django_template <local>
@@ -69,6 +71,7 @@ xml_etree <local>
6971
#apps
7072
#math
7173
#template
74+
#extension
7275

7376

7477
[group default]
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
[project]
2+
name = "pyperformance_bm_ctypes_argtypes"
3+
requires-python = ">=3.7"
4+
dependencies = ["pyperf"]
5+
urls = {repository = "https://github.com/python/pyperformance"}
6+
dynamic = ["version"]
7+
8+
[tool.pyperformance]
9+
name = "ctypes_argtypes"
10+
tags = "extension"
11+
extra_opts = ["--argtypes"]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include <Python.h>
2+
3+
#if defined(_WIN32) || defined(__CYGWIN__)
4+
#define EXPORTED_SYMBOL __declspec(dllexport)
5+
#else
6+
#define EXPORTED_SYMBOL
7+
#endif
8+
9+
10+
EXPORTED_SYMBOL
11+
void void_foo_void(void) {
12+
13+
}
14+
15+
EXPORTED_SYMBOL
16+
int int_foo_int(int a) {
17+
return a + 1;
18+
}
19+
20+
EXPORTED_SYMBOL
21+
void void_foo_int(int a) {
22+
23+
}
24+
25+
EXPORTED_SYMBOL
26+
void void_foo_int_int(int a, int b) {
27+
28+
}
29+
30+
EXPORTED_SYMBOL
31+
void void_foo_int_int_int(int a, int b, int c) {
32+
33+
}
34+
35+
EXPORTED_SYMBOL
36+
void void_foo_int_int_int_int(int a, int b, int c, int d) {
37+
38+
}
39+
40+
EXPORTED_SYMBOL
41+
void void_foo_constchar(const char* str) {
42+
43+
}
44+
45+
PyMODINIT_FUNC
46+
PyInit_cmodule(void) {
47+
// DELIBERATELY EMPTY
48+
49+
// This isn't actually a Python extension module (it's used via ctypes), so
50+
// this entry point function will never be called. However, we are utilizing
51+
// setuptools to build it, and on Windows, setuptools explicitly passes the
52+
// flag /EXPORT:PyInit_cmodule, so it must be defined.
53+
return NULL;
54+
}

0 commit comments

Comments
 (0)