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

Optimize Build::compile_objects: Only spawns one thread #780

Merged
merged 1 commit into from
Jul 10, 2023
Merged

Optimize Build::compile_objects: Only spawns one thread #780

merged 1 commit into from
Jul 10, 2023

Conversation

NobodyXu
Copy link
Collaborator

This PR optimizes Build::compile_objects so that it only spawns one thread for printing warnings for both cfg(features = "parallel") and cfg(not(features = "parallel")).

This PR achieves this by using os_pipe to create a pair of pipe fds and use that through out the entire Build::compile_objects for Command::stderr.

This eliminates the thread spawns for each process just to forward warnings.

The second optimization is specific to cfg(features = "parallel"), which previously spawns one thread per process creation.

I replaced that by simply adopting Command::spawn and collects the Command, program_name, process::Child and the jobserver::Acquired token into a Vec, then iterate over them.

This removes all the unsafe code in there while also significantly improves performance, especially when the users has a lot of source files but each don't take long to compile (sounds like common cases for me).

Signed-off-by: Jiahao XU Jiahao_XU@outlook.com

@@ -19,6 +19,7 @@ edition = "2018"

[dependencies]
jobserver = { version = "0.1.16", optional = true }
os_pipe = "1"
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The msrv CI for windows failed because it automatically pulls in the latest version of os_pipe v1.1.2, which depends on windows-sys v0.42.

windows-sys v0.42 seems to use if, match, || and && in const, hence failed in rust v1.34.

However, we only need os_pipe v1.0.0 to work here, I've tested this with cargo update --Zminimal-version and all the tests ran just fine.

So I think this does not break the msrv given that the users manage the Cargo.lock to not use the latest version of os_pipe, but not entirely sure about that.

Copy link
Member

Choose a reason for hiding this comment

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

I agree this doesn't practically update the MSRV. I think a reasonable solution would be to run cargo update -p os_pipe --precise 1.0.0 (possibly after cargo generate-lockfile if needed) in the CI script.

That said, do you know what version these features require? I don't mind bumping it a bit.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That said, do you know what version these features require? I don't mind bumping it a bit.

I looked into the tracking issue rust-lang/rust#57563 and it seems to be the first item in the list rust-lang/rust#49146 , which is stablised in 1.46.

P.S. If we are bumping msrv to 1.46, then I can replace the Option used in fn jobserver() with mem::MaybeUninit.

Copy link
Member

@thomcc thomcc Jan 31, 2023

Choose a reason for hiding this comment

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

1.46 should be fine. I'll ask if anybody objects on Zulip (https://rust-lang.zulipchat.com/#narrow/stream/351149-t-libs.2Fcrates/topic/Any.20objections.20to.20bumping.20.60cc.60's.20MSRV), but my main goal was to avoid going past Debian stable (which is on 1.48), since it tends to cause a lot of spurious bug reports. Even then, those would probably be directed at the crate that causes the compilation error (in this case windows-sys), rather than us.

That said, I'm slightly unsure about adding the dependency on os_pipe, since it's not nearly as widely used as cc, and is pretty lightweight (so it might be better to implement manually). That said, it might be fine.

Copy link
Collaborator Author

@NobodyXu NobodyXu Jan 31, 2023

Choose a reason for hiding this comment

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

(so it might be better to implement manually).

That's definitely doable, I checked their source code and it is really simple, though I personally don't want more unsafe code in cc.

Copy link
Member

Choose a reason for hiding this comment

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

Sure, although I think that might be easier unsafe to audit than the current code.

Copy link
Member

Choose a reason for hiding this comment

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

It's also notable that we don't currently have any non-optional dependencies, and even among optional dependencies we don't have any that aren't maintained by rust-lang team members (this would change that). So I'm not sure, it might be worth being a bit conservative here.

I'm largely inclined to think windows-sys and libc are fine, though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So I'm not sure, it might be worth being a bit conservative here.

Hmmm, maybe a good idea to move os_pipe into rust-lang considering that it is also a fundamental lib used by other crates?

CC @oconnor663 owner of os_pipe.

Copy link
Member

Choose a reason for hiding this comment

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

In any case I still need to review this code and use of the dependency (sadly, it's looking like I might not get to this until the weekend, not sure).

Choose a reason for hiding this comment

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

Happy to help if I can. Luckily the whole crate isn't very much code :)

src/lib.rs Show resolved Hide resolved
@thomcc
Copy link
Member

thomcc commented Jan 30, 2023

Thanks, this looks great. I'll review more thoroughly later this week, but removing the unsafe alone (without the optimizations) would be enough for me to be very excited about this.

@thomcc thomcc mentioned this pull request Jan 31, 2023
@NobodyXu
Copy link
Collaborator Author

@thomcc Friendly ping just in case you forgot this PR

@thomcc
Copy link
Member

thomcc commented Feb 27, 2023

Ah, sorry, I've been very busy and sadly been neglecting this more than I should. Give me a week or so, I should have some time after that.

@NobodyXu
Copy link
Collaborator Author

NobodyXu commented Apr 1, 2023

@thomcc Pinging as this has been stale for really long time.

@NobodyXu
Copy link
Collaborator Author

NobodyXu commented May 5, 2023

@thomcc Pinging since this has been stale for another 2 months.

@NobodyXu
Copy link
Collaborator Author

@JohnTitor Can you review this please if you have time?
It seems that @thomcc is too busy to review this PR and it has been stale for really long time.

This PR isn't very complex and it should not take you too much time to review it.

 - Refactor: Extract new fn `Build::compile_object_inner`
 - Optimize away thread spawning in `Build::compile_objects`
   Instead of spawning a new thread just to manage the compilation task, we
   instead spawn the child and put it into a Vec and manage it in the
   current thread.
 - Add new optional dep os_pipe v1
 - Optimize `Build::compile_objects`: Only spawn one `print` thread
   instead of one per `Child`.
 - Rm unused explicit lifetime in `Build::compile_objects`
 - Optimize `Build::compile_objects`: Do not wait on `child` in `KillOnDrop::drop`
 - Refactor: Create newtype `PrintThread`
 - Refactor `spawn`/`run`/`run_output` to use `PrintThread`
   and extract new fn `run_inner`.
 - Refactor `Build::compile_objects` to use `spawn` when feat `parallel` is enabled
 - Fix `spawn`: Ensure stderr is reset on panic
 - Refactor: Extract new fn `wait_on_child`
 - Rename `Build::compile_object_inner` => `create_compile_object_cmd`

Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
@NobodyXu
Copy link
Collaborator Author

@thomcc Please review this if you have time, it has been stale for really long time.

}
let child = &mut self.0;

child.kill().ok();
Copy link
Member

Choose a reason for hiding this comment

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

Nit, but I'm not a fan of using .ok() as an idiom to ignore the return value here. I'd suggest using let _ = and a comment.

(Or, possibly, calling .expect() since it's probably worth killing the process if this fails. But that'd be a semantic change from the previous code, so if done at all that should be a separate PR.)

@joshtriplett
Copy link
Member

I love this PR. Thank you!

I reviewed the code, and both the intent and implementation seems reasonable to me. Given that, and that it passes CI, I'm going to go ahead and merge it.

@joshtriplett joshtriplett merged commit 17c8858 into rust-lang:main Jul 10, 2023
16 checks passed
@NobodyXu NobodyXu deleted the optimize branch July 10, 2023 03:00
@thomcc
Copy link
Member

thomcc commented Jul 10, 2023

From zulip:

We should not have a required dependency on a crate that needs OS implementations (and assumes every non-windows OS is unix https://github.com/oconnor663/os_pipe.rs/blob/master/src/lib.rs#L283-L287C1). I think this should probably be:

  1. Behind #[cfg(any(unix, windows))].
  2. Ideally optional (the common usage of this crate is with warnings disabled, for example)

We should fix this before cutting a release. I'll try to get to it in the next couple days.

@apoelstra
Copy link

This PR seems to be causing build problems with rust-secp256k1. The error message looks like:

   Compiling no_std_test v0.1.0 (/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test)
     Running `/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc --crate-name no_std_test src/main.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=123 --crate-type bin --emit=dep-info,link -C opt-level=3 -C panic=abort -C embed-bitcode=no -C metadata=1293a4e6b7efff78 -C extra-filename=-1293a4e6b7efff78 --out-dir /store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps -L dependency=/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps --extern libc=/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/liblibc-5de6dfcb24ee9f89.rlib --extern secp256k1=/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libsecp256k1-5c90214252d1cafe.rlib --extern serde_cbor=/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libserde_cbor-b92effaf107035cb.rlib -L native=/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/build/secp256k1-sys-cfb84e400e95762c/out`
error: linking with `cc` failed: exit status: 1
  |
  = note: LC_ALL="C" PATH="/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/store/home/apoelstra/.cargo/bin:/store/home/apoelstra/bin:/store/home/apoelstra/.idris2/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/store/home/apoelstra/.local/bin:/store/home/apoelstra/.cabal/bin" VSLANG="1033" "cc" "-m64" "/tmp/rustchhL2Pf/symbols.o" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/no_std_test-1293a4e6b7efff78.no_std_test.87cf14f7b6e6638a-cgu.0.rcgu.o" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/no_std_test-1293a4e6b7efff78.no_std_test.87cf14f7b6e6638a-cgu.1.rcgu.o" "-Wl,--as-needed" "-L" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps" "-L" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/build/secp256k1-sys-cfb84e400e95762c/out" "-L" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libserde_cbor-b92effaf107035cb.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libhalf-e6655c99ab38f9c5.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libbyteorder-5fc3ce4f7f805d99.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libsecp256k1-5c90214252d1cafe.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/librand-ebdb156e0d3d08a3.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/librand_core-aa5db4de6c0d4a69.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libsecp256k1_sys-a96dfb2f870f90d5.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libserde-084022b6fd934a86.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/liblibc-5de6dfcb24ee9f89.rlib" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-cd2f2bc505f56f50.rlib" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-ec02dd343723da85.rlib" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-00ae71943a8bc9aa.rlib" "-Wl,-Bdynamic" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/no_std_test-1293a4e6b7efff78" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-Wl,-O1" "-nodefaultlibs"
  = note: /usr/bin/ld: /usr/lib/gcc/x86_64-pc-linux-gnu/12.2.1/../../../../lib/Scrt1.o: in function `_start':
          (.text+0x21): undefined reference to `__libc_start_main'
          /usr/bin/ld: /store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/no_std_test-1293a4e6b7efff78.no_std_test.87cf14f7b6e6638a-cgu.1.rcgu.o: in function `core::fmt::Write::write_char':
          no_std_test.87cf14f7b6e6638a-cgu.1:(.text._ZN4core3fmt5Write10write_char17h3bde70e0b60d9d00E+0xda): undefined reference to `memcpy'
          <snip>

where the snipped output is a pile of more "cannot find memcpy" type errors from various crates, including libcore. If I compile with 57853c4, the previous commit before this one, and force a linker error by adding an unimplemented function (not sure how else to get the exact cc command), the outputt looks like

  = note: LC_ALL="C" PATH="/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/bin:/store/home/apoelstra/.cargo/bin:/store/home/apoelstra/bin:/store/home/apoelstra/.idris2/bin:/usr/local/sbin:/usr/local/bin:/usr/bin:/usr/bin/site_perl:/usr/bin/vendor_perl:/usr/bin/core_perl:/store/home/apoelstra/.local/bin:/store/home/apoelstra/.cabal/bin" VSLANG="1033" "cc" "-m64" "/tmp/rustc3zNS0N/symbols.o" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/no_std_test-2f327716e2f5651f.no_std_test.a728628877dda375-cgu.0.rcgu.o" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/no_std_test-2f327716e2f5651f.no_std_test.a728628877dda375-cgu.1.rcgu.o" "-Wl,--as-needed" "-L" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps" "-L" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/build/secp256k1-sys-9dd44ba92659f6c7/out" "-L" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libserde_cbor-b92effaf107035cb.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libhalf-e6655c99ab38f9c5.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libbyteorder-5fc3ce4f7f805d99.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libsecp256k1-b0ad93fc3995ce88.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/librand-ebdb156e0d3d08a3.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/librand_core-aa5db4de6c0d4a69.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libsecp256k1_sys-eacd782dd019dfc5.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/libserde-084022b6fd934a86.rlib" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/liblibc-3070fa89807530f5.rlib" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/librustc_std_workspace_core-cd2f2bc505f56f50.rlib" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-ec02dd343723da85.rlib" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-00ae71943a8bc9aa.rlib" "-Wl,-Bdynamic" "-lc" "-lm" "-lrt" "-lpthread" "-Wl,--eh-frame-hdr" "-Wl,-z,noexecstack" "-L" "/store/home/apoelstra/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-o" "/store/home/apoelstra/code/rust-bitcoin/rust-secp256k1/pr-review/no_std_test/target/release/deps/no_std_test-2f327716e2f5651f" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-Wl,-O1" "-nodefaultlibs"

If you compare these two invocations you will see that the latter has -lc -lm -lrt -lpthread while the former does not. I guess that -lc is what's causing errors related to memcpy being undefined.

I've read the code in this PR a few times and can't see any way that it would cause these linker flags to get dropped, but empirically that's what appears to be happening.

@NobodyXu
Copy link
Collaborator Author

NobodyXu commented Aug 9, 2023

@apoelstra The error log says "linking with cc failed", meaning it isn't executing any cc commands but rather failed linking with cc.

Since you are doing a no-std-test, I think this is where the error comes from, since cc relies on linking with libc on unix to work.

tcharding added a commit to tcharding/rust-secp256k1 that referenced this pull request Aug 9, 2023
A recent commit (rust-lang/cc-rs#780) broke our CI nightly job. Pin to a
version before that was merged.

This should be temporary until the issue is fixed.
@apoelstra
Copy link

@NobodyXu this makes sense, but is somehow even more confusing than my original theory :(. Why would this PR cause a link error with the cc crate itself?

@joshtriplett
Copy link
Member

I am similarly not seeing how this PR causes the observed issue. Can you file a new issue so this doesn't get lost?

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.

None yet

5 participants