use dever_runtime::async_stream::AsyncStream; use dever_runtime::channel::Channel; use dever_runtime::task::{Group, Limits, Runtime, Scope, Task}; struct ConcurrencyCase { runtime: Runtime, values: Vec, limit: i64, } fn concurrency_fault() -> Fault { &FaultInfo { message: "extended concurrency failed", source: "extended-concurrency-reference", line: 1, column: 1, } } type ChannelOutput = (Vec>, bool); fn channel_generated(values: &[ConcurrencyCase]) -> Result { let Some(value) = values.first() else { return Ok((Vec::new(), true)); }; value .runtime .run(async |scope| EXT_CHANNEL(&value.values, value.limit, scope).await) .map_err(|_| concurrency_fault())? .map_err(|_| concurrency_fault()) } async fn send_values(values: Vec, channel: Channel) -> Result<(), Fault> { for value in values { channel.send(value).await.map_err(|_| concurrency_fault())?; } channel.close().await.map_err(|_| concurrency_fault()) } fn channel_reference(values: &[ConcurrencyCase]) -> Result { let Some(value) = values.first() else { return Ok((Vec::new(), true)); }; value .runtime .run(async |scope| { let channel = Channel::new(value.limit).map_err(|_| concurrency_fault())?; let producer = channel.clone(); let owned = value.values.clone(); let pending = scope .spawn_leaf(move || send_values(owned, producer)) .map_err(|_| concurrency_fault())?; let mut received = Vec::with_capacity(value.values.len()); for _ in &value.values { received.push(channel.receive().await.map_err(|_| concurrency_fault())?); } pending.wait().await.map_err(|_| concurrency_fault())??; let ended = channel .receive() .await .map_err(|_| concurrency_fault())? .is_none(); Ok((received, ended)) }) .map_err(|_| concurrency_fault())? } fn channel_score(value: &ChannelOutput) -> u64 { value.0.iter().fold(u64::from(value.1), |score, item| { score .wrapping_mul(31) .wrapping_add(item.unwrap_or(0) as u64) }) } async fn stream_total(stream: &AsyncStream, limit: Option) -> Result { let _close = stream.close_on_drop(); let mut total = 0i64; while let Some(value) = stream.next().await.map_err(|_| concurrency_fault())? { total = total.checked_add(value).ok_or_else(concurrency_fault)?; if limit.is_some_and(|limit| value >= limit) { break; } } Ok(total) } fn pull_generated(values: &[ConcurrencyCase]) -> Result<(i64, i64), Fault> { let Some(value) = values.first() else { return Ok((0, 0)); }; value .runtime .run_leaf(EXT_PULL(&value.values)) .map_err(|_| concurrency_fault())? .map_err(|_| concurrency_fault()) } fn prefix_generated(values: &[ConcurrencyCase]) -> Result<(i64, i64), Fault> { let Some(value) = values.first() else { return Ok((0, 0)); }; value .runtime .run_leaf(EXT_PREFIX(&value.values)) .map_err(|_| concurrency_fault())? .map_err(|_| concurrency_fault()) } fn stream_reference(values: &[ConcurrencyCase]) -> Result<(i64, i64), Fault> { let Some(value) = values.first() else { return Ok((0, 0)); }; value .runtime .run_leaf(async { let stream = AsyncStream::values(value.values.clone()); let alias = stream.clone(); let total = stream_total(&stream, EARLY.then_some(8)).await?; let after = stream_total(&alias, None).await?; Ok((total, after)) }) .map_err(|_| concurrency_fault())? } fn stream_score(value: &(i64, i64)) -> u64 { (value.0 as u64) .wrapping_mul(31) .wrapping_add(value.1 as u64) } async fn parallel_reference_inner(value: &ConcurrencyCase, scope: &Scope<'_>) -> Result { let channel = Channel::new(1).map_err(|_| concurrency_fault())?; let consumer = channel.clone(); let pending = scope .spawn_leaf( move || async move { stream_total(&AsyncStream::channel(consumer), None).await }, ) .map_err(|_| concurrency_fault())?; let mut items = value.values.iter().copied(); let group: Group>, Fault> = Group::new(value.limit).map_err(|_| concurrency_fault())?; group .each( || std::future::ready(Ok(items.next())), |item| { let target = channel.clone(); scope.spawn_leaf(move || async move { target.send(item).await.map_err(|_| concurrency_fault()) }) }, ) .await .map_err(|_| concurrency_fault())??; channel.close().await.map_err(|_| concurrency_fault())?; pending.wait().await.map_err(|_| concurrency_fault())? } fn parallel_generated(values: &[ConcurrencyCase]) -> Result { let Some(value) = values.first() else { return Ok(0); }; value .runtime .run(async |scope| EXT_PARALLEL(&value.values, value.limit, scope).await) .map_err(|_| concurrency_fault())? .map_err(|_| concurrency_fault()) } fn parallel_reference(values: &[ConcurrencyCase]) -> Result { let Some(value) = values.first() else { return Ok(0); }; value .runtime .run(async |scope| parallel_reference_inner(value, scope).await) .map_err(|_| concurrency_fault())? } fn new_case(limit: i64) -> ConcurrencyCase { ConcurrencyCase { runtime: Runtime::new(Limits { threads: 2, workers: 2, tasks: 8, }) .unwrap(), values: (1..=256).collect(), limit, } } fn main() { // 两侧共用运行库与固定限额;计时含复制、提交、领取及作用域清理。 let mut case = new_case(1); assert_eq!( case.runtime.run_leaf(EXT_BOUNDARY()).unwrap().unwrap(), (15, Some(9), 0) ); let expected = case.values.iter().sum::(); println!("case,sample,implementation,elapsed_ns,checksum"); for capacity in [1, 64] { case.limit = capacity; let values = std::slice::from_ref(&case); let received = case.values.iter().copied().map(Some).collect::>(); assert_eq!(channel_generated(values).unwrap(), (received.clone(), true)); assert_eq!(channel_reference(values).unwrap(), (received, true)); compare_named( &format!("channel_capacity_{capacity}_shared_runtime"), values, 32, channel_generated, channel_reference, channel_score, "rust_runtime", ); } case.limit = 8; let values = std::slice::from_ref(&case); assert_eq!(pull_generated(values).unwrap(), (expected, 0)); assert_eq!(prefix_generated(values).unwrap(), (36, 0)); compare_named( "async_stream_pull_shared_runtime", values, 128, pull_generated, stream_reference::, stream_score, "rust_runtime", ); compare_named( "async_stream_prefix_shared_runtime", values, 128, prefix_generated, stream_reference::, stream_score, "rust_runtime", ); for limit in [1, 4] { case.limit = limit; let values = std::slice::from_ref(&case); assert_eq!(parallel_generated(values).unwrap(), expected); assert_eq!(parallel_reference(values).unwrap(), expected); compare_named( &format!("parallel_each_limit_{limit}_shared_runtime"), values, 32, parallel_generated, parallel_reference, |value| *value as u64, "rust_runtime", ); } // 真正调用空输入 fixture,区别于 samples 的空用例切片。 case.values.clear(); let empty = std::slice::from_ref(&case); assert_eq!(channel_generated(empty).unwrap(), (Vec::new(), true)); assert_eq!(channel_reference(empty).unwrap(), (Vec::new(), true)); assert_eq!(pull_generated(empty).unwrap(), (0, 0)); assert_eq!(prefix_generated(empty).unwrap(), (0, 0)); assert_eq!(stream_reference::(empty).unwrap(), (0, 0)); assert_eq!(stream_reference::(empty).unwrap(), (0, 0)); assert_eq!(parallel_generated(empty).unwrap(), 0); assert_eq!(parallel_reference(empty).unwrap(), 0); }