Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Start benchmarking #122

Merged
merged 3 commits into from
Feb 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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