Сравнение методов суммирования в C#
Я работаю над разделом проекта, который использует большое количество методов сумма. Эти методы sum применяются к Datatable
чтобы проверить лучший метод, я использую следующий
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 обновлено
следующие методы, используемые для расчета суммы
метод 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 выдает ошибку
арифметическая операция привела к переполнению.
в то время как вычисление и цикл Foreach все еще могут завершить вычисление, хотя, возможно, неверно.
является ли такое поведение причиной беспокойства или мне не хватает директива ? (также вычисленные цифры большие)
запрос 2
Я был под впечатлением, что Linq делает это быстрее, есть ли оптимизированный метод или параметр это делает его более эффективным.
спасибо за советы
Арвинд
3 ответов
самая быстрая сумма следующая (с precompute 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 (2objects) не может быть быстрее как цикл (вы могли бы сделать еще лучше, используя for(var i = ...)
loop вместо foreach) - я думаю, что вы хотели использовать Linq2Sql - тогда агрегация (сумма) будет выполнена в базе данных, и она должна быть быстрее - но поскольку вы, похоже, не используете database-data...
запрос 1.
Как вы можете видеть в документации перечисли.Sum метод расширения выдает OverflowException при переполнении целого числа. DataTable.Вычислить не имеет такой функциональности, а также целочисленных операций, которые вы используете в методе 2.
обновление:
Запрос 2.
Я был под впечатлением, что Linq делает это быстрее, есть ли оптимизированный метод или параметр, который делает его лучше.
AFAIK, нет метода оптимизации алгоритма суммирования массива (без использования параллельных вычислений). Linq удваивает время, используемое foreach. Итак, я не думаю, что речь идет о производительности linq, но вычислить неэффективность (обратите внимание, что есть накладные расходы для интерпретации строки запроса).