我想给一些同步代码添加并发性,在这个过程中发现了一个性能问题,很难理解。
以下代码的运行结果为:
Mission Fibonacci1Async cost 9.4195388 seconds, value 75025
Mission Fibonacci2Async cost 0.2260129 seconds, value 75025
唯一不同的是第二个函数添加了一行等待 Task.WhenAll(new Task[] { t1, t2 });,使性能提高了 40 倍。
谁能向我解释一下?
static Task<int> Fibonacci1Async(int n)
{
return Task.Run<int>(() => Fibonacci1(n));
}
static int Fibonacci1(int n)
{
if (n == 0) return 0;
else if (n == 1) return 1;
else
{
var t1 = Fibonacci1Async(n - 1);
var t2 = Fibonacci1Async(n - 2);
return t1.Result + t2.Result;
}
}
static Task<int> Fibonacci2Async(int n)
{
return Task.Run<int>(() => Fibonacci2(n));
}
static int Fibonacci2(int n)
{
if (n == 0) return 0;
else if (n == 1) return 1;
else
{
var t1 = Fibonacci2Async(n - 1);
var t2 = Fibonacci2Async(n - 2);
Task.WaitAll(new Task[] { t1, t2 });
return t1.Result + t2.Result;
}
}
static void Benchmark(Func<int, Task<int>> func)
{
DateTime time = DateTime.Now;
var task = func(25);
task.Wait();
TimeSpan cost = DateTime.Now - time;
Console.WriteLine("Mission {0} cost {1} seconds value {2}", func.Method.Name, cost.TotalSeconds, task.Result);
}
static void Main(string[] args)
{
Benchmark(Fibonacci1Async);
Benchmark(Fibonacci2Async);
Console.ReadKey();
return;
}
我怀疑答案与Task.Wait
内联有关。
在表达式t1.Result + t2.Result
中,+
运算符从左到右(串行)计算其参数。所以它会在t1
上阻塞,然后在t2
上阻塞.
我猜在您的系统上,大部分时间t1
已经开始但不是t2
.在这种情况下,Task.WaitAll
可以"内联"t2
到当前线程池任务中,而不是启动新的线程池任务,但+
将在t1
上阻塞。
这只是一个猜测;你应该使用探查器来准确了解发生了什么。
我无法在我的系统上重现它。我总是看到两个版本大致相同,即使将处理器亲和力应用于该过程也是如此。
附言命名约定Async
在这里并不真正适用。此代码不使用async
/await
- 它使用的是任务并行库。