blob: cc9b6de7c6c8d15c36497eac7cc451709d9bfe9b [file] [log] [blame] [edit]
<!DOCTYPE HTML>
<html lang="en" class="light sidebar-visible" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>Parallel compilation - Rust Compiler Development Guide</title>
<!-- Custom HTML head -->
<meta name="description" content="A guide to developing the Rust compiler (rustc)">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="favicon.svg">
<link rel="shortcut icon" href="favicon.png">
<link rel="stylesheet" href="css/variables.css">
<link rel="stylesheet" href="css/general.css">
<link rel="stylesheet" href="css/chrome.css">
<link rel="stylesheet" href="css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" id="highlight-css" href="highlight.css">
<link rel="stylesheet" id="tomorrow-night-css" href="tomorrow-night.css">
<link rel="stylesheet" id="ayu-highlight-css" href="ayu-highlight.css">
<!-- Custom theme stylesheets -->
<link rel="stylesheet" href="pagetoc.css">
<!-- Provide site root and default themes to javascript -->
<script>
const path_to_root = "";
const default_light_theme = "light";
const default_dark_theme = "navy";
window.path_to_searchindex_js = "searchindex.js";
</script>
<!-- Start loading toc.js asap -->
<script src="toc.js"></script>
</head>
<body>
<div id="mdbook-help-container">
<div id="mdbook-help-popup">
<h2 class="mdbook-help-title">Keyboard shortcuts</h2>
<div>
<p>Press <kbd></kbd> or <kbd></kbd> to navigate between chapters</p>
<p>Press <kbd>S</kbd> or <kbd>/</kbd> to search in the book</p>
<p>Press <kbd>?</kbd> to show this help</p>
<p>Press <kbd>Esc</kbd> to hide this help</p>
</div>
</div>
</div>
<div id="body-container">
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
let theme = localStorage.getItem('mdbook-theme');
let sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
const default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? default_dark_theme : default_light_theme;
let theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
const html = document.documentElement;
html.classList.remove('light')
html.classList.add(theme);
html.classList.add("js");
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
let sidebar = null;
const sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
sidebar_toggle.checked = false;
}
if (sidebar === 'visible') {
sidebar_toggle.checked = true;
} else {
html.classList.remove('sidebar-visible');
}
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<!-- populated by js -->
<mdbook-sidebar-scrollbox class="sidebar-scrollbox"></mdbook-sidebar-scrollbox>
<noscript>
<iframe class="sidebar-iframe-outer" src="toc.html"></iframe>
</noscript>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="default_theme">Auto</button></li>
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search (`/`)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="/ s" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">Rust Compiler Development Guide</h1>
<div class="right-buttons">
<a href="print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide" title="Git repository" aria-label="Git repository">
<i id="git-repository-button" class="fa fa-github"></i>
</a>
<a href="https://github.com/rust-lang/rustc-dev-guide/edit/main/src/parallel-rustc.md" title="Suggest an edit" aria-label="Suggest an edit" rel="edit">
<i id="git-edit-button" class="fa fa-edit"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<div class="search-wrapper">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
<div class="spinner-wrapper">
<i class="fa fa-spinner fa-spin"></i>
</div>
</div>
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="parallel-compilation"><a class="header" href="#parallel-compilation">Parallel compilation</a></h1>
<div class="warning">
As of <!-- date-check --> November 2024,
the parallel front-end is undergoing significant changes,
so this page contains quite a bit of outdated information.
<p>Tracking issue: <a href="https://github.com/rust-lang/rust/issues/113349">https://github.com/rust-lang/rust/issues/113349</a></p>
</div>
<p>As of <!-- date-check --> November 2024, most of the rust compiler is now
parallelized.</p>
<ul>
<li>The codegen part is executed concurrently by default. You can use the <code>-C codegen-units=n</code> option to control the number of concurrent tasks.</li>
<li>The parts after HIR lowering to codegen such as type checking, borrowing
checking, and mir optimization are parallelized in the nightly version.
Currently, they are executed in serial by default, and parallelization is
manually enabled by the user using the <code>-Z threads = n</code> option.</li>
<li>Other parts, such as lexical parsing, HIR lowering, and macro expansion, are
still executed in serial mode.</li>
</ul>
<div class="warning">
The following sections are kept for now but are quite outdated.
</div>
<hr />
<h2 id="code-generation"><a class="header" href="#code-generation">Code generation</a></h2>
<p>During monomorphization the compiler splits up all the code to
be generated into smaller chunks called <em>codegen units</em>. These are then generated by
independent instances of LLVM running in parallel. At the end, the linker
is run to combine all the codegen units together into one binary. This process
occurs in the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_codegen_ssa/base/index.html"><code>rustc_codegen_ssa::base</code></a> module.</p>
<h2 id="data-structures"><a class="header" href="#data-structures">Data structures</a></h2>
<p>The underlying thread-safe data-structures used in the parallel compiler
can be found in the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/sync/index.html"><code>rustc_data_structures::sync</code></a> module. These data structures
are implemented differently depending on whether <code>parallel-compiler</code> is true.</p>
<div class="table-wrapper"><table><thead><tr><th>data structure</th><th>parallel</th><th>non-parallel</th></tr></thead><tbody>
<tr><td>Lock&lt;T&gt;</td><td>(parking_lot::Mutex&lt;T&gt;)</td><td>(std::cell::RefCell)</td></tr>
<tr><td>RwLock&lt;T&gt;</td><td>(parking_lot::RwLock&lt;T&gt;)</td><td>(std::cell::RefCell)</td></tr>
<tr><td>MTLock&lt;T&gt;</td><td>(Lock&lt;T&gt;)</td><td>(T)</td></tr>
<tr><td>ReadGuard</td><td>parking_lot::RwLockReadGuard</td><td>std::cell::Ref</td></tr>
<tr><td>MappedReadGuard</td><td>parking_lot::MappedRwLockReadGuard</td><td>std::cell::Ref</td></tr>
<tr><td>WriteGuard</td><td>parking_lot::RwLockWriteGuard</td><td>std::cell::RefMut</td></tr>
<tr><td>MappedWriteGuard</td><td>parking_lot::MappedRwLockWriteGuard</td><td>std::cell::RefMut</td></tr>
<tr><td>LockGuard</td><td>parking_lot::MutexGuard</td><td>std::cell::RefMut</td></tr>
</tbody></table>
</div>
<ul>
<li>
<p>These thread-safe data structures are interspersed during compilation which
can cause lock contention resulting in degraded performance as the number of
threads increases beyond 4. So we audit the use of these data structures
which leads to either a refactoring so as to reduce the use of shared state,
or the authoring of persistent documentation covering the specific of the
invariants, the atomicity, and the lock orderings.</p>
</li>
<li>
<p>On the other hand, we still need to figure out what other invariants
during compilation might not hold in parallel compilation.</p>
</li>
</ul>
<h3 id="workerlocal"><a class="header" href="#workerlocal">WorkerLocal</a></h3>
<p><a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/sync/worker_local/struct.WorkerLocal.html"><code>WorkerLocal</code></a> is a special data structure implemented for parallel compilers. It
holds worker-locals values for each thread in a thread pool. You can only
access the worker local value through the <code>Deref</code> <code>impl</code> on the thread pool it
was constructed on. It panics otherwise.</p>
<p><code>WorkerLocal</code> is used to implement the <code>Arena</code> allocator in the parallel
environment, which is critical in parallel queries. Its implementation is
located in the <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_data_structures/sync/worker_local/index.html"><code>rustc_data_structures::sync::worker_local</code></a> module. However,
in the non-parallel compiler, it is implemented as <code>(OneThread&lt;T&gt;)</code>, whose <code>T</code>
can be accessed directly through <code>Deref::deref</code>.</p>
<h2 id="parallel-iterator"><a class="header" href="#parallel-iterator">Parallel iterator</a></h2>
<p>The parallel iterators provided by the <a href="https://crates.io/crates/rayon"><code>rayon</code></a> crate are easy ways to
implement parallelism. In the current implementation of the parallel compiler
we use a custom <a href="https://github.com/rust-lang/rustc-rayon">fork</a> of <code>rayon</code> to run tasks in parallel.</p>
<p>Some iterator functions are implemented to run loops in parallel
when <code>parallel-compiler</code> is true.</p>
<div class="table-wrapper"><table><thead><tr><th>Function(Omit <code>Send</code> and <code>Sync</code>)</th><th>Introduction</th><th>Owning Module</th></tr></thead><tbody>
<tr><td><strong>par_iter</strong>&lt;T: IntoParallelIterator&gt;(t: T) -&gt; T::Iter</td><td>generate a parallel iterator</td><td>rustc_data_structure::sync</td></tr>
<tr><td><strong>par_for_each_in</strong>&lt;T: IntoParallelIterator&gt;(t: T, for_each: impl Fn(T::Item))</td><td>generate a parallel iterator and run <code>for_each</code> on each element</td><td>rustc_data_structure::sync</td></tr>
<tr><td><strong>Map::par_body_owners</strong>(self, f: impl Fn(LocalDefId))</td><td>run <code>f</code> on all hir owners in the crate</td><td>rustc_middle::hir::map</td></tr>
<tr><td><strong>Map::par_for_each_module</strong>(self, f: impl Fn(LocalDefId))</td><td>run <code>f</code> on all modules and sub modules in the crate</td><td>rustc_middle::hir::map</td></tr>
<tr><td><strong>ModuleItems::par_items</strong>(&amp;self, f: impl Fn(ItemId))</td><td>run <code>f</code> on all items in the module</td><td>rustc_middle::hir</td></tr>
<tr><td><strong>ModuleItems::par_trait_items</strong>(&amp;self, f: impl Fn(TraitItemId))</td><td>run <code>f</code> on all trait items in the module</td><td>rustc_middle::hir</td></tr>
<tr><td><strong>ModuleItems::par_impl_items</strong>(&amp;self, f: impl Fn(ImplItemId))</td><td>run <code>f</code> on all impl items in the module</td><td>rustc_middle::hir</td></tr>
<tr><td><strong>ModuleItems::par_foreign_items</strong>(&amp;self, f: impl Fn(ForeignItemId))</td><td>run <code>f</code> on all foreign items in the module</td><td>rustc_middle::hir</td></tr>
</tbody></table>
</div>
<p>There are a lot of loops in the compiler which can possibly be parallelized
using these functions. As of <!-- date-check--> August 2022, scenarios where
the parallel iterator function has been used are as follows:</p>
<div class="table-wrapper"><table><thead><tr><th>caller</th><th>scenario</th><th>callee</th></tr></thead><tbody>
<tr><td>rustc_metadata::rmeta::encoder::prefetch_mir</td><td>Prefetch queries which will be needed later by metadata encoding</td><td>par_iter</td></tr>
<tr><td>rustc_monomorphize::collector::collect_crate_mono_items</td><td>Collect monomorphized items reachable from non-generic items</td><td>par_for_each_in</td></tr>
<tr><td>rustc_interface::passes::analysis</td><td>Check the validity of the match statements</td><td>Map::par_body_owners</td></tr>
<tr><td>rustc_interface::passes::analysis</td><td>MIR borrow check</td><td>Map::par_body_owners</td></tr>
<tr><td>rustc_typeck::check::typeck_item_bodies</td><td>Type check</td><td>Map::par_body_owners</td></tr>
<tr><td>rustc_interface::passes::hir_id_validator::check_crate</td><td>Check the validity of hir</td><td>Map::par_for_each_module</td></tr>
<tr><td>rustc_interface::passes::analysis</td><td>Check the validity of loops body, attributes, naked functions, unstable abi, const bodys</td><td>Map::par_for_each_module</td></tr>
<tr><td>rustc_interface::passes::analysis</td><td>Liveness and intrinsic checking of MIR</td><td>Map::par_for_each_module</td></tr>
<tr><td>rustc_interface::passes::analysis</td><td>Deathness checking</td><td>Map::par_for_each_module</td></tr>
<tr><td>rustc_interface::passes::analysis</td><td>Privacy checking</td><td>Map::par_for_each_module</td></tr>
<tr><td>rustc_lint::late::check_crate</td><td>Run per-module lints</td><td>Map::par_for_each_module</td></tr>
<tr><td>rustc_typeck::check_crate</td><td>Well-formedness checking</td><td>Map::par_for_each_module</td></tr>
</tbody></table>
</div>
<p>There are still many loops that have the potential to use parallel iterators.</p>
<h2 id="query-system"><a class="header" href="#query-system">Query system</a></h2>
<p>The query model has some properties that make it actually feasible to evaluate
multiple queries in parallel without too much effort:</p>
<ul>
<li>All data a query provider can access is via the query context, so
the query context can take care of synchronizing access.</li>
<li>Query results are required to be immutable so they can safely be used by
different threads concurrently.</li>
</ul>
<p>When a query <code>foo</code> is evaluated, the cache table for <code>foo</code> is locked.</p>
<ul>
<li>If there already is a result, we can clone it, release the lock and
we are done.</li>
<li>If there is no cache entry and no other active query invocation computing the
same result, we mark the key as being "in progress", release the lock and
start evaluating.</li>
<li>If there <em>is</em> another query invocation for the same key in progress, we
release the lock, and just block the thread until the other invocation has
computed the result we are waiting for. <strong>Cycle error detection</strong> in the parallel
compiler requires more complex logic than in single-threaded mode. When
worker threads in parallel queries stop making progress due to interdependence,
the compiler uses an extra thread <em>(named deadlock handler)</em> to detect, remove and
report the cycle error.</li>
</ul>
<p>The parallel query feature still has implementation to do, most of which is
related to the previous <code>Data Structures</code> and <code>Parallel Iterators</code>. See <a href="https://github.com/rust-lang/rust/issues/48685">this
open feature tracking issue</a>.</p>
<h2 id="rustdoc"><a class="header" href="#rustdoc">Rustdoc</a></h2>
<p>As of <!-- date-check--> November 2022, there are still a number of steps to
complete before <code>rustdoc</code> rendering can be made parallel (see a open discussion
of <a href="https://github.com/rust-lang/rust/issues/82741">parallel <code>rustdoc</code></a>).</p>
<h2 id="resources"><a class="header" href="#resources">Resources</a></h2>
<p>Here are some resources that can be used to learn more:</p>
<ul>
<li><a href="https://internals.rust-lang.org/t/help-test-parallel-rustc/11503">This IRLO thread by alexchricton about performance</a></li>
<li><a href="https://internals.rust-lang.org/t/parallelizing-rustc-using-rayon/6606">This IRLO thread by Zoxc, one of the pioneers of the effort</a></li>
<li><a href="https://github.com/nikomatsakis/rustc-parallelization/blob/master/interior-mutability-list.md">This list of interior mutability in the compiler by nikomatsakis</a></li>
</ul>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="serialization.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="rustdoc-internals.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="serialization.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="rustdoc-internals.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="elasticlunr.min.js"></script>
<script src="mark.min.js"></script>
<script src="searcher.js"></script>
<script src="clipboard.min.js"></script>
<script src="highlight.js"></script>
<script src="book.js"></script>
<!-- Custom JS scripts -->
<script src="mermaid.min.js"></script>
<script src="mermaid-init.js"></script>
<script src="pagetoc.js"></script>
</div>
</body>
</html>