Improve futex performance
1. Enlarge the size of the futex buckets; 2. Wake up the waiting threads in one ocall.
This commit is contained in:
parent
2400cc4baa
commit
9b17ac1847
@ -187,12 +187,11 @@ pub fn futex_requeue(
|
|||||||
Ok(nwakes)
|
Ok(nwakes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure futex bucket count is the power of 2
|
|
||||||
const BUCKET_COUNT: usize = 1 << 8;
|
|
||||||
const BUCKET_MASK: usize = BUCKET_COUNT - 1;
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref FUTEX_BUCKETS: FutexBucketVec = { FutexBucketVec::new(BUCKET_COUNT) };
|
// Use the same count as linux kernel to keep the same performance
|
||||||
|
static ref BUCKET_COUNT: usize = ((1 << 8) * (*crate::sched::NCORES)).next_power_of_two();
|
||||||
|
static ref BUCKET_MASK: usize = *BUCKET_COUNT - 1;
|
||||||
|
static ref FUTEX_BUCKETS: FutexBucketVec = { FutexBucketVec::new(*BUCKET_COUNT) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Copy, Clone)]
|
#[derive(PartialEq, Copy, Clone)]
|
||||||
@ -227,7 +226,7 @@ impl FutexItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn wake(&self) {
|
pub fn wake(&self) {
|
||||||
self.waiter.wake()
|
self.waiter().wake()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wait(&self, timeout: &Option<timespec_t>) -> Result<()> {
|
pub fn wait(&self, timeout: &Option<timespec_t>) -> Result<()> {
|
||||||
@ -239,6 +238,15 @@ impl FutexItem {
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn waiter(&self) -> &WaiterRef {
|
||||||
|
&self.waiter
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn batch_wake(items: &[FutexItem]) {
|
||||||
|
let waiters: Vec<&WaiterRef> = items.iter().map(|item| item.waiter()).collect();
|
||||||
|
Waiter::batch_wake(&waiters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct FutexBucket {
|
struct FutexBucket {
|
||||||
@ -266,19 +274,23 @@ impl FutexBucket {
|
|||||||
self.queue.swap_remove_back(item_i.unwrap())
|
self.queue.swap_remove_back(item_i.unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: consider using std::future to improve the readability
|
||||||
pub fn dequeue_and_wake_items(&mut self, key: FutexKey, max_count: usize) -> usize {
|
pub fn dequeue_and_wake_items(&mut self, key: FutexKey, max_count: usize) -> usize {
|
||||||
let mut count = 0;
|
let mut count = 0;
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
|
let mut items_to_wake = Vec::new();
|
||||||
while count < max_count && idx < self.queue.len() {
|
while count < max_count && idx < self.queue.len() {
|
||||||
if key == self.queue[idx].key {
|
if key == self.queue[idx].key {
|
||||||
if let Some(item) = self.queue.swap_remove_back(idx) {
|
if let Some(item) = self.queue.swap_remove_back(idx) {
|
||||||
item.wake();
|
items_to_wake.push(item);
|
||||||
count += 1;
|
count += 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FutexItem::batch_wake(&items_to_wake);
|
||||||
count
|
count
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,7 +347,7 @@ impl FutexBucketVec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_bucket(&self, key: FutexKey) -> (usize, FutexBucketRef) {
|
pub fn get_bucket(&self, key: FutexKey) -> (usize, FutexBucketRef) {
|
||||||
let idx = BUCKET_MASK & {
|
let idx = *BUCKET_MASK & {
|
||||||
// The addr is the multiples of 4, so we ignore the last 2 bits
|
// The addr is the multiples of 4, so we ignore the last 2 bits
|
||||||
let addr = key.addr() >> 2;
|
let addr = key.addr() >> 2;
|
||||||
let mut s = DefaultHasher::new();
|
let mut s = DefaultHasher::new();
|
||||||
@ -377,10 +389,35 @@ impl Waiter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn wake(&self) {
|
pub fn wake(&self) {
|
||||||
if self.is_woken.fetch_or(true, Ordering::SeqCst) == false {
|
if self.is_woken().fetch_or(true, Ordering::SeqCst) == false {
|
||||||
set_event(self.thread);
|
set_events(&[self.thread])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn thread(&self) -> *const c_void {
|
||||||
|
self.thread
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_woken(&self) -> &AtomicBool {
|
||||||
|
&self.is_woken
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn batch_wake(waiters: &[&WaiterRef]) {
|
||||||
|
let threads: Vec<*const c_void> = waiters
|
||||||
|
.iter()
|
||||||
|
.filter_map(|waiter| {
|
||||||
|
// Only wake up items that are not woken.
|
||||||
|
// Set the item to be woken if it is not woken.
|
||||||
|
if waiter.is_woken().fetch_or(true, Ordering::SeqCst) == false {
|
||||||
|
Some(waiter.thread())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
set_events(&threads);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Waiter {
|
impl PartialEq for Waiter {
|
||||||
@ -425,15 +462,24 @@ fn wait_event_timeout(thread: *const c_void, timeout: &Option<timespec_t>) -> Re
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_event(thread: *const c_void) {
|
fn set_events(threads: &[*const c_void]) {
|
||||||
|
if threads.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let mut ret: c_int = 0;
|
let mut ret: c_int = 0;
|
||||||
let mut sgx_ret: c_int = 0;
|
let sgx_ret = unsafe {
|
||||||
unsafe {
|
sgx_thread_set_multiple_untrusted_events_ocall(
|
||||||
sgx_ret = sgx_thread_set_untrusted_event_ocall(&mut ret as *mut c_int, thread);
|
&mut ret as *mut c_int,
|
||||||
}
|
threads.as_ptr(),
|
||||||
if ret != 0 || sgx_ret != 0 {
|
threads.len(),
|
||||||
panic!("ERROR: sgx_thread_set_untrusted_event_ocall failed");
|
)
|
||||||
}
|
};
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
ret == 0 && sgx_ret == 0,
|
||||||
|
"ERROR: sgx_thread_set_multiple_untrusted_events_ocall failed"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -446,7 +492,9 @@ extern "C" {
|
|||||||
errno: *mut c_int,
|
errno: *mut c_int,
|
||||||
) -> c_int;
|
) -> c_int;
|
||||||
|
|
||||||
/* Wake a thread waiting on its untrusted event */
|
fn sgx_thread_set_multiple_untrusted_events_ocall(
|
||||||
fn sgx_thread_set_untrusted_event_ocall(ret: *mut c_int, waiter_thread: *const c_void)
|
ret: *mut c_int,
|
||||||
-> c_int;
|
waiters: *const *const c_void,
|
||||||
|
total: usize,
|
||||||
|
) -> c_int;
|
||||||
}
|
}
|
||||||
|
@ -117,7 +117,7 @@ impl Index<usize> for CpuSet {
|
|||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// The number of all CPU cores on the platform
|
/// The number of all CPU cores on the platform
|
||||||
static ref NCORES: usize = {
|
pub static ref NCORES: usize = {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn occlum_ocall_ncores(ret: *mut i32) -> sgx_status_t;
|
fn occlum_ocall_ncores(ret: *mut i32) -> sgx_status_t;
|
||||||
}
|
}
|
||||||
|
@ -5,5 +5,6 @@ mod do_sched_yield;
|
|||||||
mod sched_agent;
|
mod sched_agent;
|
||||||
mod syscalls;
|
mod syscalls;
|
||||||
|
|
||||||
|
pub use cpu_set::NCORES;
|
||||||
pub use sched_agent::SchedAgent;
|
pub use sched_agent::SchedAgent;
|
||||||
pub use syscalls::*;
|
pub use syscalls::*;
|
||||||
|
Loading…
Reference in New Issue
Block a user