使用asParallel vs async函数()对集合进行迭代



同时尝试了解如何使用并行性和并发性在对三种不同方法进行基准测试时,我比较了登录列表所需的时间但这三种方法现在让我非常困惑:

我在问这个代码是怎么回事?

  • 第一种方法是同步
  • 第二次同步用作并行
  • 3rd mehtod is async使用yeild的正常循环
public class LookUpCollections
{
private const int N = 3;
private readonly List<int> _list;
public LookUpCollections()
{
_list = new List<int>();
for (int i = 0; i < N; i++)
{
_list.Add(i);
}
}
[Benchmark]
public void ListLookup() => _list.ForEach(x => Thread.Sleep(TimeSpan.FromSeconds(2)));
[Benchmark]
public void ListLookupAsParallel() => _list.AsParallel<int>().ForAll((x) =>  
Thread.Sleep(TimeSpan.FromSeconds(2)));
[Benchmark]
public async IAsyncEnumerable<int> ListLookupAsync()
{
foreach (var item in _list)
{
Task.Delay(TimeSpan.FromSeconds(2)); // the method will complete before 2sec delay 
await Task.Delay(TimeSpan.FromSeconds(2)); // some asynchronous work
yield return item;
}
}
}

这是的基准结果

或者这是控制台的副本和过去,以防图像出现问题:

BenchmarkDotNet=v0.12.1, OS=Windows 10.0.19041.450
.NET Core SDK=3.1.401
[Host]    : .NET Core 2.1.21 
DefaultJob : .NET Core 2.1.21
                                                                                                                                
Method                  Mean              Error             StdDev  
|--------------------- |--------------------:|-----------------:|-----------------:|
|           ListLookup | 6,027,116,440.00 ns | 6,926,407.532 ns | 6,478,965.903 ns |
| ListLookupAsParallel | 2,008,655,313.33 ns | 5,275,609.028 ns | 4,934,807.958 ns |
|      ListLookupAsync |            27.47 ns |         0.611 ns |         1.632 ns |                                                                                                          
LookUpCollections.ListLookupAsync: Default -> 6 outliers were removed (37.39 ns..40.15 ns) 
// * Legends * 
Mean   : Arithmetic mean of all measurements
Error  : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
1 ns   : 1 Nanosecond (0.000000001 sec)    

根据您的评论,您对第三种方法的结果有一些疑问。我问过你如何调用这个方法,因为如果像这样正确调用,它确实需要大约6秒:

List<int> _list;
async Task Main()
{
const int N = 3;

_list = new List<int>();
for (int i = 0; i < N; i++)
{
_list.Add(i);
}

await foreach(var i in ListLookupAsync())
{
Console.WriteLine(i);   
}
}
public async IAsyncEnumerable<int> ListLookupAsync()
{
Console.WriteLine($"{DateTime.Now} - Entering ListLookupAsync");

foreach (var item in _list)
{
await Task.Delay(TimeSpan.FromSeconds(2)); // some asynchronous work
yield return item;
}

Console.WriteLine($"{DateTime.Now} - Exiting ListLookupAsync");
}

我真的怀疑你量错了东西。基本上,消耗一种IAsyncEnumerable确实与消耗一个不可用的可枚举对象具有相同的效果:迭代/收益返回是按顺序执行的。由于_list包含3个项目,使用上面的代码消耗可枚举项需要3倍的2秒延迟。

如果您想并行执行基于任务的方法,您可以利用Task.WhenAll:

List<int> _list;
async Task Main()
{
const int N = 3;

_list = new List<int>();
for (int i = 0; i < N; i++)
{
_list.Add(i);
}

Console.WriteLine($"{DateTime.Now} - Before WhenAll");
await Task.WhenAll(_list.Select(TaskBased));
Console.WriteLine($"{DateTime.Now} - After WhenAll");
}
public async Task TaskBased(int index)
{
await Task.Delay(TimeSpan.FromSeconds(2)); // some asynchronous work
Console.WriteLine(index);
}

这大约需要2秒钟。

但是,不管怎样:在质疑基准测试的结果之前,请绝对确保您了解代码的作用,否则您可能会测量错误的东西并得出错误的结论。

最新更新