Skip to content

Conversation

Zoxc
Copy link
Contributor

@Zoxc Zoxc commented Mar 2, 2024

This makes saving the dep graph and finalizing the incremental session directory run in parallel with linking.

It adds a task function which allows a closure to run on the Rayon thread pool and be waited on later. This is used in codegen_and_build_linker to start the linker in separate task.

This is blocked on rust-lang/rustc-rayon#12 as otherwise this runs into deadlocks.

@rustbot
Copy link
Collaborator

rustbot commented Mar 2, 2024

r? @estebank

rustbot has assigned @estebank.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Mar 2, 2024
@rust-log-analyzer

This comment has been minimized.

@@ -461,8 +461,8 @@ fn run_compiler(
// Linking is done outside the `compiler.enter()` so that the
// `GlobalCtxt` within `Queries` can be freed as early as possible.
if let Some(linker) = linker {
let _timer = sess.timer("link");
linker.link(sess, codegen_backend)?
let _timer = sess.timer("waiting for linking");
Copy link
Member

Choose a reason for hiding this comment

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

Can we keep a timer for the entire time linking takes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We still have link_crate which covers the codegen backend part of linking.

let sess = self.compiler.sess.clone();
let codegen_backend = self.compiler.codegen_backend.clone();

Ok(task(move || linker.link(&sess, &*codegen_backend)))
Copy link
Member

@bjorn3 bjorn3 Mar 2, 2024

Choose a reason for hiding this comment

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

Why does this need to run in a separate task? It is immediately awaited, right? The parallelism should happen inside the link method using join, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not immediately awaited. The parallelism happens because we create a task here (think of it as a separate thread).

Copy link
Member

Choose a reason for hiding this comment

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

I would have expected the parallelism to come from the join call inside link.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, there is a save_dep_graph call inside compiler.enter() that happens after this call. Instead of the join() call and channels would it be possible to move the finalize_session_directory and dep graph dropping to right after the save_dep_graph call? That seems a lot simpler.

Copy link
Member

@bjorn3 bjorn3 Mar 5, 2024

Choose a reason for hiding this comment

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

Something like this. I also fixed a pre-existing incr comp issue where finalize_session_directory is called before the dep graph is actually written, resulting in a broken incr comp cache if you interrupt the build at just the right moment.

diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs
index df7778cd512..9376fa9552c 100644
--- a/compiler/rustc_interface/src/queries.rs
+++ b/compiler/rustc_interface/src/queries.rs
@@ -5,11 +5,10 @@
 use rustc_ast as ast;
 use rustc_codegen_ssa::traits::CodegenBackend;
 use rustc_codegen_ssa::CodegenResults;
-use rustc_data_structures::jobserver;
 use rustc_data_structures::steal::Steal;
 use rustc_data_structures::svh::Svh;
 use rustc_data_structures::sync::{
-    join, task, AppendOnlyIndexVec, DynSend, FreezeLock, Lrc, OnceLock, Task, WorkerLocal,
+    task, AppendOnlyIndexVec, DynSend, FreezeLock, Lrc, OnceLock, Task, WorkerLocal,
 };
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::{StableCrateId, CRATE_DEF_ID, LOCAL_CRATE};
@@ -26,8 +25,7 @@
 use rustc_session::Session;
 use rustc_span::symbol::sym;
 use std::any::Any;
-use std::cell::{RefCell, RefMut};
-use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
+use std::cell::{OnceCell, RefCell, RefMut};
 use std::sync::Arc;
 
 /// Represent the result of a query.
@@ -89,26 +87,23 @@ pub struct Queries<'tcx> {
     arena: WorkerLocal<Arena<'tcx>>,
     hir_arena: WorkerLocal<rustc_hir::Arena<'tcx>>,
 
-    dep_graph_serialized_rx: Steal<Receiver<()>>,
-    dep_graph_serialized_tx: SyncSender<()>,
-
     parse: Query<ast::Crate>,
     // This just points to what's in `gcx_cell`.
     gcx: Query<&'tcx GlobalCtxt<'tcx>>,
+    // Only present when incr. comp. is enabled.
+    crate_hash: OnceCell<Option<Svh>>,
 }
 
 impl<'tcx> Queries<'tcx> {
     pub fn new(compiler: &'tcx Compiler) -> Queries<'tcx> {
-        let (tx, rx) = sync_channel(1);
         Queries {
             compiler,
-            dep_graph_serialized_rx: Steal::new(rx),
-            dep_graph_serialized_tx: tx,
             gcx_cell: OnceLock::new(),
             arena: WorkerLocal::new(|_| Arena::default()),
             hir_arena: WorkerLocal::new(|_| rustc_hir::Arena::default()),
             parse: Default::default(),
             gcx: Default::default(),
+            crate_hash: Default::default(),
         }
     }
 
@@ -245,31 +240,27 @@ pub fn codegen_and_build_linker(&'tcx self) -> Result<Task<Result<()>>> {
             let ongoing_codegen = passes::start_codegen(&*self.compiler.codegen_backend, tcx);
 
             let linker = Linker {
-                dep_graph_serialized_rx: self.dep_graph_serialized_rx.steal(),
                 dep_graph: tcx.dep_graph.clone(),
                 output_filenames: tcx.output_filenames(()).clone(),
-                crate_hash: if tcx.needs_crate_hash() {
-                    Some(tcx.crate_hash(LOCAL_CRATE))
-                } else {
-                    None
-                },
                 ongoing_codegen,
             };
 
             let sess = self.compiler.sess.clone();
             let codegen_backend = self.compiler.codegen_backend.clone();
 
+            // Ensure the crate hash is computed if necessary
+            self.crate_hash
+                .set(if tcx.needs_crate_hash() { Some(tcx.crate_hash(LOCAL_CRATE)) } else { None })
+                .unwrap();
+
             Ok(task(move || linker.link(&sess, &*codegen_backend)))
         })
     }
 }
 
 struct Linker {
-    dep_graph_serialized_rx: Receiver<()>,
     dep_graph: DepGraph,
     output_filenames: Arc<OutputFilenames>,
-    // Only present when incr. comp. is enabled.
-    crate_hash: Option<Svh>,
     ongoing_codegen: Box<dyn Any + DynSend>,
 }
 
@@ -286,54 +277,34 @@ fn link(self, sess: &Lrc<Session>, codegen_backend: &dyn CodegenBackend) -> Resu
             rustc_incremental::save_work_product_index(sess, &self.dep_graph, work_products)
         });
 
-        let dep_graph_serialized_rx = self.dep_graph_serialized_rx;
+        let prof = sess.prof.clone();
+        prof.generic_activity("drop_dep_graph").run(move || drop(self.dep_graph));
 
-        join(
-            || {
-                if !sess
-                    .opts
-                    .output_types
-                    .keys()
-                    .any(|&i| i == OutputType::Exe || i == OutputType::Metadata)
-                {
-                    return Ok(());
-                }
-
-                if sess.opts.unstable_opts.no_link {
-                    let rlink_file = self.output_filenames.with_extension(config::RLINK_EXT);
-                    CodegenResults::serialize_rlink(
-                        sess,
-                        &rlink_file,
-                        &codegen_results,
-                        &*self.output_filenames,
-                    )
-                    .map_err(|error| {
-                        sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error })
-                    })?;
-                    return Ok(());
-                }
-
-                let _timer = sess.prof.verbose_generic_activity("link_crate");
-                codegen_backend.link(sess, codegen_results, &self.output_filenames)
-            },
-            || {
-                let dep_graph_serialized_rx = dep_graph_serialized_rx;
-
-                // Wait for the dep graph to be serialized before finalizing the session directory.
-                if !dep_graph_serialized_rx.try_recv().is_ok() {
-                    jobserver::release_thread();
-                    dep_graph_serialized_rx.recv().unwrap();
-                    jobserver::acquire_thread();
-                }
+        if !sess
+            .opts
+            .output_types
+            .keys()
+            .any(|&i| i == OutputType::Exe || i == OutputType::Metadata)
+        {
+            return Ok(());
+        }
 
-                sess.prof.generic_activity("drop_dep_graph").run(move || drop(self.dep_graph));
+        if sess.opts.unstable_opts.no_link {
+            let rlink_file = self.output_filenames.with_extension(config::RLINK_EXT);
+            CodegenResults::serialize_rlink(
+                sess,
+                &rlink_file,
+                &codegen_results,
+                &*self.output_filenames,
+            )
+            .map_err(|error| {
+                sess.dcx().emit_fatal(FailedWritingFile { path: &rlink_file, error })
+            })?;
+            return Ok(());
+        }
 
-                // Now that we won't touch anything in the incremental compilation directory
-                // any more, we can finalize it (which involves renaming it)
-                rustc_incremental::finalize_session_directory(sess, self.crate_hash);
-            },
-        )
-        .0
+        let _timer = sess.prof.verbose_generic_activity("link_crate");
+        codegen_backend.link(sess, codegen_results, &self.output_filenames)
     }
 }
 
@@ -362,16 +333,20 @@ pub fn enter<F, T>(&self, f: F) -> T
             self.sess.time("serialize_dep_graph", || gcx.enter(rustc_incremental::save_dep_graph));
         }
 
-        // Finish the dep graph encoding before we signal `dep_graph_serialized`.
+        // The timer's lifetime spans the dropping of `queries`, which contains
+        // the global context.
+        _timer = Some(self.sess.timer("free_global_ctxt"));
         if let Err((path, error)) = queries.finish() {
             self.sess.dcx().emit_fatal(errors::FailedWritingFile { path: &path, error });
         }
 
-        queries.dep_graph_serialized_tx.send(()).ok();
+        // Now that we won't touch anything in the incremental compilation directory
+        // any more, we can finalize it (which involves renaming it)
+        rustc_incremental::finalize_session_directory(
+            &queries.compiler.sess,
+            queries.crate_hash.get().unwrap().clone(),
+        );
 
-        // The timer's lifetime spans the dropping of `queries`, which contains
-        // the global context.
-        _timer = Some(self.sess.timer("free_global_ctxt"));
         ret
     }
 }

Copy link
Contributor Author

Choose a reason for hiding this comment

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

would it be possible to move the finalize_session_directory and dep graph dropping to right after the save_dep_graph call?

No. finalize_session_directory needs to happen after we save the work product from the codegen backend.

pub sess: Session,
pub codegen_backend: Box<dyn CodegenBackend>,
pub sess: Lrc<Session>,
pub codegen_backend: Lrc<dyn CodegenBackend>,
Copy link
Member

Choose a reason for hiding this comment

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

Where exactly does the requirement for turning Box<dyn CodegenBackend> into Lrc<dyn CodegenBackend> come from?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's used in the task which calls link.

Copy link
Member

Choose a reason for hiding this comment

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

Maybe codegen_backend.link() itself could return a Task instead?

@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Collaborator

bors commented Mar 8, 2024

☔ The latest upstream changes (presumably #122151) made this pull request unmergeable. Please resolve the merge conflicts.

@Dylan-DPC Dylan-DPC added S-blocked Status: Blocked on something else such as an RFC or other implementation work. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 4, 2025
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@bors
Copy link
Collaborator

bors commented Apr 11, 2025

☔ The latest upstream changes (presumably #139011) made this pull request unmergeable. Please resolve the merge conflicts.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-blocked Status: Blocked on something else such as an RFC or other implementation work. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants