#![cfg_attr(not(any(feature = "std", test)), no_std)]
#![cfg_attr(feature = "nightly", feature(doc_cfg, portable_simd, ptr_metadata))]
#![cfg_attr(feature = "default_impl", allow(incomplete_features))]
#![cfg_attr(feature = "default_impl", feature(specialization))]
#![deny(missing_docs)]
#[cfg(any(feature = "alloc", test))]
extern crate alloc as std_alloc;
mod alloc;
pub mod arch;
#[cfg(any(feature = "alloc", test))]
mod boxed;
mod cell;
mod cmp;
#[cfg(all(any(feature = "std", test), any(unix, target_os = "wasi")))]
mod ffi;
mod fmt;
#[cfg(all(any(feature = "std", test), any(unix, windows)))]
mod fs;
#[cfg(any(feature = "std", test))]
mod io;
mod iter;
mod marker;
mod mem;
#[cfg(any(feature = "std", test))]
mod net;
mod num;
mod ops;
mod panic;
#[cfg(all(any(feature = "std", test), any(unix, target_os = "wasi")))]
mod path;
mod pin;
mod primitive;
#[cfg(all(any(feature = "std", test), unix))]
mod process;
mod ptr;
#[cfg(any(feature = "alloc", test))]
mod string;
mod sync;
#[cfg(any(feature = "alloc", test))]
mod vec;
pub unsafe trait Pessimize {
fn hide(self) -> Self;
fn assume_read(&self);
fn assume_accessed(&mut self);
fn assume_accessed_imut(&self);
}
#[inline]
pub fn hide<T: Pessimize>(x: T) -> T {
Pessimize::hide(x)
}
#[inline]
pub fn assume_read<T: Pessimize>(x: &T) {
Pessimize::assume_read(x)
}
#[inline]
pub fn consume<T: Pessimize>(x: T) {
assume_read(&x);
}
#[inline]
pub fn assume_globals_read() {
unsafe { core::arch::asm!("", options(preserves_flags, nostack, readonly)) }
}
#[inline]
pub fn assume_accessed<R: Pessimize>(r: &mut R) {
Pessimize::assume_accessed(r)
}
#[inline]
pub fn assume_accessed_imut<R: Pessimize>(r: &R) {
Pessimize::assume_accessed_imut(r)
}
#[inline]
pub fn assume_globals_accessed() {
unsafe { core::arch::asm!("", options(preserves_flags, nostack)) }
}
pub unsafe trait PessimizeCast {
type Pessimized: Pessimize;
fn into_pessimize(self) -> Self::Pessimized;
unsafe fn from_pessimize(x: Self::Pessimized) -> Self;
}
pub trait BorrowPessimize: PessimizeCast {
type BorrowedPessimize: Pessimize;
fn with_pessimize(&self, f: impl FnOnce(&Self::BorrowedPessimize));
fn assume_accessed_impl(&mut self);
}
#[inline]
pub fn impl_with_pessimize_via_copy<T: Copy + PessimizeCast>(
self_: &T,
f: impl FnOnce(&T::Pessimized),
) {
let pessimize = T::into_pessimize(*self_);
f(&pessimize)
}
#[inline]
pub fn impl_assume_accessed_via_extract_pessimized<T: PessimizeCast>(
self_: &mut T,
extract_pessimized: impl FnOnce(&mut T) -> T::Pessimized,
) {
let mut pessimize = extract_pessimized(self_);
assume_accessed(&mut pessimize);
unsafe { (self_ as *mut T).write(T::from_pessimize(pessimize)) };
}
#[inline]
pub fn impl_assume_accessed_via_extract_self<T: PessimizeCast>(
self_: &mut T,
extract_self: impl FnOnce(&mut T) -> T,
) {
impl_assume_accessed_via_extract_pessimized(self_, |self_| {
T::into_pessimize(extract_self(self_))
});
}
unsafe impl<T: BorrowPessimize> Pessimize for T {
#[inline]
fn hide(self) -> Self {
unsafe { Self::from_pessimize(hide(self.into_pessimize())) }
}
#[inline]
fn assume_read(&self) {
Self::with_pessimize(self, assume_read)
}
#[inline]
fn assume_accessed(&mut self) {
Self::assume_accessed_impl(self)
}
#[inline]
fn assume_accessed_imut(&self) {
Self::with_pessimize(self, assume_accessed_imut)
}
}
#[cfg(feature = "default_impl")]
mod default_impl {
use super::*;
#[doc(cfg(all(feature = "nightly", feature = "default_impl")))]
unsafe impl<T> Pessimize for T {
#[inline]
default fn hide(mut self) -> Self {
let mut r: &mut Self = &mut self;
assume_accessed::<&mut T>(&mut r);
self
}
#[inline]
default fn assume_read(&self) {
consume::<&T>(self)
}
#[inline]
default fn assume_accessed(mut self: &mut Self) {
assume_accessed::<&mut T>(&mut self);
}
#[inline]
default fn assume_accessed_imut(&self) {
assume_accessed_imut::<&T>(&self)
}
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_asm_values {
(
$doc_cfg:meta
{
$(
$reg:ident : ( $($value_type:ty),* )
),*
}
) => {
$($(
#[allow(asm_sub_register)]
#[cfg_attr(feature = "nightly", $doc_cfg)]
unsafe impl $crate::Pessimize for $value_type {
#[inline]
fn hide(mut self) -> Self {
unsafe {
core::arch::asm!("/* {0} */", inout($reg) self, options(preserves_flags, nostack, nomem));
}
self
}
#[inline]
fn assume_read(&self) {
unsafe {
core::arch::asm!("/* {0} */", in($reg) *self, options(preserves_flags, nostack, nomem))
}
}
#[inline]
fn assume_accessed(&mut self) {
Self::assume_read(self)
}
#[inline]
fn assume_accessed_imut(&self) {
Self::assume_read(self)
}
}
)*)*
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_cast {
(
$doc_cfg:meta
{
$(
$inner:ty : (
$(
$(
| $param:ident $( : ( $trait1:path $(, $traitN:path)* ) )? |
)?
$outer:ty : ($into:expr, $from:expr)
),*
)
),*
}
) => {
$($(
#[cfg_attr(feature = "nightly", $doc_cfg)]
unsafe impl $(< $param $( : $trait1 $( + $traitN )* )? >)? $crate::PessimizeCast for $outer {
type Pessimized = $inner;
#[allow(clippy::redundant_closure_call)]
#[inline]
fn into_pessimize(self) -> $inner {
$into(self)
}
#[allow(clippy::redundant_closure_call)]
#[inline]
unsafe fn from_pessimize(inner: $inner) -> Self {
$from(inner)
}
}
)*)*
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_extractible {
(
$doc_cfg:meta
{
$(
$inner:ty : (
$(
$(
| $param:ident $( : ( $trait1:path $(, $traitN:path)* ) )? |
)?
$outer:ty : ($into:expr, $from:expr, $extract:expr)
),*
)
),*
}
) => {
$crate::pessimize_cast!(
$doc_cfg
{
$(
$inner : (
$(
$(
| $param $( : ( $trait1 $(, $traitN)* ) )? |
)?
$outer : ($into, $from)
),*
)
),*
}
);
$($(
#[cfg_attr(feature = "nightly", $doc_cfg)]
impl $(< $param $( : $trait1 $( + $traitN )* )? >)? $crate::BorrowPessimize for $outer {
type BorrowedPessimize = $inner;
#[allow(clippy::redundant_closure_call)]
#[inline]
fn with_pessimize(&self, f: impl FnOnce(&$inner)) {
f(&$extract(self))
}
#[allow(clippy::redundant_closure_call)]
#[inline]
fn assume_accessed_impl(&mut self) {
$crate::impl_assume_accessed_via_extract_pessimized(self, |self_: &mut Self| $extract(self_))
}
}
)*)*
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_copy {
(
$doc_cfg:meta
{
$(
$inner:ty : (
$(
$(
| $param:ident $( : $traits:tt )? |
)?
$outer:ty : ($into:expr, $from:expr)
),*
)
),*
}
) => {
$crate::pessimize_extractible!(
$doc_cfg
{
$(
$inner : (
$(
$(
| $param $( : $traits )? |
)?
$outer : ($into, $from, |self_: &Self| $into(*self_))
),*
)
),*
}
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_into_from {
(
$doc_cfg:meta
{
$(
$inner:ty : (
$(
$( | $param:ident $( : $traits:tt )? | )?
$outer:ty
),*
)
),*
}
) => {
$crate::pessimize_copy!(
$doc_cfg
{
$(
$inner : (
$(
$( | $param $( : $traits )? | )?
$outer : (Self::into, Self::from)
),*
)
),*
}
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_zsts {
(
$doc_cfg:meta
{
$(
$( | $param:ident $( : $traits:tt )? | )?
$name:ty : $make:expr
),*
}
) => {
$crate::pessimize_extractible!(
$doc_cfg
{
() : (
$(
$( | $param $( : $traits )? | )?
$name : (
|_self| (),
|()| $make,
|_self| ()
)
),*
)
}
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_tuple_structs {
(
$doc_cfg:meta
{
$(
$( | $param:ident $( : $traits:tt )? | )?
$outer:ty {
$( $name:ident : $inner:ty ),*
}
),*
}
) => {
$crate::pessimize_copy!(
$doc_cfg
{
$(
( $($inner,)* ) : (
$( | $param $( : $traits )? | )?
$outer : (
|Self( $($name),* )| ( $($name,)* ),
|( $($name,)* )| Self( $($name),* )
)
)
),*
}
);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_newtypes {
(
$doc_cfg:meta
{
$(
$(
| $param:ident $( : ( $trait1:path $(, $traitN:path)* ) )? |
)?
$outer:ty { $inner:ty }
),*
}
) => {
$crate::pessimize_cast!(
$doc_cfg
{
$(
$inner : (
$(
| $param $( : ( $trait1 $(, $traitN)* ) )? |
)?
$outer : (
|Self(inner)| inner,
|inner| Self(inner)
)
)
),*
}
);
$(
#[cfg_attr(feature = "nightly", $doc_cfg)]
impl $(< $param $( : $trait1 $( + $traitN )* )? >)? $crate::BorrowPessimize for $outer {
type BorrowedPessimize = $inner;
#[inline]
fn with_pessimize(&self, f: impl FnOnce(&$inner)) {
f(&self.0)
}
#[inline]
fn assume_accessed_impl(&mut self) {
$crate::assume_accessed(&mut self.0)
}
}
)*
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_once_like {
(
$doc_cfg:meta
{
$(
$(
| $param:ident $( : ( $trait1:path $(, $traitN:path)* ) )? |
)?
$outer:ty : (
$inner:ty,
$extract:expr,
$make:expr
)
),*
}
) => {
$crate::pessimize_cast!(
$doc_cfg
{
$(
$inner : (
$(
| $param $( : ( $trait1 $(, $traitN)* ) )? |
)?
$outer : (|mut self_| $extract(&mut self_), $make)
)
),*
}
);
$(
#[cfg_attr(feature = "nightly", $doc_cfg)]
impl $(< $param $( : $trait1 $( + $traitN )* )? >)? $crate::BorrowPessimize for $outer {
type BorrowedPessimize = *const Self;
#[inline]
fn with_pessimize(&self, f: impl FnOnce(&Self::BorrowedPessimize)) {
f(&(self as *const Self))
}
#[allow(clippy::redundant_closure_call)]
#[inline]
fn assume_accessed_impl(&mut self) {
let mut value = $extract(self);
$crate::assume_accessed(&mut value);
*self = $make(value)
}
}
)*
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! pessimize_collections {
(
$doc_cfg:meta
{
$(
($owned_inner:ty, $borrowed_inner:ty) : (
$(
$(
| $param:ident $( : ( $trait1:path $(, $traitN:path)* ) )? |
)?
$outer:ty : ($into_owned:expr, $from_owned:expr, $extract_borrowed:expr)
),*
)
),*
}
) => {
$crate::pessimize_cast!(
$doc_cfg
{
$(
$owned_inner : (
$(
$(
| $param $( : ( $trait1 $(, $traitN)* ) )? |
)?
$outer : (
$into_owned,
|owned| {
$crate::assume_globals_accessed();
$from_owned(owned)
}
)
),*
)
),*
}
);
$($(
#[cfg_attr(feature = "nightly", $doc_cfg)]
impl $(< $param $( : $trait1 $( + $traitN )* )? >)? $crate::BorrowPessimize for $outer {
type BorrowedPessimize = $borrowed_inner;
#[allow(clippy::redundant_closure_call)]
#[inline]
fn with_pessimize(&self, f: impl FnOnce(&$borrowed_inner)) {
f(&$extract_borrowed(self))
}
#[allow(clippy::redundant_closure_call)]
#[inline]
fn assume_accessed_impl(&mut self) {
$crate::assume_globals_accessed();
$crate::impl_assume_accessed_via_extract_pessimized(self, |self_: &mut Self| $into_owned(core::mem::take(self_)))
}
}
)*)*
};
}
#[doc(hidden)]
pub static mut TEST_GLOBAL_STATE: isize = -42;
#[cfg(test)]
pub(crate) mod tests {
use super::*;
use crate::ptr::tests::{test_all_pinned_pointers, test_unpinned_pointers};
#[cfg(feature = "nightly")]
use std::simd::{Simd, SimdElement};
use std::{
fmt::Debug,
time::{Duration, Instant},
};
pub fn test_pinned_value<T: Clone + Debug + PartialEq + Pessimize>(x: T) {
let old_x = x.clone();
assume_read(&x);
assert_eq!(x, old_x);
assert_eq!(hide(x.clone()), old_x);
test_all_pinned_pointers::<T, _>(x.clone());
consume(x);
}
pub fn test_value<T: Clone + Debug + PartialEq + Pessimize + Unpin>(x: T) {
test_unpinned_pointers(x.clone());
test_pinned_value(x);
}
pub fn test_value_type<T: Clone + Debug + Default + PartialEq + Pessimize + Unpin>(
min: T,
max: T,
) {
test_value(min);
test_value(T::default());
test_value(max);
}
#[allow(unused)]
pub fn test_simd<
Scalar: Copy + Default,
const LANES: usize,
T: Copy + Debug + Default + From<[Scalar; LANES]> + PartialEq + Pessimize + Unpin,
>(
min: Scalar,
max: Scalar,
) {
test_value_type(T::from([min; LANES]), T::from([max; LANES]));
}
#[allow(unused)]
#[cfg(feature = "nightly")]
pub fn test_portable_simd<
Scalar: Debug + Default + PartialEq + SimdElement + Unpin,
const LANES: usize,
>(
min: Scalar,
max: Scalar,
) where
Simd<Scalar, LANES>: Pessimize,
{
test_simd::<Scalar, LANES, Simd<Scalar, LANES>>(min, max)
}
const MAX_SUPERSCALAR_WAYS: u64 = 4;
const MAX_LOOP_FREQ: u64 = MAX_SUPERSCALAR_WAYS * 10_000_000_000;
const MIN_DURATION: Duration = Duration::from_millis(2);
const MIN_ITERATIONS: u64 = 2 * MAX_LOOP_FREQ * MIN_DURATION.as_nanos() as u64 / 1_000_000_000;
fn time_loop(mut op: impl FnMut(u64)) -> Duration {
let start = Instant::now();
for iter in 0..MIN_ITERATIONS {
op(iter);
}
start.elapsed()
}
fn checked_empty_loop_duration() -> Duration {
#[cfg(target_pointer_width = "64")]
let elapsed = time_loop(consume);
#[cfg(not(target_pointer_width = "64"))]
let elapsed = time_loop(|iter| consume(&iter));
assert!(elapsed >= MIN_DURATION);
elapsed
}
pub fn assert_unoptimized<T>(input: T, mut op: impl FnMut(T) -> T) -> T {
let mut opt = Some(input);
let elapsed = time_loop(|_iter| {
let mut input = opt.take().unwrap();
for _ in 0..=MAX_SUPERSCALAR_WAYS {
input = op(input);
}
opt = Some(input);
});
let elapsed_empty = checked_empty_loop_duration();
let ratio = elapsed.as_secs_f64() / elapsed_empty.as_secs_f64();
assert!(ratio > 1.9);
eprintln!(
"Operation pessimized (running at {:.1}x empty iteration speed of {:.1} GHz)",
(MAX_SUPERSCALAR_WAYS + 1) as f64 / ratio,
MIN_ITERATIONS as f64 / elapsed_empty.as_nanos() as f64
);
opt.take().unwrap()
}
pub fn test_unoptimized_value<T: Clone + PartialEq + Pessimize>(x: T) {
let old_x = x.clone();
assert_unoptimized(x, |mut x| {
x = hide(x);
consume(x == old_x);
x
});
}
pub fn test_unoptimized_value_type<T: Clone + Default + PartialEq + Pessimize>() {
test_unoptimized_value(T::default());
}
pub fn test_unoptimized_stateful_zsv<T: Pessimize>(x: T) {
let old_state = unsafe { TEST_GLOBAL_STATE };
assert_unoptimized(x, |mut x| {
x = hide(x);
unsafe { consume(TEST_GLOBAL_STATE == old_state) };
x
});
}
pub fn test_unoptimized_stateful_zst<T: Default + Pessimize>() {
test_unoptimized_stateful_zsv(T::default())
}
pub const BIG: usize = 2;
#[test]
fn non_native() {
#[cfg(feature = "default_impl")]
test_value_type::<[isize; BIG]>([isize::MIN; BIG], [isize::MAX; BIG]);
#[cfg(not(feature = "default_impl"))]
for inner in [isize::MIN, 0, isize::MAX] {
crate::ptr::tests::test_all_pointers::<[isize; BIG], _>([inner; BIG]);
}
}
#[test]
#[ignore]
fn non_native_optim() {
#[cfg(feature = "default_impl")]
test_unoptimized_value_type::<[isize; BIG]>();
#[cfg(not(feature = "default_impl"))]
crate::ptr::tests::test_unoptimized_ptrs::<[isize; BIG], _>([0isize; BIG]);
}
}