I used the code below to run a test for this case. The idea in FibonacciTestAsync is to run multiple computations in parallel.
Answer is divided into a C# 7 and a C# 8 part.
Answer when using C# 7
Test durations with sequence 40, 41, 42, 43, 44, 45:
- FibonacciTestAsync: ~31 seconds
- FibonacciTestNonAsync: ~76 seconds
Please note that these results are significantly slower than results from the asp.net Core-section below.
Environment: Windows 10, VS 2017, .NET Framework 4.6.1, NUnit, Resharper
private static readonly int[] Numbers = { 40, 41, 42, 43, 44, 45 };
private static int GetFibonacci(int n)
{
if (n == 0 || n == 1) return n;
return GetFibonacci(n - 1) + GetFibonacci(n - 2);
}
private static Task<int> GetFibonacciAsync(int n) => Task.Run(() => GetFibonacci(n));
public static IEnumerator<int> GetFibonaccies()
{
foreach (var n in Numbers)
{
var f = GetFibonacci(n); // long job
yield return f; // << please do not be lazy and do not stuck here till next request but calculate next number in background to quickly respond your next request
}
}
// in C# 8: public static async IAsyncEnumerable<int> GetFibonacciesAsync()
public static IEnumerable<int> GetFibonacciesAsync()
{
var taskList = Numbers
.Select(GetFibonacciAsync)
.ToList();
foreach (var task in taskList)
{
// in C# 8: yield return await task;
yield return task.GetAwaiter().GetResult();
}
}
private static readonly IList<int> ExpectedOutput = new List<int>
{
102334155,
165580141,
267914296,
433494437,
701408733,
1134903170
};
[Test]
public void FibonacciTestNonAsync()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<int>();
using (var fibonacciNumberEnumerator = GetFibonaccies())
{
while (fibonacciNumberEnumerator.MoveNext())
{
result.Add(fibonacciNumberEnumerator.Current);
Console.WriteLine(fibonacciNumberEnumerator.Current);
}
}
sw.Stop();
Console.WriteLine("Elapsed={0}", (double)sw.ElapsedMilliseconds / 1000);
Assert.AreEqual(ExpectedOutput, result);
}
[Test]
public void FibonacciTestAsync()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<int>();
// here you can play a little bit:
// Try to replace GetFibonacciesAsync() with GetFibonacciesAsync().Take(1) and observe that the test will run a lot faster
var fibonacciNumbers = GetFibonacciesAsync();
foreach (var item in fibonacciNumbers)
{
result.Add(item);
Console.WriteLine(item);
}
sw.Stop();
Console.WriteLine("Elapsed={0}", (double)sw.ElapsedMilliseconds / 1000);
Assert.AreEqual(ExpectedOutput, result);
}
Answer when using C# 8 or higher
Test durations with sequence 40, 41, 42, 43, 44, 45:
- FibonacciTestAsync: ~11 seconds
- FibonacciTestNonAsync: ~26 seconds
Environment used during testing: Windows 10, VS 2019, asp.net Core 5, NUnit, Resharper
// original array from OP, takes too long too compute private static readonly int[] Numbers = { 45, 43, 76, 23, 54, 22 };
private static readonly int[] Numbers = { 40, 41, 42, 43, 44, 45 };
private static int GetFibonacci(int n)
{
if (n == 0 || n == 1) return n;
return GetFibonacci(n - 1) + GetFibonacci(n - 2);
}
private static Task<int> GetFibonacciAsync(int n) => Task.Run(() => GetFibonacci(n));
public static IEnumerator<int> GetFibonaccies()
{
foreach (int n in Numbers)
{
var f = GetFibonacci(n); // long job
yield return f; // << please do not be lazy and do not stuck here till next request but calculate next number in background to quickly respond your next request
}
}
public static async IAsyncEnumerable<int> GetFibonacciesAsync()
{
var taskList = Numbers
.Select(GetFibonacciAsync) // starting task here
.ToList();
foreach (var task in taskList)
{
yield return await task; // as soon as current task is completed, yield the result
}
}
private static readonly IList<int> ExpectedOutput = new List<int>
{
102334155,
165580141,
267914296,
433494437,
701408733,
1134903170
};
[Test]
public void FibonacciTestNonAsync()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<int>();
using IEnumerator<int> fibonacciNumberEnumerator = GetFibonaccies();
while (fibonacciNumberEnumerator.MoveNext())
{
result.Add(fibonacciNumberEnumerator.Current);
Console.WriteLine(fibonacciNumberEnumerator.Current);
}
sw.Stop();
Console.WriteLine("Elapsed={0}", (double)sw.ElapsedMilliseconds / 1000);
Assert.AreEqual(ExpectedOutput, result);
}
[Test]
public async Task FibonacciTestAsync()
{
var sw = new Stopwatch();
sw.Start();
var result = new List<int>();
var fibonacciNumbers = GetFibonacciesAsync();
await foreach (var item in fibonacciNumbers)
{
result.Add(item);
Console.WriteLine(item);
}
sw.Stop();
Console.WriteLine("Elapsed={0}", (double)sw.ElapsedMilliseconds / 1000);
Assert.AreEqual(ExpectedOutput, result);
}