簡體   English   中英

提高LINQ性能

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

  1. 我想知道是否有任何方法可以通過僅僅瀏覽一次所有記錄來改進它(因為我們只需要在使用plain for循環時在單個循環中執行它)?

  2. 在我們完成總和和平均之前,確實沒有必要將所有內容加載到內存中 當然,我們可以在從文件加載每個元素時對它們求和。 有沒有辦法減少內存消耗?


編輯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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM