[英]Improve LINQ performance
我有這樣的linq語句:
var records = from line in myfile
let data = line.Split(',')
select new { a=int.Parse(data[0]), b=int.Parse(data[1]) };
var average = records.Sum(r => r.b)!=0?records.Sum(r => r.a) / records.Sum(r => r.b):0;
我的問題是:記錄的次數是多少次。(r => rb)是在最后一行計算的? LINQ是否每次需要計算總和時都會遍歷所有記錄(在這種情況下,3 Sum()所以循環3次)? 或者只巧妙地循環所有記錄一次並計算所有總和?
編輯1 :
我想知道是否有任何方法可以通過僅僅瀏覽一次所有記錄來改進它(因為我們只需要在使用plain for循環時在單個循環中執行它)?
在我們完成總和和平均之前,確實沒有必要將所有內容加載到內存中 。 當然,我們可以在從文件加載每個元素時對它們求和。 有沒有辦法減少內存消耗?
編輯2
只是為了澄清一下,在我結束之前我沒有使用LINQ。 使用plain while / for循環可以實現所有性能要求。 但我接着嘗試通過使用LINQ來提高可讀性並減少代碼行。 似乎我們無法同時獲得兩者。
兩次,寫這樣,它將是一次:
var sum = records.Sum(r => r.b);
var avarage = sum != 0 ? records.Sum(r => r.a)/sum: 0;
有很多答案,但沒有一個能夠解決你的所有問題。
記錄的次數是多少次。(r => rb)是在最后一行計算的?
三次。
LINQ是否每次需要計算總和時都會遍歷所有記錄(在這種情況下,3 Sum()所以循環3次)?
是。
或者只巧妙地循環所有記錄一次並計算所有總和?
沒有。
我想知道是否有任何方法可以通過僅僅瀏覽一次所有記錄來改進它(因為我們只需要在使用plain for循環時在單個循環中執行它)?
你可以這樣做,但它需要你急切地加載所有與你的下一個問題相矛盾的數據。
在我們完成總和和平均之前,確實沒有必要將所有內容加載到內存中。 當然,我們可以在從文件加載每個元素時對它們求和。 有沒有辦法減少內存消耗?
那是對的。 在你的原始帖子中,你有一個名為myFile
的變量,你正在迭代它並將它放入一個名為line
的局部變量中(讀取:基本上是一個foreach
)。 由於您沒有顯示如何獲取myFile
數據,我假設您正在急切地加載所有數據。
這是一個延遲加載數據的快速示例:
public IEnumerable<string> GetData()
{
using (var fileStream = File.OpenRead(@"C:\Temp\MyData.txt"))
{
using (var streamReader = new StreamReader(fileStream))
{
string line;
while ((line = streamReader.ReadLine()) != null)
{
yield return line;
}
}
}
}
public void CalculateSumAndAverage()
{
var sumA = 0;
var sumB = 0;
var average = 0;
foreach (var line in GetData())
{
var split = line.Split(',');
var a = Convert.ToInt32(split[0]);
var b = Convert.ToInt32(split[1]);
sumA += a;
sumB += b;
}
// I'm not a big fan of ternary operators,
// but feel free to convert this if you so desire.
if (sumB != 0)
{
average = sumA / sumB;
}
else
{
// This else clause is redundant, but I converted it from a ternary operator.
average = 0;
}
}
三次,你應該在這里使用的是Aggregate
,而不是Sum
。
// do your original selection
var records = from line in myfile
let data = line.Split(',')
select new { a=int.Parse(data[0]), b=int.Parse(data[1]) };
// aggregate them into one record
var sumRec = records.Aggregate((runningSum, next) =>
{
runningSum.a += next.a;
runningSum.b += next.b;
return runningSum;
});
// Calculate your average
var average = sumRec.b != 0 ? sumRec.a / sumRec.b : 0;
每次調用Sum方法都會遍歷myfile中的所有行。 為了提高性能寫:
var records = (from line in myfile
let data = line.Split(',')
select new { a=int.Parse(data[0]), b=int.Parse(data[1]) }).ToList();
所以它會創建包含所有元素的列表(帶有“a”和“b”屬性),然后每次調用Sum方法都將遍歷此列表而不拆分和解析數據。 當然,你可以進一步記住一些臨時變量中Sum方法的結果。
詹姆斯,我不是一位專家,這是我的想法。 我認為可能會減少到1.也許有更多的代碼。 記錄仍然是AnonymousType {int a,int b}的IEnumerable。
*動態是一種快速解決方法。 你應該為它編寫一個結構。
int sum_a = 0,sum_b = 0;
Func<string[], dynamic> b = (string[] data) => {
sum_a += int.Parse(data[0]);
sum_b += int.Parse(data[1]);
return new {a = int.Parse(data[0]),b = int.Parse(data[0]) };
};
var records = from line in fileLines
let data = line.Split(',')
let result = b(data)
select new { a = (int)result.a, b = (int)result.b };
var average = sum_b != 0 ? sum_a / sum_b : 0;
對於其他結構,它很簡單。
public struct Int_Int //May be a class or interface for mapping
{
public int a = 0, b = 0;
}
然后
int sum_a = 0,sum_b = 0;
Func<string[], Int_Int> b = (string[] data) => {
sum_a += int.Parse(data[0]);
sum_b += int.Parse(data[1]);
return new Int_Int() { a = int.Parse(data[0]), b = int.Parse(data[0]) };
};
var records = from line in fileLines
let data = line.Split(',')
select b(data);
var average = sum_b != 0 ? sum_a / sum_b : 0;
SUM會在您調用它時隨時獲取所有記錄,我建議您使用ToList() - > To ToList()?
var records = from line in myfile
let data = line.Split(',')
select new { a=int.Parse(data[0]), b=int.Parse(data[1]) }.ToList();
var sumb = records.Sum(r => r.b);
var average = sumb !=0?records.Sum(r => r.a) / sumb :0;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.