【发布时间】:2015-01-31 08:15:23
【问题描述】:
看看下面的程序。这很不言自明,但无论如何我都会解释:)
我有两种方法,一种快,一种慢。这些方法做的事情完全相同:它们创建一个包含 50,000 行和 1000 列的表。我写入表中可变数量的列。在下面的代码中,我选择了 10 个 (NUM_COLS_TO_WRITE_TO)。
换句话说,1000 列中只有 10 列实际包含数据。好的。这两种方法的唯一区别在于,快速填充列并然后调用DataTable.AddRow,而慢速填充之后。就是这样。
然而,性能差异是令人震惊的(无论如何对我来说)。快速版本几乎完全不受更改我们写入的列数的影响,而慢速版本线性上升。例如,当我写入的列数为 20 时,快速版本需要 2.8 秒,但慢速版本需要 分钟。
这里到底发生了什么?
我认为添加dt.BeginLoadData 可能会有所不同,它确实在某种程度上将时间从 61 秒缩短到了约 50 秒,但这仍然是一个巨大的差异。
当然,显而易见的答案是,“好吧,不要那样做。”好的。当然。但究竟是什么导致了这种情况?这是预期的行为吗?我当然没想到。 :)
public class Program
{
private const int NUM_ROWS = 50000;
private const int NUM_COLS_TO_WRITE_TO = 10;
private const int NUM_COLS_TO_CREATE = 1000;
private static void AddRowFast() {
DataTable dt = new DataTable();
//add a table with 1000 columns
for (int i = 0; i < NUM_COLS_TO_CREATE; i++) {
dt.Columns.Add("x" + i, typeof(string));
}
for (int i = 0; i < NUM_ROWS; i++) {
var theRow = dt.NewRow();
for (int j = 0; j < NUM_COLS_TO_WRITE_TO; j++) {
theRow[j] = "whatever";
}
//add the row *after* populating it
dt.Rows.Add(theRow);
}
}
private static void AddRowSlow() {
DataTable dt = new DataTable();
//add a table with 1000 columns
for (int i = 0; i < NUM_COLS_TO_CREATE; i++) {
dt.Columns.Add("x" + i, typeof(string));
}
for (int i = 0; i < NUM_ROWS; i++) {
var theRow = dt.NewRow();
//add the row *before* populating it
dt.Rows.Add(theRow);
for (int j=0; j< NUM_COLS_TO_WRITE_TO; j++){
theRow[j] = "whatever";
}
}
}
static void Main(string[] args)
{
var sw = Stopwatch.StartNew();
AddRowFast();
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
sw.Restart();
AddRowSlow();
sw.Stop();
Console.WriteLine(sw.Elapsed.TotalMilliseconds);
//When NUM_COLS is 5
//FAST: 2754.6782
//SLOW: 15794.1378
//When NUM_COLS is 10
//FAST: 2777.431 ms
//SLOW 32004.7203 ms
//When NUM_COLS is 20
//FAST: 2831.1733 ms
//SLOW: 61246.2243 ms
}
}
更新
在慢速版本中调用 theRow.BeginEdit 和 theRow.EndEdit 会使慢速版本或多或少保持不变(在我的机器上大约 4 秒)。如果我真的有桌子上有一些限制,我想这对我来说可能有意义。
【问题讨论】:
-
我认为解释很简单。当
DataRow在表格之外时,更改数据不会触发任何检查/验证/通知等。为了更准确的解释,我会查看DataRows 列值设置器代码... -
@Dennis,但我认为 BeginLoadData 的目的是“在加载数据时关闭通知、索引维护和约束”。此外,似乎慢 30 倍 是一个相当大的差异。
-
另外,我觉得奇怪的是表中的列数会影响运行时。也就是说,如果我更新 10 列并且只有 are 10 列,那么它运行得非常好。那么,.NET 对我从未接触过的所有这些列做了什么?事件是否也在这些事件上触发?看起来很奇怪。
-
@aquinas:您的示例中没有调用
BeginLoadData。此外,文档说“将 BeginLoadData 与 LoadDataRow 和 EndLoadData 结合使用”。看起来它适用于非常特殊的场景。 -
对,就像我上面说的,当我添加 BeginLoadData 时,它会在 60 秒中缩短大约 10 秒。
标签: c# .net performance