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

Multithreaded2 #248

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open

Multithreaded2 #248

wants to merge 10 commits into from

Conversation

Pat-Lafon
Copy link

Hello! I've been occasionally interested in #180 which has been stalled for at least a year. I think I now have a little bit of time/interest to try this. I'm not super familiar with this code base nor in making code parallel so I would appreciate any feedback.

I'm starting from the same idea that #180 created except I am following the suggestion of using a runtime flag instead of a compile time feature flag to enable parallelism. Because of the non-trivial changes that have occurred on main, I have started from scratch instead of rebasing into #180... but all credit should go to @NishantJoshi00

I have done roughly the minimal amount of work to get started so I can get help addressing the three open questions as I understand them:

  • TargetDirPool was used in the previous attempt. I haven't added it yet since I'm not sure about the interactions here but it should be trivial to add if it is still useful/the right approach.
  • I have attempted to add the two previous cases that were identified in feat(multithread): add rayon & par_iter for multithreaded execution #180 as tests. These are still non-deterministically failing. As I see it there are maybe a couple of solutions: let the threads output in any order and adjust the numbering so they aren't numbered out of order, buffer the output such that the messages can be printed in a deterministic order, or something else.
  • Getting the right granularity on where all of the locks are placed. I've started conservatively in following with previous work and can go from there.

@Pat-Lafon
Copy link
Author

Oh, hmm, I see that there are ci issues because of rayon's dependencies having build scripts(outside of the two non-deterministically failing tests I had mentioned). I don't think there is much I can do there. I would hope this isn't an issue since rayon is a very popular and well-maintained but I understand if an exception cannot be made.

@taiki-e
Copy link
Owner

taiki-e commented Apr 6, 2024

Thanks for the PR!

Oh, hmm, I see that there are ci issues because of rayon's dependencies having build scripts(outside of the two non-deterministically failing tests I had mentioned). I don't think there is much I can do there. I would hope this isn't an issue since rayon is a very popular and well-maintained but I understand if an exception cannot be made.

It is not very important whether the dependencies have build scripts or not, as they are only used as a reference when auditing dependencies. The error can be suppressed by adding the dependency name to the following list:

cargo-hack/.deny.toml

Lines 22 to 25 in 74823c8

build.allow-build-scripts = [
{ name = "anyhow" },
{ name = "libc" }, # via ctrlc
{ name = "nix" }, # via ctrlc

tests/test.rs Outdated Show resolved Hide resolved
src/main.rs Outdated
) -> Result<()> {
let mut progress = progress.lock().unwrap();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this guard is held until the execution of the command is completed, so I'm concerned that this may cause the executions to not be parallel.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added some lexical scoping to limit how long these locks are held. This did involve moving the count increment because of the off chance that 2 threads would increment before either got to print their count. In general, I'm not sure how to look for more cases of this forced serialization.

@Pat-Lafon Pat-Lafon requested a review from taiki-e April 9, 2024 03:15
Copy link

@m-esposito m-esposito left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! Some small nits about the synchronization. Other than that I am looking forward for this to go in :)

src/main.rs Outdated
Comment on lines 464 to 472
} else if cx.parallel {
cx.target.par_iter().try_for_each(|target| {
let mut line = line.clone();
line.arg("--target");
line.arg(target);
packages.iter().try_for_each(|pkg| {
exec_on_package(cx, pkg.id, &pkg.kind, &line, progress, keep_going)
})
})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit: I think it might be better to nest this in a larger else clause that catches the entire !target.is_empty() case, rather than three individual cases of empty, non-empty parallel, non-empty non-parallel.

src/main.rs Outdated Show resolved Hide resolved
@Pat-Lafon
Copy link
Author

@m-esposito I rebased on main and took your suggestion for &Arc -> Arc though it seems to run afoul with clippy. Do you think it is a false positive with clippy?

The if statement still has 4 branches regardless of how I reorganize it no? I guess I don't mind but it doesn't seem like much of an improvement to me.

Do you have any suggestions for actually verifying that code is running in parallel as expected and isn't blocked in some way? I don't have much context for this code but I would imagine that is the main blocker

@m-esposito
Copy link

Hm, re the &Arc, it seems like your original method is correct. https://stackoverflow.com/questions/55576425/why-does-clippy-suggests-passing-an-arc-as-a-reference Sorry for the nit noise.

About the benchmarking, I did some preliminary testing and it seems like it's not running in parallel. Cargo invocations can't run in parallel without splitting away the target directories (noticeably when you save in an IDE w/ rust-analyzer, with check enabled on every save, then you try cargo run - it blocks until rust-analyzer's cargo invocation finishes), so TargetDirPool is necessary.

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.

3 participants