Search code examples
c#performanceienumerable

Is IEnumerable.Max() the fastest way?


I am developing a part of a software, where I have a list (List<Sample> at the moment) of samples like the following:

public class Sample
{
    //...
    public double ValueChannel1 { get; set; }
    public double ValueChannel2 { get; set; }
    //...
}

These lists have between ~100 and several thousands of these samples and there are around 100k samples per second.
Now I need to find the maximum and minimum value out of each of these lists, which I do the following way at the moment:

var Ch1Max = task.Samples.Max<Sample>(s => s.ValueChannel1);
var Ch1Min = task.Samples.Min<Sample>(s => s.ValueChannel1);
var Ch2Max = task.Samples.Max<Sample>(s => s.ValueChannel2);
var Ch2Min = task.Samples.Min<Sample>(s => s.ValueChannel2);

To no surprise this is not very fast, so I was asking myself if there is something faster to do that, but I could not think of or find one?

Does someone know a faster way to do this? Maybe there is a way to find min and max with "one loop" instead of one for min and one for max?

Edit:
I profiled the current code with the following results:
731 tasks each containing one of these lists needed 845ms to process and 95% of that where the min/max searching.
I have no specific "target time", but as this runs all the time in my app (as it is capturing measurement data) it should cause as little CPU utlization as possible, to keep the hardware requirements as low as possible...

Best solution found:
In the end I choose the soltuion from Tim, as it was even a little faster then Konrad ones:
Tim's solution caused a speed-up of ~53% and Konrads's "only" ~43%.

Final solution (for now):

double Ch1Max = Double.MinValue, Ch1Min = Double.MaxValue;
double Ch2Max = Double.MinValue, Ch2Min = Double.MaxValue;

var samples = task.Samples.ToArray();
int count = samples.Length;
for (int i = 0; i < count; ++i)
{
    var valueChannel1 = samples[i].ValueChannel1; // get only once => faster
    if (valueChannel1 > Ch1Max) Ch1Max = valueChannel1;
    if (valueChannel1 < Ch1Min) Ch1Min = valueChannel1;

    var valueChannel2 = samples[i].ValueChannel2;
    if (valueChannel2 > Ch2Max) Ch2Max = valueChannel2;
    if (valueChannel2 < Ch2Min) Ch2Min = valueChannel2;
}

This sums up to a speed up of ~70% compared to my initial solution...


Solution

  • You can use a single loop:

    double Ch1Max = double.MinValue;
    double Ch1Min = double.MaxValue;
    double Ch2Max = double.MinValue;
    double Ch2Min = double.MaxValue;
    foreach(Sample s in samples)
    {
        if(s.ValueChannel1 > Ch1Max) Ch1Max = s.ValueChannel1;
        if(s.ValueChannel1 < Ch1Min) Ch1Min = s.ValueChannel1;
        if(s.ValueChannel2 > Ch2Max) Ch2Max = s.ValueChannel2;
        if(s.ValueChannel2 < Ch2Min) Ch2Min = s.ValueChannel2;
    }