提高数据库行读取效率,无需装箱

时间:2016-11-25 22:13:28

标签: c# performance boxing

我使用拳击方法通过type-switch helper class described here从数据库中读取数据。

装箱方法主要用于我从/向默认类型派生的自定义类型(例如,我将DB_Image元素存储为int32值,这与图像列表中的索引相对应)。

我发现这种方法在性能方面有多糟糕。问题是,我能做得更好吗?

欢迎举例。

1 个答案:

答案 0 :(得分:0)

好的,所以我刚开始编写一个关于从DataTable读取数据的不同方法的小测试程序。

我使用以下4种方法来设置intstringdoubleDateTime属性,如下所示:

设置转换:myObj.IntValue = Convert.ToInt32(row["int"]);

设置演员:myObj.IntValue = (int)row["int"];

设置字段:myObj.IntValue = row.Field<int>("int");

设置TypeSwitchmyObj.IntValue = TypeSwitch.GetValue(typeof(int), row["int"]);

测试1:没有DBNull值的数据表 包含10行的表格,为每行设置4个(intstringdoubleDateTime)属性,执行了100万次:

设置转换:9002 ms

设置演员:8504毫秒

设置字段:9312 ms

设置TypeSwitch:10779毫秒

3种方法之间的区别并不大(+/- 10%),但TypeSwitch支付字典访问价格(比投射慢27%)......

测试2:DBNull的数据表 当然,现在有DBNull.Value个问题,所以我需要修改方法1到3,否则会崩溃。

包含10行(5只包含非空值,5只包含空值)的表设置4(intstringdoubleDateTime )每行的属性,执行100万次:

因此,我在方法1到3中添加了如下测试:

row["int"] != DBNull.Value ? xxxx : default(int)

其中xxxx与之前的测试相同。

对于TypeSwitch,还会对DBNull.Value进行测试。如果不为null,则返回Convert值,否则它使用另一个开关并返回默认值作为其他方法。

结果如下:

设置转换:16995毫秒

Set cast:16487 ms

设置字段:17204 ms

设置TypeSwitch:10652 ms

现在大惊喜 - 我们用TypeSwitch比其他方法更快更多 ......实际上,这是因为我们两次访问该字段方法1-3中的行,所以我将这个修订版用于方法1和2

var val = row["int"];
int i = val != DBNull.Value ? xxxx : default(val);

这里是修订后的结果(仍然是5个非空行和5个空行,执行了100万次):

设置转换:9256.1556 ms

Set cast:8895.1208 ms

设置字段:13549.9371 ms

设置TypeSwitch:10935.5278毫秒

好的,我们回到原来的位置,投射速度最快,TypeSwitch现在慢18%,而Field是一个明显的输家。

现在,为了完整起见,我们只需在时间度量var val = row["int"];中删除行字段访问时查看结果:

设置转换:630.113毫秒

Set cast:314.9697 ms

设置字段:5149.6852 ms

设置TypeSwitch:2354.9869 ms

Kaboooom!显然,大多数情况下,实际上是随机访问数据的列,而不是盒子转换。

所以,我继续尝试按ID而不是列名访问列(我重新添加行访问次数到时间测量)

设置转换:1480.0548毫秒

Set cast:1139.0841 ms

设置字段:5928.8186 ms

设置TypeSwitch:2923.8451 ms

现在我们处于这个测试的底线:演员现在是明显的赢家!

<强>结论 当然,使用r.Field<int>("aId")设置值是个坏主意,TypeSwitch会受到字典访问的开销。

此外,按名称而不是索引访问字段是一件糟糕的表现。

<强>更新

我已经编写了另一项测试来比较DataTable.FillFbDataReaderDapper.Query<T>,以填写声明如下的ObjectA项目列表:

public class ObjectA : ObjectBase {
        public int aID;
        public string aStr;
        public double aDbl;
        public DateTime aDate;
    }

我已经运行了20次样本,从具有322759行的Firebird 2.5数据库中读取数据并构建相应的List<ObjectA>。我添加了一个测试来检查所有三种方法加载相同数量的项目,并且所有项目都具有匹配的属性。 对于DataTableFbDataReader,我使用了上述Cast方法,访问了ID为DataTable的元素。我意识到,对于FbDataReader,无论使用字段名称还是列ID,访问时间都不会发生太大变化,因此我将字段名称保留为访问者。

结果

DataTable:       42742.922 ms
FbDataReader:    31088.6197 ms
Dapper.Query<T>: 28626.5392 ms

所以胜利者显然是DapperDataTable是失败者,正如预期的那样。请注意,加载超过6Mio记录的时间,在所有三种情况下都非常令人印象深刻......

相关问题