[英]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
切换为字典。 字典查找比迭代数组的所有成员快得多。
当然,您需要实现GetHashCode
和Equals
或为每个Datapoint
定义唯一的键。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.