Skip to content
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

⚡️ zv,zn,zb,zd,zm: Add zvariant::parsed::Signature & use that in ser/de #966

Merged
merged 22 commits into from
Sep 2, 2024

Conversation

zeenix
Copy link
Contributor

@zeenix zeenix commented Sep 1, 2024

The commits in the PR are quite self-explanatory but the main benefit coming from this PR, is massive efficiency gains in ser/de of data. Benchmarks reveal that on average we gain about 50% improvement in performance. The fixed-array ser benchmark can now be up to 94% faster on some machines. On my (rather high-end) machine, the benchmark results are as follows:

❯ cargo +nightly bench --features gvariant
     Running benches/benchmarks.rs (/home/zeenix/checkout/dbus2/zbus/target/release/deps/benchmarks-1291df1070d36afd)
dbus/big_array_ser      time:   [171.66 µs 172.22 µs 172.75 µs]
                        change: [-51.493% -50.998% -50.505%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 17 outliers among 100 measurements (17.00%)
  7 (7.00%) low severe
  1 (1.00%) low mild
  5 (5.00%) high mild
  4 (4.00%) high severe
dbus/big_array_de       time:   [268.10 µs 268.18 µs 268.28 µs]
                        change: [-58.217% -57.863% -57.492%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 18 outliers among 100 measurements (18.00%)
  4 (4.00%) low severe
  3 (3.00%) low mild
  1 (1.00%) high mild
  10 (10.00%) high severe

gvariant/big_array_ser  time:   [177.43 µs 177.48 µs 177.56 µs]
                        change: [-57.919% -57.541% -57.181%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 11 outliers among 100 measurements (11.00%)
  1 (1.00%) low mild
  2 (2.00%) high mild
  8 (8.00%) high severe
gvariant/big_array_de   time:   [397.70 µs 398.11 µs 398.55 µs]
                        change: [-46.473% -45.993% -45.551%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) high mild
  2 (2.00%) high severe

dbus/big_array_and_ass_dict_ser
                        time:   [494.11 µs 494.43 µs 494.83 µs]
                        change: [-47.351% -46.771% -46.245%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 4 outliers among 100 measurements (4.00%)
  2 (2.00%) high mild
  2 (2.00%) high severe
dbus/big_array_and_ass_dict_de
                        time:   [1.3815 ms 1.3835 ms 1.3854 ms]
                        change: [-36.342% -35.625% -34.790%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 32 outliers among 100 measurements (32.00%)
  19 (19.00%) low severe
  3 (3.00%) low mild
  3 (3.00%) high mild
  7 (7.00%) high severe

gvariant/big_array_and_ass_dict_ser
                        time:   [481.69 µs 481.88 µs 482.10 µs]
                        change: [-54.601% -54.358% -54.007%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 14 outliers among 100 measurements (14.00%)
  3 (3.00%) low mild
  7 (7.00%) high mild
  4 (4.00%) high severe
gvariant/big_array_and_ass_dict_de
                        time:   [1.5528 ms 1.5539 ms 1.5551 ms]
                        change: [-32.072% -31.506% -30.860%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 3 outliers among 100 measurements (3.00%)
  2 (2.00%) high mild
  1 (1.00%) high severe

dbus/big_array_and_asv_dict_ser
                        time:   [1.4085 ms 1.4119 ms 1.4151 ms]
                        change: [-3.5727% -2.4103% -1.2595%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) low mild
  1 (1.00%) high severe
dbus/big_array_and_asv_dict_de
                        time:   [4.4548 ms 4.4626 ms 4.4747 ms]
                        change: [-19.025% -17.857% -16.720%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 16 outliers among 100 measurements (16.00%)
  2 (2.00%) high mild
  14 (14.00%) high severe

gvariant/big_array_and_asv_dict_ser
                        time:   [1.4278 ms 1.4316 ms 1.4350 ms]
                        change: [-29.324% -28.448% -27.545%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 12 outliers among 100 measurements (12.00%)
  4 (4.00%) low severe
  1 (1.00%) low mild
  3 (3.00%) high mild
  4 (4.00%) high severe
gvariant/big_array_and_asv_dict_de
                        time:   [3.1782 ms 3.1846 ms 3.1898 ms]
                        change: [-44.570% -43.678% -42.646%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 2 outliers among 100 measurements (2.00%)
  1 (1.00%) high mild
  1 (1.00%) high severe

dbus/fixed_size_array_ser
                        time:   [86.514 µs 86.561 µs 86.618 µs]
                        change: [-91.815% -91.740% -91.665%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 13 outliers among 100 measurements (13.00%)
  7 (7.00%) high mild
  6 (6.00%) high severe
dbus/fixed_size_array_de
                        time:   [819.56 µs 819.82 µs 820.14 µs]
                        change: [-57.296% -56.997% -56.677%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 17 outliers among 100 measurements (17.00%)
  1 (1.00%) low mild
  5 (5.00%) high mild
  11 (11.00%) high severe

gvariant/fixed_size_array_ser
                        time:   [159.54 µs 159.58 µs 159.64 µs]
                        change: [-92.033% -91.960% -91.887%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 19 outliers among 100 measurements (19.00%)
  1 (1.00%) high mild
  18 (18.00%) high severe
gvariant/fixed_size_array_de
                        time:   [1.1735 ms 1.1739 ms 1.1745 ms]
                        change: [-68.283% -68.044% -67.761%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 20 outliers among 100 measurements (20.00%)
  2 (2.00%) high mild
  18 (18.00%) high severe

This PR addresses most of #882. Completely addressing it would require breaking API so it has to wait for the next major release.

Let's move the main and mandatory dependencies to the top of the file.
We'll be using this for our new signal parsing code.
This is a generic variant for invalid signatures. It will be used in a
following commit when parsing the new signature type.
The internal ser/de structs don't need to implement `Send`, `Sync` or
`Unpin`.
@zeenix zeenix mentioned this pull request Sep 1, 2024
This is a new type that represents a parsed signature. We'll use this to
avoid parsing the signature as part of (de)serialization, which seems to
be quite expensive at times, especially for large arrays.

While the the parsed::Signature can be constructed w/o any allocation
and in const context even, unfortunately converting from and to
`Signature` or the underlying string form, is not free and will
require allocations for non-basic types.

However, we'll be changing `Type` trait to provide us a
`parsed::Signature` as a constant, allowing us to avoid allocations and
parsing.
* Th Serializer impls now use parsed::Signature.
* We add new serialization functions that that take a parsed::Signature.
* We keep backwards-compatibility by keeping the existing serialization
  functions that translate Signature to parsed::Signature. This is not
  going to be as efficient as using the parsed::Signature from the
  beginning but we'll be moving towards using the parsed::Signature in
  the following commits anyway.

Benchmarks show that this change can result is big performance gains,
ranging from 5% to 94%, depending on the type being encoded and the host
system resources available (and other environmental factors).
@zeenix zeenix force-pushed the parsed-signature branch 2 times, most recently from 0105b80 to 03a25db Compare September 1, 2024 21:17
* Th Deserializer impls now use parsed::Signature.
* We add new serialized::Data::deserialize_for_parsed_signature method.
* We keep backwards-compatibility by keeping the existing
  serialized::Data::deserialize method that translate Signature to
  parsed::Signature. This is not going to be as efficient as using the
  parsed::Signature from the beginning but we'll be moving towards using
  the parsed::Signature in the following commits anyway.
This already shows off the performance gains, proving that a parsed
signature is really the way to go. In the following commits, we'll
also update the Type to give us parsed signature so we can have
ser/de optimized e2e.
Add parsed signature consts to `Basic`. This doesn't break the API since
we can determine the signature in const context, using
`Basic::SIGNATURE_CHAR`.
Add a method to `Type` trait that returns the parsed form of the
signature. This method has a default implementation to not break the API.
By default, it parses the return value of the `signature()` method. Since
that is not efficient at all, we encourage implementors to implement this
method (instead of `signature()`). Moreover, for keeping type
implementation easy, we also now provide a default implementation of
`signature`, which converts the parsed signature back to a string form.

This does indeed create a mutual recursion between the default
implementations of `signature` and `parsed_signature`, but I believe this
is a good trade-off for the sake of simplicity and ease of implementation.
All current manul implementations of `Type` out there are guaranteed to
have an implementation of `signature` method, so this will only affect new
implementations. Hopefully they will read the docs when they implement the
trait. If not, they'll quickly run into a stack overflow, which will force
them to read the docs. :)

This commit also replaces most implementations of `signature` with
`parsed_signature`. Only manual implementations of `Type` outside this
repository will want to (not need to) implement `parsed_signature` if they
want encoding/decoding to be super efficient efficient.

I originally wrote this as a const (as it should be) but that will need to
have a default value to not break the API and from a consts context, we
can't call `signature`, even if we could parse its return value. This will
be an easy fix to add in the next major version.
This is analogous to the `Type::parsed_signature` method, but for dynamic types.
This is just a natural follow-up to the addition of
`DynamicDeserialize::dynamic_parsed_signature` method.
A variant of `deserialize_for_dynamic_signature` that takes a parsed
signature reference.
Instead of first getting the signature and then calling specific ser/de
API that takes a signature.
Instead make use of the new `parsed::Signature` API.

This involves breaking the API a bit since now both `ArraySeed` and
`StructureSeed` do not implement `Sync` and `Send` now. However the
chances of anyong using them directly are already extremely low and even
if they'd use them directly, the changes of them needing `Sync` and
`Send` are even lower. So I think we'll be ok.
It's no longer used or needed. So long!
Otherwise, it can enable Maybe singature support, which will fail with an
assertion when used with dbus ser/de.
@zeenix zeenix merged commit 3f19f40 into dbus2:main Sep 2, 2024
7 checks passed
@zeenix zeenix deleted the parsed-signature branch September 2, 2024 10:45
zeenix added a commit that referenced this pull request Sep 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant