[英]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.