Skip to main content

Span

Struct Span 

#[repr(packed(4))]
pub struct Span(u64);
Expand description

A compressed span.

[SpanData] is 16 bytes, which is too big to stick everywhere. Span is only 8 bytes. A large majority of SpanData instances can be made to fit within those 8 bytes. Any SpanData whose fields don’t fit into a Span are stored in a separate interner table, and the Span will index into that table.

An earlier version of this code used only 4 bytes for Span, but that was slower because many fewer spans could be stored inline and the interner was used a lot more. That version of the code also predated the storage of parents.

Experiments with uncompressed spans yielded worse performance in most cases because memory usage and cache miss rates are significantly higher without compression.

There are four different span formats and each packs data into the 8 bytes in a different way. A variable-length prefix in the high bits identifies which format is being used. It is an invariant that we always use the first listed format whose requirements are satisfied. len is hi - lo.

  • Inline-context format (requires 15-bit length, 16-bit context, and no parent):

    [ prefix=0 | len:15 | ctxt:16 | lo:32 ]

  • Inline-parent format (requires 14-bit length, root context, and 15-bit parent):

    [ prefix=110 | len:14 | parent:15 | lo:32 ]

  • Inline-pair format (requires 24-bit lo, 7-bit length, 15-bit context, 16-bit parent):

    [ prefix=10 | len:7 | ctxt:15 | parent:16 | lo:24 ]

  • Interned format (all cases not covered above):

    [ prefix=111 | ctxt:29 | index:32 ]

Notes about this design.

  • This configuration was the best one found after many measurements of real-world crates, and replaced an earlier 8-byte design.

  • The size of lo depends on the size of the crate and its dependencies. Crates whose lo size exceeds 24-bits are extremely rare.

  • The most common numbers of bits needed for len are from 0 to 7, with a peak usually at 3 or 4, and then it drops off quickly from 8 onwards. Lengths larger than 14 bits are rare, but many crates will have a small number of such lengths (sometimes 20+ bits).

  • The number of bits needed for ctxt and parent values depend partly on the crate size and partly on the form of the code. For both fields, values needing more than 15 bits are very rare, and most crates won’t hit these limits.

  • The ctxt is stored inline in every format (Every ctxt must fit in 29 bits; if we ever hit that we’d have many gigabytes of contexts, and certainly would have OOM’d anyway.) This means reading the ctxt never requires checking the interner. This is good because the ctxt is often consulted by itself. It also means we don’t need to store the ctxt in the interner table, which we achieve by using SpanDataNoCtxt, which minimizes the number of unique values that need to be interned.

  • The use of repr(packed(4)) means Span has an alignment of 4 bytes, which keeps many types that contain spans (e.g. AST nodes) smaller. repr(packed(1)) isn’t worth it; it only shrinks a few types and performance is a tiny bit worse.

In order to reliably use parented spans in incremental compilation, accesses to lo and hi must introduce a dependency to the parent definition’s span. This is performed using the callback SPAN_TRACK to access the query engine.

Tuple Fields§

§0: u64

Auto Trait Implementations§

§

impl DynSend for Span

§

impl DynSync for Span

§

impl Freeze for Span

§

impl RefUnwindSafe for Span

§

impl Send for Span

§

impl Sync for Span

§

impl Unpin for Span

§

impl UnsafeUnpin for Span

§

impl UnwindSafe for Span

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more