Skip to content

Commit a3f5f21

Browse files
authored
Add benchmarks (#148)
Add asv based benchmarks to guide future optimisation work.
1 parent 0028cc3 commit a3f5f21

File tree

6 files changed

+335
-92
lines changed

6 files changed

+335
-92
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,4 @@ output
1515
.DS_Store
1616
.tox
1717
.venv
18+
.asv

asv.conf.json

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
{
2+
// The version of the config file format. Do not change, unless
3+
// you know what you are doing.
4+
"version": 1,
5+
6+
// The name of the project being benchmarked
7+
"project": "python-betterproto",
8+
9+
// The project's homepage
10+
"project_url": "https://github.com/danielgtaylor/python-betterproto",
11+
12+
// The URL or local path of the source code repository for the
13+
// project being benchmarked
14+
"repo": ".",
15+
16+
// The Python project's subdirectory in your repo. If missing or
17+
// the empty string, the project is assumed to be located at the root
18+
// of the repository.
19+
// "repo_subdir": "",
20+
21+
// Customizable commands for building, installing, and
22+
// uninstalling the project. See asv.conf.json documentation.
23+
//
24+
"install_command": ["python -m pip install ."],
25+
"uninstall_command": ["return-code=any python -m pip uninstall -y {project}"],
26+
"build_command": ["python -m pip wheel -w {build_cache_dir} {build_dir}"],
27+
28+
// List of branches to benchmark. If not provided, defaults to "master"
29+
// (for git) or "default" (for mercurial).
30+
// "branches": ["master"], // for git
31+
// "branches": ["default"], // for mercurial
32+
33+
// The DVCS being used. If not set, it will be automatically
34+
// determined from "repo" by looking at the protocol in the URL
35+
// (if remote), or by looking for special directories, such as
36+
// ".git" (if local).
37+
// "dvcs": "git",
38+
39+
// The tool to use to create environments. May be "conda",
40+
// "virtualenv" or other value depending on the plugins in use.
41+
// If missing or the empty string, the tool will be automatically
42+
// determined by looking for tools on the PATH environment
43+
// variable.
44+
"environment_type": "virtualenv",
45+
46+
// timeout in seconds for installing any dependencies in environment
47+
// defaults to 10 min
48+
//"install_timeout": 600,
49+
50+
// the base URL to show a commit for the project.
51+
// "show_commit_url": "http://github.com/owner/project/commit/",
52+
53+
// The Pythons you'd like to test against. If not provided, defaults
54+
// to the current version of Python used to run `asv`.
55+
// "pythons": ["2.7", "3.6"],
56+
57+
// The list of conda channel names to be searched for benchmark
58+
// dependency packages in the specified order
59+
// "conda_channels": ["conda-forge", "defaults"],
60+
61+
// The matrix of dependencies to test. Each key is the name of a
62+
// package (in PyPI) and the values are version numbers. An empty
63+
// list or empty string indicates to just test against the default
64+
// (latest) version. null indicates that the package is to not be
65+
// installed. If the package to be tested is only available from
66+
// PyPi, and the 'environment_type' is conda, then you can preface
67+
// the package name by 'pip+', and the package will be installed via
68+
// pip (with all the conda available packages installed first,
69+
// followed by the pip installed packages).
70+
//
71+
// "matrix": {
72+
// "numpy": ["1.6", "1.7"],
73+
// "six": ["", null], // test with and without six installed
74+
// "pip+emcee": [""], // emcee is only available for install with pip.
75+
// },
76+
77+
// Combinations of libraries/python versions can be excluded/included
78+
// from the set to test. Each entry is a dictionary containing additional
79+
// key-value pairs to include/exclude.
80+
//
81+
// An exclude entry excludes entries where all values match. The
82+
// values are regexps that should match the whole string.
83+
//
84+
// An include entry adds an environment. Only the packages listed
85+
// are installed. The 'python' key is required. The exclude rules
86+
// do not apply to includes.
87+
//
88+
// In addition to package names, the following keys are available:
89+
//
90+
// - python
91+
// Python version, as in the *pythons* variable above.
92+
// - environment_type
93+
// Environment type, as above.
94+
// - sys_platform
95+
// Platform, as in sys.platform. Possible values for the common
96+
// cases: 'linux2', 'win32', 'cygwin', 'darwin'.
97+
//
98+
// "exclude": [
99+
// {"python": "3.2", "sys_platform": "win32"}, // skip py3.2 on windows
100+
// {"environment_type": "conda", "six": null}, // don't run without six on conda
101+
// ],
102+
//
103+
// "include": [
104+
// // additional env for python2.7
105+
// {"python": "2.7", "numpy": "1.8"},
106+
// // additional env if run on windows+conda
107+
// {"platform": "win32", "environment_type": "conda", "python": "2.7", "libpython": ""},
108+
// ],
109+
110+
// The directory (relative to the current directory) that benchmarks are
111+
// stored in. If not provided, defaults to "benchmarks"
112+
// "benchmark_dir": "benchmarks",
113+
114+
// The directory (relative to the current directory) to cache the Python
115+
// environments in. If not provided, defaults to "env"
116+
"env_dir": ".asv/env",
117+
118+
// The directory (relative to the current directory) that raw benchmark
119+
// results are stored in. If not provided, defaults to "results".
120+
"results_dir": ".asv/results",
121+
122+
// The directory (relative to the current directory) that the html tree
123+
// should be written to. If not provided, defaults to "html".
124+
"html_dir": ".asv/html",
125+
126+
// The number of characters to retain in the commit hashes.
127+
// "hash_length": 8,
128+
129+
// `asv` will cache results of the recent builds in each
130+
// environment, making them faster to install next time. This is
131+
// the number of builds to keep, per environment.
132+
// "build_cache_size": 2,
133+
134+
// The commits after which the regression search in `asv publish`
135+
// should start looking for regressions. Dictionary whose keys are
136+
// regexps matching to benchmark names, and values corresponding to
137+
// the commit (exclusive) after which to start looking for
138+
// regressions. The default is to start from the first commit
139+
// with results. If the commit is `null`, regression detection is
140+
// skipped for the matching benchmark.
141+
//
142+
// "regressions_first_commits": {
143+
// "some_benchmark": "352cdf", // Consider regressions only after this commit
144+
// "another_benchmark": null, // Skip regression detection altogether
145+
// },
146+
147+
// The thresholds for relative change in results, after which `asv
148+
// publish` starts reporting regressions. Dictionary of the same
149+
// form as in ``regressions_first_commits``, with values
150+
// indicating the thresholds. If multiple entries match, the
151+
// maximum is taken. If no entry matches, the default is 5%.
152+
//
153+
// "regressions_thresholds": {
154+
// "some_benchmark": 0.01, // Threshold of 1%
155+
// "another_benchmark": 0.5, // Threshold of 50%
156+
// },
157+
}

benchmarks/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

benchmarks/benchmarks.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import betterproto
2+
from dataclasses import dataclass
3+
4+
5+
@dataclass
6+
class TestMessage(betterproto.Message):
7+
foo: int = betterproto.uint32_field(0)
8+
bar: str = betterproto.string_field(1)
9+
baz: float = betterproto.float_field(2)
10+
11+
class BenchMessage:
12+
"""Test creation and usage a proto message.
13+
"""
14+
15+
def setup(self):
16+
self.cls = TestMessage
17+
self.instance = TestMessage()
18+
self.instance_filled = TestMessage(0, "test", 0.0)
19+
20+
def time_overhead(self):
21+
"""Overhead in class definition.
22+
"""
23+
@dataclass
24+
class Message(betterproto.Message):
25+
foo: int = betterproto.uint32_field(0)
26+
bar: str = betterproto.string_field(1)
27+
baz: float = betterproto.float_field(2)
28+
29+
def time_instantiation(self):
30+
"""Time instantiation
31+
"""
32+
self.cls()
33+
34+
def time_attribute_access(self):
35+
"""Time to access an attribute
36+
"""
37+
self.instance.foo
38+
self.instance.bar
39+
self.instance.baz
40+
41+
def time_init_with_values(self):
42+
"""Time to set an attribute
43+
"""
44+
self.cls(0, "test", 0.0)
45+
46+
def time_attribute_setting(self):
47+
"""Time to set attributes
48+
"""
49+
self.instance.foo = 0
50+
self.instance.bar = "test"
51+
self.instance.baz = 0.0
52+
53+
def time_serialize(self):
54+
"""Time serializing a message to wire."""
55+
bytes(self.instance_filled)
56+
57+
58+
class MemSuite:
59+
def setup(self):
60+
self.cls = TestMessage
61+
62+
def mem_instance(self):
63+
return self.cls()

0 commit comments

Comments
 (0)