C# IEnumerable.Max()是最快的方法吗?

C# IEnumerable.Max()是最快的方法吗?,c#,performance,ienumerable,C#,Performance,Ienumerable,我正在开发一个软件的一部分,其中有一个样本列表(list),如下所示: public class Sample { //... public double ValueChannel1 { get; set; } public double ValueChannel2 { get; set; } //... } 这些列表中有大约100到数千个样本,每秒大约有10万个样本。 现在,我需要从这些列表中找出最大值和最小值,我现在用以下方法: var Ch1Max = t

我正在开发一个软件的一部分,其中有一个样本列表(
list
),如下所示:

public class Sample
{
    //...
    public double ValueChannel1 { get; set; }
    public double ValueChannel2 { get; set; }
    //...
}
这些列表中有大约100到数千个样本,每秒大约有10万个样本。
现在,我需要从这些列表中找出最大值和最小值,我现在用以下方法:

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);
var Ch1Max=task.Samples.Max(s=>s.ValueChannel1);
var Ch1Min=task.Samples.Min(s=>s.ValueChannel1);
var Ch2Max=task.Samples.Max(s=>s.ValueChannel2);
var Ch2Min=task.Samples.Min(s=>s.ValueChannel2);
毫不奇怪,这不是很快,所以我问自己,如果有更快的事情做,但我想不出或找不到一个

有人知道更快的方法吗? 也许有一种方法可以通过“一个循环”找到最小值和最大值,而不是一个循环求最小值和一个循环求最大值

编辑:
我分析了当前代码,结果如下:
731个任务(每个任务包含其中一个列表)需要845毫秒的处理时间,其中95%的任务需要最小/最大搜索时间。
我没有具体的“目标时间”,但由于它一直在我的应用程序中运行(因为它正在捕获测量数据),因此应该尽可能少地使用CPU,以保持尽可能低的硬件要求

找到的最佳解决方案:
最后我选择了Tim的soltuion,因为它比Konrad的更快了一点:
Tim的解决方案使速度提高了约53%,Konrads的“仅”提高了约43%

最终解决方案(目前):

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;
}
double Ch1Max=double.MinValue,Ch1Min=double.MaxValue;
double Ch2Max=double.MinValue,Ch2Min=double.MaxValue;
var samples=task.samples.ToArray();
int count=样本长度;
对于(int i=0;i更快
如果(valueChannel1>Ch1Max)Ch1Max=valueChannel1;
如果(valueChannel1Ch2Max)Ch2Max=valueChannel2;
如果(valueChannel2

与我的初始解决方案相比,总速度提高了约70%。

您可以使用单个循环:

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;
}
double Ch1Max=double.MinValue;
double Ch1Min=double.MaxValue;
double Ch2Max=double.MinValue;
double Ch2Min=double.MaxValue;
foreach(样本中的样本)
{
如果(s.ValueChannel1>Ch1Max),Ch1Max=s.ValueChannel1;
如果(s.ValueChannel1Ch2Max),Ch2Max=s.ValueChannel2;
如果(s.ValueChannel2
如果您可以控制您的
列表
对象(我的意思是您不能从第三方代码等处获得它),您可以将其封装在您自己的类中,这样可以在添加元素时动态跟踪最大值和最小值

只需查看新的
示例是否未“设置新记录”,并相应地调整缓存的最大/最小值(如果是)

如果列表只向前,那么这种方法将非常有效,例如,您没有从列表中删除元素

编辑:

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;
}
下面是一个示例实现(这有点“概念验证”,当然还有很多改进空间):

公共类示例
{
公共双价值渠道1
{
得到;
设置
}
公共双价值渠道2
{
得到;
设置
}
//等等。
}
公共类样本列表
{
/*这就是我们正在整理的清单。
*SampleList也可以从List继承,但通常不推荐使用这种方法-
*读一读“写作重于继承”*/
私有列表_samples=新列表();
/// 
///缓存ValueChannel1属性的已知最低值
/// 
public double?valuechannel1 minimum//它是一个可为空的double,因为虽然列表仍然为空,但最小值和最大值还没有值
{
得到;
私人设置;
}
公共双值通道1最大值{get;私有集;}
public double?valuechannel2最小值{get;private set;}
public double?ValueChannel2Maximum{get;private set;}
公共无效添加(示例)
{
if(sample==null)
{
抛出新的ArgumentNullException(“示例”);
}
//你打破记录了吗?

如果(sample.ValueChannel1,您始终可以自己实现最小/最大逻辑。使用
Aggregate
扩展方法应该很容易:

Pair<Sample, Sample> minMax = task.Samples.Aggregate(
  new Pair<Sample, Sample> {
    First = new Sample { ValueChannel1 = double.MaxValue, ValueChannel2 = double.MaxValue },
    Second = new Sample { ValueChannel1 = double.MinValue, ValueChannel2 = double.MinValue }
  },
  (minmax, sample) => {
    minmax.First.ValueChannel1 = Math.Min(minmax.First.ValueChannel1, sample.ValueChannel1);
    minmax.First.ValueChannel2 = Math.Min(minmax.First.ValueChannel2, sample.ValueChannel2);

    minmax.Second.ValueChannel1 = Math.Max(minmax.Second.ValueChannel1, sample.ValueChannel1);
    minmax.Second.ValueChannel2 = Math.Max(minmax.Second.ValueChannel2, sample.ValueChannel2);
  });

Console.Out.WriteLine("Channel 1 Min = {0}, Channel 1 Max = {1}, Channel 2 Min = {2}, Channel 2 Max = {3}",
minMax.First.ValueChannel1,
minMax.Second.ValueChannel1,
minMax.First.ValueChannel2,
minMax.Second.ValueChannel2);
Pair minMax=task.Samples.Aggregate(
新的一对{
第一个=新样本{ValueChannel1=double.MaxValue,ValueChannel2=double.MaxValue},
第二个=新样本{ValueChannel1=double.MinValue,ValueChannel2=double.MinValue}
},
(最小最大值,样本)=>{
minmax.First.ValueChannel1=Math.Min(minmax.First.ValueChannel1,sample.ValueChannel1);
minmax.First.ValueChannel2=Math.Min(minmax.First.ValueChannel2,sample.ValueChannel2);
minmax.Second.ValueChannel1=Math.Max(minmax.Second.ValueChannel1,sample.ValueChannel1);
minmax.Second.ValueChannel2=Math.Max(minmax.Second.ValueChannel2,sample.ValueChannel2);
});
WriteLine(“通道1最小值={0},通道1最大值={1},通道2最小值={2},通道2最大值={3}”,
minMax.First.ValueChannel1,
最小最大值。秒。值通道1,
minMax.First.ValueChannel2,
最小最大值。秒。值通道2);
读起来可能更好(可能不是,这取决于您是否喜欢函数式思维),但它肯定比使用带有4个变量的简单foreach循环慢(因为有很多函数调用)。这种方法的优点是它不使用任何外部变量(但是你可以将foreach循环封装在一个方法中)。

有多慢是“不是很快”?你有没有想过