Skip to content

Commit

Permalink
minor edits
Browse files Browse the repository at this point in the history
  • Loading branch information
Noratrieb committed Sep 24, 2024
1 parent 1ae4bea commit a8a36c7
Showing 1 changed file with 38 additions and 38 deletions.
76 changes: 38 additions & 38 deletions src/memory.md
Original file line number Diff line number Diff line change
@@ -1,57 +1,57 @@
# Memory Management in Rustc

Generally `rustc` tries to be pretty careful how it manages memory. The
compiler allocates _a lot_ of data structures throughout compilation, and if we
are not careful, it will take a lot of time and space to do so.
Generally rustc tries to be pretty careful how it manages memory.
The compiler allocates _a lot_ of data structures throughout compilation,
and if we are not careful, it will take a lot of time and space to do so.

One of the main way the compiler manages this is using [`arena`]s and [interning].
One of the main way the compiler manages this is using [arena]s and [interning].

[`arena`]: https://en.wikipedia.org/wiki/Region-based_memory_management
[arena]: https://en.wikipedia.org/wiki/Region-based_memory_management
[interning]: https://en.wikipedia.org/wiki/String_interning

## Arenas and Interning

Since A LOT of data structures are created during compilation, for performance
reasons, we allocate them from a global memory pool. Each are allocated once
from a long-lived *`arena`*. This is called _arena allocation_. This system
reduces allocations/deallocations of memory. It also allows for easy comparison
of types (more on types [here](./ty.md)) for equality: for each interned
type `X`, we implemented [`PartialEq` for X][peqimpl], so we can just compare
pointers. The [`CtxtInterners`] type contains a bunch of maps of interned types
and the `arena` itself.
reasons, we allocate them from a global memory pool.
Each are allocated once from a long-lived *arena*.
This is called _arena allocation_.
This system reduces allocations/deallocations of memory.
It also allows for easy comparison of types (more on types [here](./ty.md)) for equality:
for each interned type `X`, we implemented [`PartialEq` for X][peqimpl],
so we can just compare pointers.
The [`CtxtInterners`] type contains a bunch of maps of interned types and the arena itself.

[`CtxtInterners`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.CtxtInterners.html#structfield.arena
[peqimpl]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.Ty.html#implementations

### Example: `ty::TyKind`

Take [`ty::TyKind`] which represents a type in the compiler. Each time we want
to construct a type, the compiler doesn’t naively allocate from the buffer.
Instead, we check if that type was already constructed. If it was, we just get
the same pointer we had before, otherwise we make a fresh pointer. With this
schema if we want to know if two types are the same, all we need to do is
compare pointers, which is efficient. [`TyKind`] should never be constructed on
the stack, and it would be unusable if done so. You always allocate them from
this `arena` and you always intern them so they are unique.

At the beginning of the compilation we make a buffer and each time we need to
allocate a type we use some of this memory buffer. If we run out of space we
get another one. The lifetime of that buffer is `'tcx`. Our types are tied to
that lifetime, so when compilation finishes all the memory related to that
buffer is freed and our `'tcx` references are invalidated.

In addition to types, there are a number of other `arena`-allocated data
structures that you can allocate, and which are found in this module. Here are
a few examples:
Taking the example of [`ty::TyKind`] which represents a type in the compiler (you
can read more [here](./ty.md)). Each time we want to construct a type, the
compiler doesn’t naively allocate from the buffer. Instead, we check if that
type was already constructed. If it was, we just get the same pointer we had
before, otherwise we make a fresh pointer. With this schema if we want to know
if two types are the same, all we need to do is compare the pointers which is
efficient. [`TyKind`] should never be constructed on the stack, and it would be unusable
if done so.
You always allocate them from this arena and you always intern them so they are
unique.

At the beginning of the compilation we make a buffer and each time we need to allocate a type we use
some of this memory buffer. If we run out of space we get another one. The lifetime of that buffer
is `'tcx`. Our types are tied to that lifetime, so when compilation finishes all the memory related
to that buffer is freed and our `'tcx` references would be invalid.

In addition to types, there are a number of other arena-allocated data structures that you can
allocate, and which are found in this module. Here are a few examples:

- [`GenericArgs`], allocated with [`mk_args`] – this will intern a slice of types, often used
to specify the values to be substituted for generics args (e.g. `HashMap<i32, u32>` would be
represented as a slice `&'tcx [tcx.types.i32, tcx.types.u32]`).
- [`TraitRef`], typically passed by value – a **trait reference** consists of a
reference to a trait along with its various type parameters (including
`Self`), like `i32: Display` (here, the def-id would reference the `Display`
trait, and the args would contain `i32`). Note that [`def-id`] is defined and
discussed in depth in the [`AdtDef` and `DefId`] section.
to specify the values to be substituted for generics args (e.g. `HashMap<i32, u32>` would be
represented as a slice `&'tcx [tcx.types.i32, tcx.types.u32]`).
- [`TraitRef`], typically passed by value – a **trait reference** consists of a reference to a trait
along with its various type parameters (including `Self`), like `i32: Display` (here, the def-id
would reference the `Display` trait, and the args would contain `i32`). Note that `def-id` is
defined and discussed in depth in the `AdtDef and DefId` section.
- [`Predicate`] defines something the trait system has to prove (see [traits] module).

[`AdtDef` and `DefId`]: ./ty.md#adts-representation
Expand Down Expand Up @@ -86,7 +86,7 @@ the arenas, anyhow).
### A Note On Lifetimes

The Rust compiler is a fairly large program containing lots of big data
structures (e.g. the [Abstract Syntax Tree (`AST`)][ast], [High-Level Intermediate
structures (e.g. the [Abstract Syntax Tree (AST)][ast], [High-Level Intermediate
Representation (`HIR`)][hir], and the type system) and as such, arenas and
references are heavily relied upon to minimize unnecessary memory use. This
manifests itself in the way people can plug into the compiler (i.e. the
Expand Down

0 comments on commit a8a36c7

Please sign in to comment.