I am trying to find the fastest way to apply some simple calculation on values within an IEnumerable<IPoint>
.
IPoint
being an interface with two parameters decimal Quantity
and decimal Price
.
I also have a Point
class that implements the interface such as :
public class Point : IPoint
{
public decimal Quantity { get; set; }
public decimal Price { get; set; }
public Point(decimal q, decimal p)
{
Quantity = q;
Price = p;
}
}
I want to transform my IEnumerable<IPoint>
such that the result would be an IEnumerable<IPoint>
where quantity = price * quantity
and price = 1 / price
I compared the results of using IEnumerable.Select
and a simple foreach
iteration over the elements of the IEnumerable
.
I randomly generated a IEnumerable<IPoint> Initial
and transform it with the following:
IEnumerable<IPoint> WithSelect = new List<Point>();
IEnumerable<IPoint> WithForeach = new List<Point>();
//With Select
var watch = System.Diagnostics.Stopwatch.StartNew();
WithSelect = Initial.Select(p => new Point(p.Price * p.Quantity, 1 / p.Price));
watch.Stop();
var elapsed1Ms = watch.ElapsedMilliseconds;
var ListTmp = new List<Point>();
//With foreach
watch.Restart();
foreach(var p in Initial)
{
ListTmp.Add(new Point(p.Price * p.Quantity, 1 / p.Price));
}
WithForeach = ListTmp;
watch.Stop();
var elapsed2Ms = watch.ElapsedMilliseconds;
The performance are:
foreach
: ~460 ms for 1M points and ~3800ms for 10M pointsSelect
: 0ms for 1M and 10M points....
If I do a WithSelect = WithSelect.ToList()
to have a more "usable" type (i.e. going from System.Linq.Enumerable.SelectEnumerableIterator<IPoint, Point>
to System.Collections.Generic.List<IPoint>
) the execution time goes up to ~3900 ms (i.e. slightly more than the for each).
I would say great, let's go for the Select
without transforming the result, but I wonder if the execution time (0ms, no matter the size of the Enumerable) is not hiding something. Does it mean that the actual calculation is only made when I access the values ? Would it also mean that if I access several time the values I'll start stacking up the calculation time ? Or is it just a very good Linq optimization that I should not worry about ?
In case the Select is hiding some extra later calculation times, is there an alternative to the two I tried that I should look for ?