使用SqlDataReader创建大量对象时的CPU性能

时间:2012-02-10 17:27:45

标签: c# performance cpu sqldatareader

我有一个存储过程,它返回几个结果集,并在(某些)结果集中返回1000行。我正在使用x线程同时执行此存储过程,最多同时执行y。

当我刚刚浏览数据时:

using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
    do
    {
        while (reader.Read())
        {
            for (int i = 0; i < reader.FieldCount; i++)
            {
                var value = reader.GetValue(i);
            }
        }
    }
    while (reader.NextResult());
 }

我获得了合理的吞吐量 - 和CPU。现在显然这没用,所以我需要一些物品!好的,现在我改变它做这样的事情:

using (SqlDataReader reader = command.ExecuteReader(CommandBehavior.CloseConnection))
{
    while(reader.Read())
    {
        Bob b = new Bob(reader);
        this.bobs.Add(b);              
    }

     reader.NextResult();

    while(reader.Read())
    {
        Clarence c = new Clarence(reader);
        this.clarences.Add(c);
    }

    // More fun
}

我的数据类实现:

public Bob(SqlDataReader reader)
{
   this.some = reader.GetInt32(0);
   this.parameter = reader.GetInt32(1);
   this.value = reader.GetString(2);
}

这表现得更糟。这并不奇怪。 令人惊讶的是,CPU下降了大约20%-25%(即不是它使用的25%;它接近它使用的50%)!为什么要做更多工作,放弃CPU?我不明白......看起来某处有一些锁定 - 但我不知道在哪里?我想充分利用机器!

编辑 - 由于糟糕的演示代码示例而更改了代码。哎呀。

另外:为了测试构造函数理论,我改变了创建对象的实现。这次它还在字段上执行for循环,并执行getValue,并将创建一个空对象。所以它没有做我想要的,但我想看看是否创建了大量的对象。事实并非如此。

第二次编辑:看起来将对象添加到列表中就是问题 - 只要我将这些对象添加到列表中,CPU立即就会丢失。不知道如何改善这个...(或者如果它值得调查;第一种情况显然是愚蠢的)

3 个答案:

答案 0 :(得分:1)

我可以看到您的方法的性能问题,但我不确定我是否可以找到解释原因的确切方法。但实际上,您将光标的流添加到实例化而不是设置属性。如果我猜测一下,当你从阅读器中拉出作为构造函数的一部分时,你会引起一些上下文切换。

如果你认为一个阅读器是一个firehose光标(它是)并且想到由持有软管的用户(正常方法)而不是被填充的容器控制的firehose之间的区别,你开始得到一张照片问题。

不确定线程​​是如何相关的?但是,如果你有多个客户端并且通过将位移动到构造函数而不是在构造对象上设置属性,然后在多个请求之间争用线程时间来停止软管的流动,我可以设想一个偶数负载下的更大问题。

答案 1 :(得分:1)

嗯,CPU没有被超出的原因只是它在等待其他东西。磁盘或网络。

考虑到你的第一个代码块只是读取并立即丢弃一个值,有几种可能性:一个是编译器可以优化值变量的分配,因为它的范围有限而且从不用过的。这意味着处理器不必等待内存分配。相反,它主要受限于您从网络线上获取数据的速度。

通常情况下,内存分配应该超快,但是,如果你分配的内存量会导致窗口将内容推送到磁盘,那么这会被你的硬盘速度所阻碍。

查看你的第二个代码块,你构建了很多对象(更多的内存使用)并保持它们。这两者都不允许编译器优化调出;这两者都给本地系统资源带来了更大的压力,而不仅仅是处理器。

要确定上述内容是否接近,您需要将监视计数器放在应用程序使用的内存量与可用RAM量之间。此外,您还需要磁盘系统上的计数器,以查看它是否在任何一种情况下都被大量访问。

重点是,在代码块运行时,尝试并观察机器中正在发生的一切。这将使您更好地了解处理器等待的原因。

答案 2 :(得分:0)

也许只是将Bob和Clarence调用结合起来本质上不那么占用CPU资源,并且由于I / O瓶颈或者其他方面的原因,部分执行只需要一些时间。

您最好的选择是通过分析器运行此功能并查看报告。