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

Offload PVF code decompression to a separate task on the blocking pool #5071

Closed
s0me0ne-unkn0wn opened this issue Jul 19, 2024 · 9 comments · Fixed by #5142
Closed

Offload PVF code decompression to a separate task on the blocking pool #5071

s0me0ne-unkn0wn opened this issue Jul 19, 2024 · 9 comments · Fixed by #5142
Assignees

Comments

@s0me0ne-unkn0wn
Copy link
Contributor

s0me0ne-unkn0wn commented Jul 19, 2024

In #3122, we moved the whole candidate validation subsystem to the blocking pool, as it performs PVF code decompression, which is a blocking task, although a small one. That wasn't the perfect solution, but in the context of problem space, as it looked like at that point, that was acceptable. Now, with agile coretime and #5012 around the corner, we must be ready to prepare a lot more PVFs than before, and the problem with blocking the candidate validation with decompressions is much more concerning.

We should offload that work to a separate task to allow candidate validation subsys to await on it asynchronously.

@s0me0ne-unkn0wn
Copy link
Contributor Author

@sandreim @alexggh @alindima I need a bit of advice here.

The idiomatic approach to run a lot of CPU-bound tasks is to do that on rayon threads (event tokio docs tell to use rayon in this case). But considering how small every task is and that we're unlikely to spawn 100s of them at the same time, I'm more inclined just to use tokio::spawn_blocking for them. WDYT, do you have some insights?

@bkchr
Copy link
Member

bkchr commented Jul 19, 2024

We have the Spawner that provides a spawn_blocking. I don't see any reason why you should use rayon here.

@sandreim
Copy link
Contributor

As @bkchr says, just spawn a blocking task. I did the same in availability recovery, you can use it as an example.

@s0me0ne-unkn0wn
Copy link
Contributor Author

Okay, I went through the code once again, and it doesn't make much sense to me 🫠
Candidate validation subsys decompresses the PVF and the PoV just to observe their sizes in metrics. It doesn't use them in any other way and passes the decompressed blobs to the PVF host, which passes them to workers that are synchronous by their nature and are a totally natural place to do the decompression job.

To my mind, it should be refactored. The PVF decompression should be performed by the preparation worker, and the PoV decompression should go to the execution worker. The PVF host should return observable decompressed sizes to the candidate validation subsys (at the end of the day, it doesn't matter when we observe the sizes, before the validation or after it).

As I see it, the only drawback of this approach is that decompression times will be included in the preparation and execution timeouts. But they should be negligible anyway? Do we maybe have benchmarks of how fast the zstd decompression is on the reference hardware?

Any objections to this approach?

@sandreim
Copy link
Contributor

sandreim commented Jul 22, 2024

Okay, I went through the code once again, and it doesn't make much sense to me 🫠 Candidate validation subsys decompresses the PVF and the PoV just to observe their sizes in metrics. It doesn't use them in any other way and passes the decompressed blobs to the PVF host, which passes them to workers that are synchronous by their nature and are a totally natural place to do the decompression job.

Not sure why you think it is not used, the code shows that the decompressed blob is being passed in ValidationParams.

@s0me0ne-unkn0wn
Copy link
Contributor Author

the code shows that the decompressed blob is being passed in ValidationParams

Exactly, but the ValidationParams themselves are not used for anything other than passing them to the PVF execution worker to provide a PVF with parameters. Instead, we could pass persisted validation data and compressed PoV there and let the execution worker decompress PoV and construct ValidationParams itself.

My point is that everything that is currently done with the compressed data in the candidate validation subsys may be done in PVF preparation/execution workers as well, and nothing will change. Candidate validation subsys doesn't need that data decompressed for itself, it decompresses them for the PVF host.

@sandreim
Copy link
Contributor

sandreim commented Jul 22, 2024

I see, this is a nice micro optimization. The impact of it depends on the number of pending validation requests in the queue and how long decompression takes. I think next step should be to measure and see if decompression significantly stalling the candidate validation loop.

@AndreiEres
Copy link
Contributor

After we merged #4791, we decompress the same PVF one more time when we prepare the node for the active set, which looks unoptimal. This can be solved with a slight refactoring of the message arguments. But does that mean we no longer need the subsystem in the blocking pool?

@s0me0ne-unkn0wn
Copy link
Contributor Author

I'm currently working on a PR that moves all the decompression to PVF host workers, stay tuned :)

github-merge-queue bot pushed a commit that referenced this issue Aug 9, 2024
Closes #5071 

This PR aims to
* Move all the blocking decompression from the candidate validation
subsystem to the PVF host workers;
* Run the candidate validation subsystem on the non-blocking pool again.

Upsides: no blocking operations in the subsystem's main loop. PVF
throughput is not limited by the ability of the subsystem to decompress
a lot of stuff. Correctness and homogeneity improve, as the artifact
used to be identified by the hash of decompressed code, and now they are
identified by the hash of compressed code, which coincides with the
on-chain `ValidationCodeHash`.

Downsides: the PVF code decompression is now accounted for in the PVF
preparation timeout (be it pre-checking or actual preparation). Taking
into account that the decompression duration is on the order of
milliseconds, and the preparation timeout is on the order of seconds, I
believe it is negligible.
dharjeezy pushed a commit to dharjeezy/polkadot-sdk that referenced this issue Aug 28, 2024
)

Closes paritytech#5071 

This PR aims to
* Move all the blocking decompression from the candidate validation
subsystem to the PVF host workers;
* Run the candidate validation subsystem on the non-blocking pool again.

Upsides: no blocking operations in the subsystem's main loop. PVF
throughput is not limited by the ability of the subsystem to decompress
a lot of stuff. Correctness and homogeneity improve, as the artifact
used to be identified by the hash of decompressed code, and now they are
identified by the hash of compressed code, which coincides with the
on-chain `ValidationCodeHash`.

Downsides: the PVF code decompression is now accounted for in the PVF
preparation timeout (be it pre-checking or actual preparation). Taking
into account that the decompression duration is on the order of
milliseconds, and the preparation timeout is on the order of seconds, I
believe it is negligible.
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 a pull request may close this issue.

4 participants