Files
core/test/rust/performance/task/scoped.rs
T
2026-11-03 03:32:07 +00:00

105 lines
4.8 KiB
Rust

// 独立 Tokio 参照补齐当前阻塞工作负载的收尾合同;不调用 Dever 运行库。
// 原始轻量参照继续保留,不将本组结果冒充原参照的性能提升。
use futures_util::FutureExt;
use std::panic::{AssertUnwindSafe, catch_unwind};
use tokio_util::task::TaskTracker;
impl NativeTasks {
fn worker_scope<Value>(
&self,
call: impl AsyncFnOnce(&TaskTracker) -> Value,
) -> Result<Value, Fault> {
let tasks = TaskTracker::new();
self.runtime.block_on(async {
let result = AssertUnwindSafe(async { call(&tasks).await }).catch_unwind().await;
tasks.close();
tasks.wait().await;
result.map_err(task_fault)
})
}
async fn tracked_blocking<Value: Send + 'static>(
&self,
tasks: &TaskTracker,
call: impl FnOnce() -> Value + Send + 'static,
) -> Result<Value, Fault> {
if tasks.is_closed() {
return Err(task_fault("closed worker scope"));
}
let permit = self.workers.clone().acquire_owned().await.unwrap();
let token = tasks.token();
let (result, _token) = tokio::task::spawn_blocking(move || {
let _permit = permit;
let result = catch_unwind(AssertUnwindSafe(call)).map_err(task_fault);
// 输出先析构,跟踪才结束;panic 载荷也在通知排空前释放。
(result, token)
}).await.map_err(task_fault)?;
result
}
}
fn reference_worker_scoped<const BLOCKING: bool, const CALLS: usize>(
values: &[TaskCase],
) -> Result<i64, Fault> {
if BLOCKING {
reference_worker_calls::<CALLS, _>(values, |owned| {
native_blocking_sleep(0)?;
native_total(&owned).map_err(NativeAsyncFailure::from)
})
} else {
reference_worker_calls::<CALLS, _>(values, |owned| native_total(&owned))
}
}
fn native_blocking_sleep(millis: i64) -> Result<(), NativeAsyncFailure> {
let fail = |message: &str| NativeAsyncFailure::Sleep(message.to_owned(), task_fault("sleep"));
let millis = u64::try_from(millis).map_err(|_| fail("sleep duration must be nonnegative"))?;
let duration = Duration::from_millis(millis);
std::time::Instant::now().checked_add(duration)
.ok_or_else(|| fail("sleep deadline is out of range"))?;
std::thread::sleep(duration);
Ok(())
}
// 保留实际业务错误及文本负载;不以填充字段强凑与生成代码相同的布局。
fn reference_worker_calls<const CALLS: usize, Failure: From<Fault> + Into<Fault> + Send + 'static>(
values: &[TaskCase],
call: impl FnOnce(Vec<i64>) -> Result<i64, Failure> + Copy + Send + 'static,
) -> Result<i64, Fault> {
let Some(value) = values.first() else { return Ok(0); };
value.native.worker_scope(async |tasks| {
let mut total = 0i64;
for _ in 0..CALLS {
let owned = value.values.clone();
let result = value.native.tracked_blocking(tasks, move || call(owned))
.await.map_err(Failure::from)??;
total = total.checked_add(result)
.ok_or_else(|| Failure::from(task_fault("integer overflow")))?;
}
Ok::<_, Failure>(total)
})?.map_err(Into::into)
}
fn worker_scoped(cases: &[TaskCase]) {
let expected = native_total(&cases[0].values).unwrap();
assert_eq!(reference_worker_scoped::<false, 1>(cases).unwrap(), expected);
assert_eq!(reference_worker_scoped::<true, 1>(cases).unwrap(), expected);
assert_eq!(reference_worker_scoped::<false, CONTINUOUS_CALLS>(cases).unwrap(), expected * CONTINUOUS_CALLS as i64);
assert_eq!(reference_worker_scoped::<true, CONTINUOUS_CALLS>(cases).unwrap(), expected * CONTINUOUS_CALLS as i64);
let failures = [TaskCase {
generated: new_runtime(), native: NativeTasks::new(), values: vec![i64::MAX, 1],
}];
assert!(reference_worker_scoped::<false, 1>(&failures).is_err());
assert!(reference_worker_scoped::<true, 1>(&failures).is_err());
assert!(reference_worker_scoped::<false, CONTINUOUS_CALLS>(&failures).is_err());
assert!(reference_worker_scoped::<true, CONTINUOUS_CALLS>(&failures).is_err());
compare_named("task_parallel_scoped", cases, 500,
generated_parallel, reference_worker_scoped::<false, 1>, |value| *value as u64, "rust_scoped");
compare_named("task_blocking_scoped", cases, 500,
generated_blocking, reference_worker_scoped::<true, 1>, |value| *value as u64, "rust_scoped");
compare_named("task_parallel_scoped_steady", cases, 64,
generated_worker_steady::<false>, reference_worker_scoped::<false, CONTINUOUS_CALLS>, |value| *value as u64, "rust_scoped");
compare_named("task_blocking_scoped_steady", cases, 64,
generated_worker_steady::<true>, reference_worker_scoped::<true, CONTINUOUS_CALLS>, |value| *value as u64, "rust_scoped");
}