我正在研究使用大量求和方法的项目的一部分。这些求和方法应用于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的方法
方法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的
答案 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中看到的那样,扩展方法会在整数溢出时抛出OverflowException。 DataTable.Compute没有像方法2中使用的那样的功能和整数操作。
的更新:强>
查询2.
我的印象是Linq做得最快,是否有优化的方法或参数使其表现更好。
AFAIK,没有方法可以优化数组求和算法(不使用并行计算)。 Linq将foreach使用的时间加倍。所以,我不认为这是关于linq性能而是计算效率低下(注意查询字符串解释存在开销)。
答案 2 :(得分:1)
你可以在linq示例(AsEnumerable
)上稍微提高一点但是这是预期的行为 - Linq(2objects)作为一个循环不能更快(你可以通过使用for(var i = ...)
做得更好循环而不是foreach) - 我想你的意思是使用Linq2Sql - 然后聚合(sum)将在数据库上完成,它应该更快 - 但因为你似乎没有使用数据库数据..