Skip to content

Commit d34b169

Browse files
authored
Use external package betterproto-rust-codec for better (de-)serialization performance (#545)
* optionally use betterproto-rust-codec * monkey patch `parse` and `__bytes__`
1 parent bd7de20 commit d34b169

File tree

4 files changed

+62
-33
lines changed

4 files changed

+62
-33
lines changed

benchmarks/benchmarks.py

Lines changed: 21 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,32 @@
66

77
@dataclass
88
class TestMessage(betterproto.Message):
9-
foo: int = betterproto.uint32_field(0)
10-
bar: str = betterproto.string_field(1)
11-
baz: float = betterproto.float_field(2)
9+
foo: int = betterproto.uint32_field(1)
10+
bar: str = betterproto.string_field(2)
11+
baz: float = betterproto.float_field(3)
1212

1313

1414
@dataclass
1515
class TestNestedChildMessage(betterproto.Message):
16-
str_key: str = betterproto.string_field(0)
17-
bytes_key: bytes = betterproto.bytes_field(1)
18-
bool_key: bool = betterproto.bool_field(2)
19-
float_key: float = betterproto.float_field(3)
20-
int_key: int = betterproto.uint64_field(4)
16+
str_key: str = betterproto.string_field(1)
17+
bytes_key: bytes = betterproto.bytes_field(2)
18+
bool_key: bool = betterproto.bool_field(3)
19+
float_key: float = betterproto.float_field(4)
20+
int_key: int = betterproto.uint64_field(5)
2121

2222

2323
@dataclass
2424
class TestNestedMessage(betterproto.Message):
25-
foo: TestNestedChildMessage = betterproto.message_field(0)
26-
bar: TestNestedChildMessage = betterproto.message_field(1)
27-
baz: TestNestedChildMessage = betterproto.message_field(2)
25+
foo: TestNestedChildMessage = betterproto.message_field(1)
26+
bar: TestNestedChildMessage = betterproto.message_field(2)
27+
baz: TestNestedChildMessage = betterproto.message_field(3)
2828

2929

3030
@dataclass
3131
class TestRepeatedMessage(betterproto.Message):
32-
foo_repeat: List[str] = betterproto.string_field(0)
33-
bar_repeat: List[int] = betterproto.int64_field(1)
34-
baz_repeat: List[bool] = betterproto.bool_field(2)
32+
foo_repeat: List[str] = betterproto.string_field(1)
33+
bar_repeat: List[int] = betterproto.int64_field(2)
34+
baz_repeat: List[bool] = betterproto.bool_field(3)
3535

3636

3737
class BenchMessage:
@@ -44,25 +44,14 @@ def setup(self):
4444
self.instance_filled_bytes = bytes(self.instance_filled)
4545
self.instance_filled_nested = TestNestedMessage(
4646
TestNestedChildMessage("foo", bytearray(b"test1"), True, 0.1234, 500),
47-
TestNestedChildMessage("bar", bytearray(b"test2"), True, 3.1415, -302),
47+
TestNestedChildMessage("bar", bytearray(b"test2"), True, 3.1415, 302),
4848
TestNestedChildMessage("baz", bytearray(b"test3"), False, 1e5, 300),
4949
)
5050
self.instance_filled_nested_bytes = bytes(self.instance_filled_nested)
5151
self.instance_filled_repeated = TestRepeatedMessage(
52-
[
53-
"test1",
54-
"test2",
55-
"test3",
56-
"test4",
57-
"test5",
58-
"test6",
59-
"test7",
60-
"test8",
61-
"test9",
62-
"test10",
63-
],
64-
[2, -100, 0, 500000, 600, -425678, 1000000000, -300, 1, -694214214466],
65-
[True, False, False, False, True, True, False, True, False, False],
52+
[f"test{i}" for i in range(1_000)],
53+
[(i - 500) ** 3 for i in range(1_000)],
54+
[i % 2 == 0 for i in range(1_000)],
6655
)
6756
self.instance_filled_repeated_bytes = bytes(self.instance_filled_repeated)
6857

@@ -71,9 +60,9 @@ def time_overhead(self):
7160

7261
@dataclass
7362
class Message(betterproto.Message):
74-
foo: int = betterproto.uint32_field(0)
75-
bar: str = betterproto.string_field(1)
76-
baz: float = betterproto.float_field(2)
63+
foo: int = betterproto.uint32_field(1)
64+
bar: str = betterproto.string_field(2)
65+
baz: float = betterproto.float_field(3)
7766

7867
def time_instantiation(self):
7968
"""Time instantiation"""

poetry.lock

Lines changed: 22 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ jinja2 = { version = ">=3.0.3", optional = true }
2020
python-dateutil = "^2.8"
2121
isort = {version = "^5.11.5", optional = true}
2222
typing-extensions = "^4.7.1"
23+
betterproto-rust-codec = { version = "0.1.0", optional = true }
2324

2425
[tool.poetry.group.dev.dependencies]
2526
asv = "^0.4.2"
@@ -48,6 +49,7 @@ protoc-gen-python_betterproto = "betterproto.plugin:main"
4849

4950
[tool.poetry.extras]
5051
compiler = ["black", "isort", "jinja2"]
52+
rust-codec = ["betterproto-rust-codec"]
5153

5254

5355
# Dev workflow tasks

src/betterproto/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1868,6 +1868,23 @@ def _validate_field_groups(cls, values):
18681868

18691869
Message.__annotations__ = {} # HACK to avoid typing.get_type_hints breaking :)
18701870

1871+
# monkey patch (de-)serialization functions of class `Message`
1872+
# with functions from `betterproto-rust-codec` if available
1873+
try:
1874+
import betterproto_rust_codec
1875+
1876+
def __parse_patch(self: T, data: bytes) -> T:
1877+
betterproto_rust_codec.deserialize(self, data)
1878+
return self
1879+
1880+
def __bytes_patch(self) -> bytes:
1881+
return betterproto_rust_codec.serialize(self)
1882+
1883+
Message.parse = __parse_patch
1884+
Message.__bytes__ = __bytes_patch
1885+
except ModuleNotFoundError:
1886+
pass
1887+
18711888

18721889
def serialized_on_wire(message: Message) -> bool:
18731890
"""

0 commit comments

Comments
 (0)