Skip to content

Commit

Permalink
Merge pull request #122 from madsmtm/criterion-benchmarking
Browse files Browse the repository at this point in the history
Start benchmarking
  • Loading branch information
madsmtm authored Feb 22, 2022
2 parents f23c3fc + 22dd211 commit 3fc99b5
Show file tree
Hide file tree
Showing 7 changed files with 205 additions and 10 deletions.
13 changes: 11 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,9 @@ jobs:
# This changes a variable, so is only set when a custom SDK is used
run: echo "SDKROOT=$HOME/extern/sdk" >> $GITHUB_ENV

- name: Install Clang
- name: Install Clang & Valgrind
if: contains(matrix.os, 'ubuntu')
run: sudo apt-get -y install clang
run: sudo apt-get -y install clang valgrind

- name: Install cross compilation tools
if: matrix.target == 'i686-unknown-linux-gnu'
Expand Down Expand Up @@ -327,6 +327,15 @@ jobs:
command: test
args: --features ${{ env.FEATURES }} ${{ env.TESTARGS }} --release

- name: Run benchmarks
# Difficult to install Valgrind on macOS
# See https://github.com/LouisBrunner/valgrind-macos
if: contains(matrix.os, 'ubuntu')
uses: actions-rs/cargo@v1
with:
command: bench
args: --bench autorelease ${{ env.TESTARGS }}

- name: Test with unstable features
if: ${{ !matrix.dinghy && matrix.rust.toolchain == 'nightly' }}
uses: actions-rs/cargo@v1
Expand Down
7 changes: 7 additions & 0 deletions objc2/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ objc2-encode = { path = "../objc2-encode", version = "=2.0.0-beta.2" }
[build-dependencies]
cc = { version = "1", optional = true }

[dev-dependencies]
iai = { version = "0.1", git = "https://github.com/madsmtm/iai", branch = "callgrind" }

[[bench]]
name = "autorelease"
harness = false

[package.metadata.docs.rs]
default-target = "x86_64-apple-darwin"

Expand Down
175 changes: 175 additions & 0 deletions objc2/benches/autorelease.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
use core::ffi::c_void;
use std::mem::ManuallyDrop;
use std::ptr::NonNull;

use objc2::ffi;
use objc2::rc::{autoreleasepool, Id, Shared};
use objc2::runtime::{Class, Object, Sel};
use objc2::{class, msg_send, sel};

#[cfg(apple)]
#[link(name = "Foundation", kind = "framework")]
extern "C" {}

#[cfg(gnustep)]
#[link(name = "gnustep-base", kind = "dylib")]
extern "C" {}

const BYTES: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

fn empty() {
#[cfg(gnustep)]
unsafe {
objc2::__gnustep_hack::get_class_to_force_linkage()
};
}

fn pool_cleanup() {
autoreleasepool(|_| {})
}

fn class() -> &'static Class {
class!(NSObject)
}

fn sel() -> Sel {
sel!(alloc)
}

fn send_message() -> &'static Class {
unsafe { msg_send![class!(NSObject), class] }
}

fn alloc_nsobject() -> *mut Object {
unsafe { msg_send![class!(NSObject), alloc] }
}

fn new_nsobject() -> Id<Object, Shared> {
let obj = alloc_nsobject();
let obj: *mut Object = unsafe { msg_send![obj, init] };
unsafe { Id::new(NonNull::new_unchecked(obj)) }
}

fn new_nsdata() -> Id<Object, Shared> {
let bytes_ptr = BYTES.as_ptr() as *const c_void;
let obj: *mut Object = unsafe { msg_send![class!(NSData), alloc] };
let obj: *mut Object = unsafe {
msg_send![
obj,
initWithBytes: bytes_ptr,
length: BYTES.len(),
]
};
unsafe { Id::new(NonNull::new_unchecked(obj)) }
}

fn new_leaked_nsdata() -> *mut Object {
ManuallyDrop::new(new_nsdata()).as_ptr()
}

fn autoreleased_nsdata() -> *mut Object {
// let bytes_ptr = BYTES.as_ptr() as *const c_void;
// unsafe {
// msg_send![
// class!(NSData),
// dataWithBytes: bytes_ptr,
// length: BYTES.len(),
// ]
// }
unsafe { msg_send![new_leaked_nsdata(), autorelease] }
}

fn new_nsstring() -> Id<Object, Shared> {
let obj: *mut Object = unsafe { msg_send![class!(NSString), alloc] };
let obj: *mut Object = unsafe { msg_send![obj, init] };
unsafe { Id::new(NonNull::new_unchecked(obj)) }
}

fn new_leaked_nsstring() -> *mut Object {
ManuallyDrop::new(new_nsstring()).as_ptr()
}

fn autoreleased_nsstring() -> *mut Object {
// unsafe { msg_send![class!(NSString), string] }
unsafe { msg_send![new_leaked_nsstring(), autorelease] }
}

fn retain_autoreleased(obj: *mut Object) -> Id<Object, Shared> {
let obj = unsafe { ffi::objc_retainAutoreleasedReturnValue(obj.cast()) };
unsafe { Id::new(NonNull::new_unchecked(obj).cast()) }
}

fn autoreleased_nsdata_pool_cleanup() -> *mut Object {
autoreleasepool(|_| autoreleased_nsdata())
}

fn autoreleased_nsdata_fast_caller_cleanup() -> Id<Object, Shared> {
retain_autoreleased(autoreleased_nsdata())
}

fn autoreleased_nsdata_fast_caller_cleanup_pool_cleanup() -> Id<Object, Shared> {
autoreleasepool(|_| retain_autoreleased(autoreleased_nsdata()))
}

fn autoreleased_nsstring_pool_cleanup() -> *mut Object {
autoreleasepool(|_| autoreleased_nsstring())
}

fn autoreleased_nsstring_fast_caller_cleanup() -> Id<Object, Shared> {
retain_autoreleased(autoreleased_nsstring())
}

fn autoreleased_nsstring_fast_caller_cleanup_pool_cleanup() -> Id<Object, Shared> {
autoreleasepool(|_| retain_autoreleased(autoreleased_nsstring()))
}

macro_rules! main_with_warmup {
($($f:ident,)+) => {
mod warmup_fns {
$(
#[inline(never)]
pub fn $f() {
let _ = iai::black_box(super::$f());
}
)+
}

fn warmup() {
$(
warmup_fns::$f();
)+
}

iai::main! {
warmup,
$(
$f,
)+
}
};
}

main_with_warmup! {
// Baseline
empty,
pool_cleanup,
class,
sel,
send_message,
alloc_nsobject,
new_nsobject,
// NSData
new_nsdata,
new_leaked_nsdata,
autoreleased_nsdata,
autoreleased_nsdata_pool_cleanup,
autoreleased_nsdata_fast_caller_cleanup,
autoreleased_nsdata_fast_caller_cleanup_pool_cleanup,
// NSString
new_nsstring,
new_leaked_nsstring,
autoreleased_nsstring,
autoreleased_nsstring_pool_cleanup,
autoreleased_nsstring_fast_caller_cleanup,
autoreleased_nsstring_fast_caller_cleanup_pool_cleanup,
}
8 changes: 4 additions & 4 deletions objc2/src/rc/autorelease.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ impl AutoreleasePool {
/// Additionally, the pools must be dropped in the same order they were
/// created.
#[doc(alias = "objc_autoreleasePoolPush")]
#[inline]
unsafe fn new() -> Self {
// TODO: Make this function pub when we're more certain of the API
let context = unsafe { ffi::objc_autoreleasePoolPush() };
Expand All @@ -57,10 +58,7 @@ impl AutoreleasePool {
}

/// This will be removed in a future version.
#[cfg_attr(
not(all(debug_assertions, not(feature = "unstable_autoreleasesafe"))),
inline
)]
#[inline]
#[doc(hidden)]
pub fn __verify_is_inner(&self) {
#[cfg(all(debug_assertions, not(feature = "unstable_autoreleasesafe")))]
Expand Down Expand Up @@ -139,6 +137,7 @@ impl Drop for AutoreleasePool {
/// [clang documentation]: https://clang.llvm.org/docs/AutomaticReferenceCounting.html#autoreleasepool
/// [revision `371`]: https://github.com/apple-oss-distributions/objc4/blob/objc4-371/runtime/objc-exception.m#L479-L482
#[doc(alias = "objc_autoreleasePoolPop")]
#[inline]
fn drop(&mut self) {
unsafe { ffi::objc_autoreleasePoolPop(self.context) }
#[cfg(all(debug_assertions, not(feature = "unstable_autoreleasesafe")))]
Expand Down Expand Up @@ -292,6 +291,7 @@ impl !AutoreleaseSafe for AutoreleasePool {}
/// # panic!("Does not panic in release mode, so for testing we make it!");
/// ```
#[doc(alias = "@autoreleasepool")]
#[inline(always)]
pub fn autoreleasepool<T, F>(f: F) -> T
where
for<'p> F: FnOnce(&'p AutoreleasePool) -> T + AutoreleaseSafe,
Expand Down
4 changes: 2 additions & 2 deletions objc2/src/rc/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ impl<T: Message, O: Ownership> Id<T, O> {
// let y: &T = &*retained;
// ```
#[doc(alias = "objc_retain")]
#[cfg_attr(not(debug_assertions), inline)]
#[inline]
pub unsafe fn retain(ptr: NonNull<T>) -> Id<T, O> {
let ptr = ptr.as_ptr() as *mut ffi::objc_object;
// SAFETY: The caller upholds that the pointer is valid
Expand Down Expand Up @@ -266,7 +266,7 @@ impl<T: Message, O: Ownership> Id<T, O> {
NonNull::new(ptr).map(|ptr| unsafe { Id::retain(ptr) })
}

#[cfg_attr(not(debug_assertions), inline)]
#[inline]
fn autorelease_inner(self) -> *mut T {
// Note that this (and the actual `autorelease`) is not an associated
// function. This breaks the guideline that smart pointers shouldn't
Expand Down
1 change: 1 addition & 0 deletions objc2/src/rc/id_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ pub trait DefaultId {
}

impl<T: DefaultId + ?Sized> Default for Id<T, T::Ownership> {
#[inline]
fn default() -> Self {
T::default_id()
}
Expand Down
7 changes: 5 additions & 2 deletions objc2/src/rc/weak_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ use core::convert::TryFrom;
use core::fmt;
use core::marker::PhantomData;
use core::ptr;
use core::ptr::NonNull;
use std::panic::{RefUnwindSafe, UnwindSafe};

use super::{Id, Shared};
Expand Down Expand Up @@ -35,6 +34,7 @@ pub struct WeakId<T: ?Sized> {
impl<T: Message> WeakId<T> {
/// Construct a new [`WeakId`] referencing the given shared [`Id`].
#[doc(alias = "objc_initWeak")]
#[inline]
pub fn new(obj: &Id<T, Shared>) -> Self {
// Note that taking `&Id<T, Owned>` would not be safe since that would
// allow loading an `Id<T, Shared>` later on.
Expand Down Expand Up @@ -68,7 +68,7 @@ impl<T: Message> WeakId<T> {
pub fn load(&self) -> Option<Id<T, Shared>> {
let ptr: *mut *mut ffi::objc_object = self.inner.get() as _;
let obj = unsafe { ffi::objc_loadWeakRetained(ptr) } as *mut T;
NonNull::new(obj).map(|obj| unsafe { Id::new(obj) })
unsafe { Id::new_null(obj) }
}

// TODO: Add `autorelease(&self) -> Option<&T>` using `objc_loadWeak`?
Expand All @@ -77,6 +77,7 @@ impl<T: Message> WeakId<T> {
impl<T: ?Sized> Drop for WeakId<T> {
/// Drops the `WeakId` pointer.
#[doc(alias = "objc_destroyWeak")]
#[inline]
fn drop(&mut self) {
unsafe { ffi::objc_destroyWeak(self.inner.get() as _) }
}
Expand All @@ -101,6 +102,7 @@ impl<T: Message> Default for WeakId<T> {
/// Constructs a new `WeakId<T>` that doesn't reference any object.
///
/// Calling [`Self::load`] on the return value always gives [`None`].
#[inline]
fn default() -> Self {
// SAFETY: The pointer is null
unsafe { Self::new_inner(ptr::null_mut()) }
Expand Down Expand Up @@ -130,6 +132,7 @@ impl<T: RefUnwindSafe + ?Sized> RefUnwindSafe for WeakId<T> {}
impl<T: RefUnwindSafe + ?Sized> UnwindSafe for WeakId<T> {}

impl<T: Message> From<Id<T, Shared>> for WeakId<T> {
#[inline]
fn from(obj: Id<T, Shared>) -> Self {
WeakId::new(&obj)
}
Expand Down

0 comments on commit 3fc99b5

Please sign in to comment.