A C# console application that estimates the value of PI using a variety of both serial and parallel implementations, the latter done with both PLINQ and the Parallel class.
class Program
{
static int Main(string[] args)
{
int returnCode = 0;
RunCalculatePi();
return returnCode;
}
/// <summary>
/// Main method to time various implementations of computing PI.
/// </summary>
public static void RunCalculatePi()
{
ComputePi.Time(() => ComputePi.SerialLinqPi());
ComputePi.Time(() => ComputePi.ParallelLinqPi());
ComputePi.Time(() => ComputePi.SerialPi());
ComputePi.Time(() => ComputePi.ParallelPi());
ComputePi.Time(() => ComputePi.ParallelPartitionerPi());
System.Console.WriteLine("----");
System.Console.ReadLine();
}
}
public static class ComputePi
{
const int num_steps = 100000000;
/// <summary>
/// Times the execution of a function and outputs both
/// the elapsed time and the function's result.
/// </summary>
public static void Time<T>(Func<T> work)
{
var sw = Stopwatch.StartNew();
var result = work();
System.Console.WriteLine(sw.Elapsed + ": " + result);
}
/// <summary>
/// Estimates the value of PI using a LINQ-based
/// implementation.
/// </summary>
public static double SerialLinqPi()
{
double step = 1.0 / (double)num_steps;
return (from i in Enumerable.Range(0, num_steps)
let x = (i + 0.5) * step
select 4.0 / (1.0 + x * x)).Sum() * step;
}
/// <summary>
/// Estimates the value of PI using a PLINQ-based implementation.
/// </summary>
public static double ParallelLinqPi()
{
double step = 1.0 / (double)num_steps;
return (from i in ParallelEnumerable.Range(0, num_steps)
let x = (i + 0.5) * step
select 4.0 / (1.0 + x * x)).Sum() * step;
}
/// <summary>
/// Estimates the value of PI using a for loop.
/// </summary>
public static double SerialPi()
{
double sum = 0.0;
double step = 1.0 / (double)num_steps;
for (int i = 0; i < num_steps; i++)
{
double x = (i + 0.5) * step;
sum = sum + 4.0 / (1.0 + x * x);
}
return step * sum;
}
/// <summary>
/// Estimates the value of PI using a Parallel.For.
/// </summary>
public static double ParallelPi()
{
double sum = 0.0;
double step = 1.0 / (double)num_steps;
object monitor = new object();
Parallel.For(0, num_steps, () => 0.0, (i, state, local) =>
{
double x = (i + 0.5) * step;
return local + 4.0 / (1.0 + x * x);
}, local =>
{
lock (monitor)
{
sum += local;
}
});
return step * sum;
}
/// <summary>
/// Estimates the value of PI using a Parallel.ForEach and a
/// range partitioner.
/// </summary>
public static double ParallelPartitionerPi()
{
double sum = 0.0;
double step = 1.0 / (double)num_steps;
object monitor = new object();
Parallel.ForEach(Partitioner.Create(0, num_steps), () => 0.0, (range, state, local) =>
{
for (int i = range.Item1; i < range.Item2; i++)
{
double x = (i + 0.5) * step;
local += 4.0 / (1.0 + x * x);
}
return local;
}, local =>
{
lock (monitor)
{
sum += local;
}
});
return step * sum;
}
}