比较C#中的求和方法
本文关键字:求和 方法 比较 | 更新日期: 2023-09-27 18:29:44
我正在处理一个使用大量求和方法的项目的一部分。这些求和方法应用于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, " ");
}
- 求和应用于计数列,其中更新c2的值
以下方法用于计算总和
使用计算的方法1
Stopwatch s2 = new Stopwatch();
s2.Start();
object sumObject;
sumObject = pglp.PGLStat_Table.Compute("Sum(count)", " ");
s2.Stop();
long d1 = s2.ElapsedMilliseconds;
使用Foreach循环的方法2
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;
使用Linq的方法3
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) 下一个是linq1016ms
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
下一个是最快的和(带有预计算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);
}
好吧,您可以在linq示例(AsEnumerable
)上改进一点,但这是预期的行为-linq(2对象)不能像循环一样快(使用for(var i = ...)
循环而不是foreach可以做得更好)-我想您想做的是使用Linq2Sql-然后在数据库上进行聚合(sum),应该会更快-但由于您似乎不使用数据库数据。。。
查询1.
正如您在文档Enumerable.Sum扩展方法中看到的那样,在整数溢出时抛出OverflowException。Compute没有像方法2中使用的整数运算那样的功能。
更新:查询2
我觉得Linq做得最快,有没有一个优化的方法或参数可以让它表现得更好。
AFAIK,没有优化阵列求和算法的方法(不使用并行计算)。林克将foreach使用的时间增加了一倍。所以,我不认为这是linq性能的问题,而是计算效率低下的问题(请注意,查询字符串解释会有开销)。