-
Notifications
You must be signed in to change notification settings - Fork 218
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use external package betterproto-rust-codec
for better (de-)serialization performance
#545
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed on slack, I think I'd prefer this to patch the parse function or conditionally define it whichever works better for you just because this current implementation will slow down the library for everyone
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks okay to me, will only be used if the optional package is installed, correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, this introduces two new try/except for every call to bytes, is that right?
I see your point. Is it better now? |
# monkey patch (de-)serialization functions of class `Message` | ||
# with functions from `betterproto-rust-codec` if available | ||
try: | ||
import betterproto_rust_codec | ||
|
||
def __parse_patch(self: T, data: bytes) -> T: | ||
betterproto_rust_codec.deserialize(self, data) | ||
return self | ||
|
||
def __bytes_patch(self) -> bytes: | ||
return betterproto_rust_codec.serialize(self) | ||
|
||
Message.parse = __parse_patch | ||
Message.__bytes__ = __bytes_patch | ||
except ModuleNotFoundError: | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd personally just do something along the lines of
HAS_ACCELERATOR = cast(Literal[False], importlib.util.find_spec("betterproto_rust_codec"))
if HAS_ACCELERATOR:
import betterproto_rust_codec
class Message:
def parse():...
def __bytes__():...
if HAS_ACCELERATOR:
parse = betterproto_rust_codec.deserialize # edit this so it returns self
__bytes__ = betterproto_rust_codec.serialize
because this reduces the number of call frames and looks significantly less hacky
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't really understand why, but directly assigning __bytes__ = betterproto_rust_codec.serialize
does not work for some reason. So editing deserialize
would probably not help either.
What do you think about this approach?
class Message:
if HAS_ACCELERATOR:
def parse(self, data):
...
else:
def parse(self, data):
...
I think this might be even more transparent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find that problematic because it's another level of indentation which is annoying especially with black. If the function isn't binding as a descriptor just leave it as is
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks LGTM!
This PR is an alternative to #520. It uses the same Rust based extension module to improve (de-)serialization performance of
betterproto
significantly. However, instead of integrating the extension into thebetterproto
project, I published it to PyPi separately, and only referenced it here as an optional dependency. For this reason, this branch is ready to use as it is.