heap: optimize EMPTY to avoid relocations
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
This commit is contained in:
@@ -86,10 +86,11 @@ pub fn stats_print() {
|
|||||||
imp::stats_print();
|
imp::stats_print();
|
||||||
}
|
}
|
||||||
|
|
||||||
// The compiler never calls `exchange_free` on Box<ZeroSizeType>, so zero-size
|
/// An arbitrary non-null address to represent zero-size allocations.
|
||||||
// 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.
|
/// This preserves the non-null invariant for types like `Box<T>`. The address may overlap with
|
||||||
pub static mut EMPTY: uint = 12345;
|
/// non-zero-size memory allocations.
|
||||||
|
pub static EMPTY: *mut () = 0x1 as *mut ();
|
||||||
|
|
||||||
/// The allocator for unique pointers.
|
/// The allocator for unique pointers.
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
@@ -97,7 +98,7 @@ pub static mut EMPTY: uint = 12345;
|
|||||||
#[inline]
|
#[inline]
|
||||||
unsafe fn exchange_malloc(size: uint, align: uint) -> *mut u8 {
|
unsafe fn exchange_malloc(size: uint, align: uint) -> *mut u8 {
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
&EMPTY as *const uint as *mut u8
|
EMPTY as *mut u8
|
||||||
} else {
|
} else {
|
||||||
allocate(size, align)
|
allocate(size, align)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
use core::prelude::*;
|
use core::prelude::*;
|
||||||
|
|
||||||
use alloc::heap::{allocate, reallocate, deallocate};
|
use alloc::heap::{EMPTY, allocate, reallocate, deallocate};
|
||||||
use core::cmp::max;
|
use core::cmp::max;
|
||||||
use core::default::Default;
|
use core::default::Default;
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
@@ -26,10 +26,6 @@ use {Mutable, MutableSeq};
|
|||||||
use slice::{MutableOrdSlice, MutableSliceAllocating, CloneableVector};
|
use slice::{MutableOrdSlice, MutableSliceAllocating, CloneableVector};
|
||||||
use slice::{Items, MutItems};
|
use slice::{Items, MutItems};
|
||||||
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub static PTR_MARKER: u8 = 0;
|
|
||||||
|
|
||||||
/// An owned, growable vector.
|
/// An owned, growable vector.
|
||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
@@ -122,7 +118,7 @@ impl<T> Vec<T> {
|
|||||||
// non-null value which is fine since we never call deallocate on the ptr
|
// 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
|
// 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.
|
// 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.
|
/// Constructs a new, empty `Vec` with the specified capacity.
|
||||||
@@ -155,7 +151,7 @@ impl<T> Vec<T> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_capacity(capacity: uint) -> Vec<T> {
|
pub fn with_capacity(capacity: uint) -> Vec<T> {
|
||||||
if mem::size_of::<T>() == 0 {
|
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 {
|
} else if capacity == 0 {
|
||||||
Vec::new()
|
Vec::new()
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use std::fmt;
|
|||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::hash;
|
use std::hash;
|
||||||
use std::{mem, raw, ptr, slice, vec};
|
use std::{mem, raw, ptr, slice, vec};
|
||||||
|
use std::rt::heap::EMPTY;
|
||||||
use serialize::{Encodable, Decodable, Encoder, Decoder};
|
use serialize::{Encodable, Decodable, Encoder, Decoder};
|
||||||
|
|
||||||
/// A non-growable owned slice. This would preferably become `~[T]`
|
/// A non-growable owned slice. This would preferably become `~[T]`
|
||||||
@@ -81,10 +82,9 @@ impl<T> OwnedSlice<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_slice<'a>(&'a self) -> &'a [T] {
|
pub fn as_slice<'a>(&'a self) -> &'a [T] {
|
||||||
static PTR_MARKER: u8 = 0;
|
|
||||||
let ptr = if self.data.is_null() {
|
let ptr = if self.data.is_null() {
|
||||||
// length zero, i.e. this will never be read as a T.
|
// 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 {
|
} else {
|
||||||
self.data as *const T
|
self.data as *const T
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user