Skip to content

Commit

Permalink
heap: optimize EMPTY to avoid relocations
Browse files Browse the repository at this point in the history
Sized deallocation makes it pointless to provide an address that never
overlaps with pointers returned by an allocator. Code can branch on the
capacity of the allocation instead of a comparison with this sentinel.

This improves the situation in #8859, and the remaining issues are only
from the logging API, which should be disabled by default in optimized
release builds anyway along with debug assertions. The remaining issues
are part of #17081.

Closes #8859
  • Loading branch information
thestinger committed Sep 15, 2014
1 parent 396f910 commit 84b3737
Show file tree
Hide file tree
Showing 3 changed files with 11 additions and 14 deletions.
11 changes: 6 additions & 5 deletions src/liballoc/heap.rs
Expand Up @@ -86,18 +86,19 @@ pub fn stats_print() {
imp::stats_print();
}

// The compiler never calls `exchange_free` on Box<ZeroSizeType>, so zero-size
// allocations can point to this `static`. It would be incorrect to use a null
// pointer, due to enums assuming types like unique pointers are never null.
pub static mut EMPTY: uint = 12345;
/// An arbitrary non-null address to represent zero-size allocations.
///
/// This preserves the non-null invariant for types like `Box<T>`. The address may overlap with
/// non-zero-size memory allocations.
pub static EMPTY: *mut () = 0x1 as *mut ();

/// The allocator for unique pointers.
#[cfg(not(test))]
#[lang="exchange_malloc"]
#[inline]
unsafe fn exchange_malloc(size: uint, align: uint) -> *mut u8 {
if size == 0 {
&EMPTY as *const uint as *mut u8
EMPTY as *mut u8
} else {
allocate(size, align)
}
Expand Down
10 changes: 3 additions & 7 deletions src/libcollections/vec.rs
Expand Up @@ -12,7 +12,7 @@

use core::prelude::*;

use alloc::heap::{allocate, reallocate, deallocate};
use alloc::heap::{EMPTY, allocate, reallocate, deallocate};
use core::cmp::max;
use core::default::Default;
use core::fmt;
Expand All @@ -26,10 +26,6 @@ use {Mutable, MutableSeq};
use slice::{MutableOrdSlice, MutableSliceAllocating, CloneableVector};
use slice::{Items, MutItems};


#[doc(hidden)]
pub static PTR_MARKER: u8 = 0;

/// An owned, growable vector.
///
/// # Examples
Expand Down Expand Up @@ -122,7 +118,7 @@ impl<T> Vec<T> {
// non-null value which is fine since we never call deallocate on the ptr
// if cap is 0. The reason for this is because the pointer of a slice
// being NULL would break the null pointer optimization for enums.
Vec { len: 0, cap: 0, ptr: &PTR_MARKER as *const _ as *mut T }
Vec { len: 0, cap: 0, ptr: EMPTY as *mut T }
}

/// Constructs a new, empty `Vec` with the specified capacity.
Expand Down Expand Up @@ -155,7 +151,7 @@ impl<T> Vec<T> {
#[inline]
pub fn with_capacity(capacity: uint) -> Vec<T> {
if mem::size_of::<T>() == 0 {
Vec { len: 0, cap: uint::MAX, ptr: &PTR_MARKER as *const _ as *mut T }
Vec { len: 0, cap: uint::MAX, ptr: EMPTY as *mut T }
} else if capacity == 0 {
Vec::new()
} else {
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/owned_slice.rs
Expand Up @@ -12,6 +12,7 @@ use std::fmt;
use std::default::Default;
use std::hash;
use std::{mem, raw, ptr, slice, vec};
use std::rt::heap::EMPTY;
use serialize::{Encodable, Decodable, Encoder, Decoder};

/// A non-growable owned slice. This would preferably become `~[T]`
Expand Down Expand Up @@ -81,10 +82,9 @@ impl<T> OwnedSlice<T> {
}

pub fn as_slice<'a>(&'a self) -> &'a [T] {
static PTR_MARKER: u8 = 0;
let ptr = if self.data.is_null() {
// length zero, i.e. this will never be read as a T.
&PTR_MARKER as *const u8 as *const T
EMPTY as *const T
} else {
self.data as *const T
};
Expand Down

0 comments on commit 84b3737

Please sign in to comment.