比较C#中的求和方法

时间:2012-04-03 05:27:55

标签: c# performance linq sum

我正在研究使用大量求和方法的项目的一部分。这些求和方法应用于Datatable

为了测试最佳方法,我使用以下

数据结构

class LogParser
{
     public DataTable PGLStat_Table = new DataTable();
     public LogParser()
     {
         PGLStat_Table.Columns.Add("type", typeof(string)); 
         PGLStat_Table.Columns.Add("desc", typeof(string)); 
         PGLStat_Table.Columns.Add("count", typeof(int));
         PGLStat_Table.Columns.Add("duration", typeof(decimal));
         PGLStat_Table.Columns.Add("cper", typeof(decimal));
         PGLStat_Table.Columns.Add("dper", typeof(decimal));
         PGLStat_Table.Columns.Add("occurancedata", typeof(string));  
     }       
}

以下方法用于填写表格

LogParser pglp = new LogParser();
Random r2 = new Random();
for (int i = 1; i < 1000000; i++)
{
    int c2 = r2.Next(1, 1000);
    pglp.PGLStat_Table.Rows.Add("Type" + i.ToString(), "desc" + i , c2, 0, 0, 0, " ");
}
  • Sum应用于count列,其中c2的值已更新

以下用于计算Sum的方法

方法1使用Compute

Stopwatch s2 = new Stopwatch();
s2.Start();
object sumObject;
sumObject = pglp.PGLStat_Table.Compute("Sum(count)", " ");
s2.Stop();
long d1 = s2.ElapsedMilliseconds;

方法2使用Foreach循环

s2.Restart();
int totalcount = 0;
foreach (DataRow dr in pglp.PGLStat_Table.Rows)
{
   int c = Convert.ToInt32(dr["count"].ToString());
   totalcount = totalcount + c;
}
s2.Stop();
long d2 = s2.ElapsedMilliseconds;

方法3使用Linq

s2.Restart();
var sum = pglp.PGLStat_Table.AsEnumerable().Sum(x => x.Field<int>("count"));
MessageBox.Show(sum.ToString());
s2.Stop();
long d3 = s2.ElapsedMilliseconds;

比较结果

a)foreach是最快的481ms

b)接下来是linq 1016ms

c)然后计算2253ms


查询1

我在下面的陈述中意外地将“c2改为i”

 pglp.PGLStat_Table.Rows.Add("Type" + i.ToString(), "desc" + i , i, 0, 0, 0, " ");

Linq语句产生错误

  

算术运算导致溢出。

尽管可能不正确,但Compute和Foreach循环仍然能够完成计算。

此类行为是否引起关注或我是否错过指令? (计算的数字也很大)

查询2

我的印象是Linq做得最快,是否有优化的方法或参数 这使它表现得更好。

感谢您的建议

Arvind的

3 个答案:

答案 0 :(得分:4)

接下来是最快的总和(使用预计算DataColumn并直接转换为int):

  static int Sum(LogParser pglp)
  {
    var column = pglp.PGLStat_Table.Columns["count"];
    int totalcount = 0;
    foreach (DataRow dr in pglp.PGLStat_Table.Rows)
    {
      totalcount += (int)dr[column];
    }
    return totalcount;
  }

统计:

00:00:00.1442297, for/each, by column, (int)
00:00:00.1595430, for/each, by column, Field<int>
00:00:00.6961964, for/each, by name, Convert.ToInt
00:00:00.1959104, linq, cast<DataRow>, by column, (int)

其他代码:

  static int Sum_ForEach_ByColumn_Field(LogParser pglp)
  {
    var column = pglp.PGLStat_Table.Columns["count"];
    int totalcount = 0;
    foreach (DataRow dr in pglp.PGLStat_Table.Rows)
    {
      totalcount += dr.Field<int>(column);
    }
    return totalcount;
  }
  static int Sum_ForEach_ByName_Convert(LogParser pglp)
  {
    int totalcount = 0;
    foreach (DataRow dr in pglp.PGLStat_Table.Rows)
    {
      int c = Convert.ToInt32(dr["count"].ToString());
      totalcount = totalcount + c;
    }
    return totalcount;
  }
  static int Sum_Linq(LogParser pglp)
  {
    var column = pglp.PGLStat_Table.Columns["count"];
    return pglp.PGLStat_Table.Rows.Cast<DataRow>().Sum(row => (int)row[column]);
  }


    var data = GenerateData();
    Sum(data);
    Sum_Linq2(data);
    var count = 3;
    foreach (var info in new[]
      {
        new {Name = "for/each, by column, (int)", Method = (Func<LogParser, int>)Sum},
        new {Name = "for/each, by column, Field<int>", Method = (Func<LogParser, int>)Sum_ForEach_ByColumn_Field},
        new {Name = "for/each, by name, Convert.ToInt", Method = (Func<LogParser, int>)Sum_ForEach_ByName_Convert},
        new {Name = "linq, cast<DataRow>, by column, (int)", Method = (Func<LogParser, int>)Sum_Linq},
      })
    {
      var watch = new Stopwatch();
      for (var i = 0; i < count; ++i)
      {
        watch.Start();
        var sum = info.Method(data);
        watch.Stop();
      }
      Console.WriteLine("{0}, {1}", TimeSpan.FromTicks(watch.Elapsed.Ticks / count), info.Name);
    }

答案 1 :(得分:1)

查询1.

正如您在文档Enumerable.Sum中看到的那样,扩展方法会在整数溢出时抛出OverflowExceptionDataTable.Compute没有像方法2中使用的那样的功能和整数操作。


更新: 查询2.

  

我的印象是Linq做得最快,是否有优化的方法或参数使其表现更好。

AFAIK,没有方法可以优化数组求和算法(不使用并行计算)。 Linq将foreach使用的时间加倍。所以,我不认为这是关于linq性能而是计算效率低下(注意查询字符串解释存在开销)。

答案 2 :(得分:1)

你可以在linq示例(AsEnumerable)上稍微提高一点但是这是预期的行为 - Linq(2objects)作为一个循环不能更快(你可以通过使用for(var i = ...)做得更好循环而不是foreach) - 我想你的意思是使用Linq2Sql - 然后聚合(sum)将在数据库上完成,它应该更快 - 但因为你似乎没有使用数据库数据..