繁体   English   中英

C#提高数组查找循环性能

[英]C# Increase Array Find Loop Performance

我有一个Datapoint[] file = new Datapoint[2592000]数组。 该数组填充有时间戳和随机值。 创建它们花了我2秒钟的时间。 但是在另一个函数prepareData(); 我正在为另一个数组TempBuffer准备240个值。 prepareData()函数中,我正在搜索file数组中的匹配值。 如果找不到,我将时间戳记并将其设置为0,否则我将获取发现值+相同的时间戳记。

该函数如下所示:

public void prepareData()
{  
    stopWatch.Reset();
    stopWatch.Start();
    Int32 unixTimestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;

    for (double i = unixTimestamp; unixTimestamp - 240 < i; i--)
    {
        bool exists = true;

        if (exists != (Array.Exists(file, element => element.XValue == i)))
        {
            TempBuffer = TempBuffer.Skip(1).Concat(new DataPoint[] { new DataPoint(UnixTODateTime(i).ToOADate(), 0) }).ToArray();
        }
        else
        {
            DataPoint point = Array.Find(file, element => element.XValue == i);
            TempBuffer = TempBuffer.Skip(1).Concat(new DataPoint[] { new DataPoint(UnixTODateTime(i).ToOADate(), point.YValues) }).ToArray();
        }
    }

    stopWatch.Stop();
    TimeSpan ts = stopWatch.Elapsed;
}

现在的问题是file的数据量很大(2'592'000),该功能需要40秒钟! 使用较小的数量(例如1万),这不是问题,并且可以正常且快速地工作。 但是,一旦我将file大小设置为我喜欢的2'592'000点,CPU就被推到了99%的性能,并且该功能需要太长时间。

TempBuffer样本值:
X =将UnixTimeStamp转换为DateTime,并将DateTime转换为AODate
{X = 43285.611087963,Y = 23}

文件样本值:
X = Unix时间戳
{X = 1530698090,Y = 24}

将临时缓冲区值转换为AODate非常重要,因为临时缓冲区数组中的数据以mschart显示。

有没有一种方法可以改善我的代码,以便获得更好的性能?

Array.Exists()和Array.Find()是O(N)个操作,您要执行它们x M(240)次。

尝试使用LINQ Join:

DataPoint[] dataPoints; // your "file" variable
var seekedTimestamps = Enumerable.Range(0, 240).Select(i => unixTimestamp - i);
var matchingDataPoints = dataPoints.Join(seekedTimestamps, dp => dp.XValue, sts => sts, (dp, sts) => dp);
var missingTimestamps = seekedTimestamps.Except(matchingDataPoints.Select(mdp => mdp.XValue));
// do your logic with found and missing here
// ...

LINQ Join使用散列(在选定的“键”上)并且接近O(n)

另外,假设输入中的时间戳是唯一的,并且您打算对输入进行多次操作,则构造一个Dictionary<int (Timestamp), DataPoint> (expensive),这将为您提供O(1)检索所需数据点的方法: var dataPoint = dict[wantedTimestamp];

您尚未提供完整的代码图。 理想情况下,我希望获得示例数据和完整的类定义。 但是,鉴于可用的限制信息,我想您会发现类似的作品:

public void prepareData()
{ 
    Int32 unixTimestamp = (Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;

    var map = file.ToLookup(x => x.XValue);

    TempBuffer =
        Enumerable
            .Range(0, 240)
            .Select(x => unixTimestamp - x)
            .SelectMany(x =>
                map[x]
                    .Concat(new DataPoint(UnixTODateTime(x).ToOADate(), 0)).Take(1))
            .ToArray();
}

这是完成任务的最有效方式(这只是模板,而不是最终代码):

public void prepareData()
{
    // it will be initialized with null values
    var tempbuffer = new DataPoint[240];

    var timestamp = (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
    var oldest = timestamp - 240 + 1;

    // fill tempbuffer with existing DataPoints
    for (int i = 0; i < file.Length; i++)
    {
        if (file[i].XValue <= timestamp && file[i].XValue > timestamp - 240)
        {
            tempbuffer[file[i].XValue - oldest] = new DataPoint(file[i].XValue, file[i].YValues);
        }
    }

    // fill null values in tempbuffer with 'empty' DataPoints
    for (int i = 0; i < tempbuffer.Length; i++)
    {
        tempbuffer[i] = tempbuffer[i] ?? new DataPoint(oldest + i, 0);
    }
}

我大约有10毫秒

评论更新:

如果要获取多个DataPoint's并使用某些函数(例如平均值)获取结果,则:

public void prepareData()
{
    // use array of lists of YValues
    var tempbuffer = new List<double>[240];

    // initialize it
    for (int i = 0; i < tempbuffer.Length; i++)
    {
        tempbuffer[i] = new List<double>(); //set capacity for better performance
    }

    var timestamp = (int)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds;
    var oldest = timestamp - 240 + 1;

    // fill tempbuffer with existing DataPoint's YValues
    for (int i = 0; i < file.Length; i++)
    {
        if (file[i].XValue <= timestamp && file[i].XValue > timestamp - 240)
        {
            tempbuffer[file[i].XValue - oldest].Add(file[i].YValues);
        }
    }

    // get result
    var result = new DataPoint[tempbuffer.Length];
    for (int i = 0; i < result.Length; i++)
    {
        result[i] = new DataPoint(oldest + i, tempbuffer[i].Count == 0 ? 0 : tempbuffer[i].Average());
    }
}

如果DataPoint是唯一的(没有2个具有相同值的实例),则应将列表file切换为字典。 字典查找比迭代数组的所有成员快得多。

当然,您需要实现GetHashCodeEquals或为每个Datapoint定义唯一的键。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM