为什么DataTable比DataReader更快

2024-04-05

因此,我们在工作中就采取哪种DataAccess途径进行了激烈的争论:DataTable还是DataReader。

免责声明我站在 DataReader 一边,这些结果震撼了我的世界。

我们最终编写了一些基准测试来测试速度差异。人们普遍认为 DataReader 更快,但我们想看看快了多少。

结果让我们感到惊讶。 DataTable 始终比 DataReader 快。有时接近两倍的速度。

所以我向你们求助,SO 的成员。为什么大多数文档甚至 Microsoft 都声明 DataReader 更快,但我们的测试却显示出相反的结果。

现在是代码:

测试线束:

    private void button1_Click(object sender, EventArgs e)
    {
        System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
        sw.Start();

        DateTime date = DateTime.Parse("01/01/1900");

        for (int i = 1; i < 1000; i++)
        {

            using (DataTable aDataTable = ArtifactBusinessModel.BusinessLogic.ArtifactBL.RetrieveDTModified(date))
            {
            }
        }
        sw.Stop();
        long dataTableTotalSeconds = sw.ElapsedMilliseconds;

        sw.Restart();


        for (int i = 1; i < 1000; i++)
        {
            List<ArtifactBusinessModel.Entities.ArtifactString> aList = ArtifactBusinessModel.BusinessLogic.ArtifactBL.RetrieveModified(date);

        }

        sw.Stop();

        long listTotalSeconds = sw.ElapsedMilliseconds;

        MessageBox.Show(String.Format("list:{0}, table:{1}", listTotalSeconds, dataTableTotalSeconds));
    }

这是 DataReader 的 DAL:

        internal static List<ArtifactString> RetrieveByModifiedDate(DateTime modifiedLast)
        {
            List<ArtifactString> artifactList = new List<ArtifactString>();

            try
            {
                using (SqlConnection conn = SecuredResource.GetSqlConnection("Artifacts"))
                {
                    using (SqlCommand command = new SqlCommand("[cache].[Artifacts_SEL_ByModifiedDate]", conn))
                    {
                        command.CommandType = CommandType.StoredProcedure;
                        command.Parameters.Add(new SqlParameter("@LastModifiedDate", modifiedLast));
                        using (SqlDataReader reader = command.ExecuteReader())
                        {
                            int formNumberOrdinal = reader.GetOrdinal("FormNumber");
                            int formOwnerOrdinal = reader.GetOrdinal("FormOwner");
                            int descriptionOrdinal = reader.GetOrdinal("Description");
                            int descriptionLongOrdinal = reader.GetOrdinal("DescriptionLong");
                            int thumbnailURLOrdinal = reader.GetOrdinal("ThumbnailURL");
                            int onlineSampleURLOrdinal = reader.GetOrdinal("OnlineSampleURL");
                            int lastModifiedMetaDataOrdinal = reader.GetOrdinal("LastModifiedMetaData");
                            int lastModifiedArtifactFileOrdinal = reader.GetOrdinal("LastModifiedArtifactFile");
                            int lastModifiedThumbnailOrdinal = reader.GetOrdinal("LastModifiedThumbnail");
                            int effectiveDateOrdinal = reader.GetOrdinal("EffectiveDate");
                            int viewabilityOrdinal = reader.GetOrdinal("Viewability");
                            int formTypeOrdinal = reader.GetOrdinal("FormType");
                            int inventoryTypeOrdinal = reader.GetOrdinal("InventoryType");
                            int createDateOrdinal = reader.GetOrdinal("CreateDate");

                            while (reader.Read())
                            {
                                ArtifactString artifact = new ArtifactString();
                                ArtifactDAL.Map(formNumberOrdinal, formOwnerOrdinal, descriptionOrdinal, descriptionLongOrdinal, formTypeOrdinal, inventoryTypeOrdinal, createDateOrdinal, thumbnailURLOrdinal, onlineSampleURLOrdinal, lastModifiedMetaDataOrdinal, lastModifiedArtifactFileOrdinal, lastModifiedThumbnailOrdinal, effectiveDateOrdinal, viewabilityOrdinal, reader, artifact);
                                artifactList.Add(artifact);
                            }
                        }
                    }
                }
            }
            catch (ApplicationException)
            {
                throw;
            }
            catch (Exception e)
            {
                string errMsg = String.Format("Error in ArtifactDAL.RetrieveByModifiedDate. Date: {0}", modifiedLast);
                Logging.Log(Severity.Error, errMsg, e);
                throw new ApplicationException(errMsg, e);
            }

            return artifactList;
        }
    internal static void Map(int? formNumberOrdinal, int? formOwnerOrdinal, int? descriptionOrdinal, int? descriptionLongOrdinal, int? formTypeOrdinal, int? inventoryTypeOrdinal, int? createDateOrdinal,
        int? thumbnailURLOrdinal, int? onlineSampleURLOrdinal, int? lastModifiedMetaDataOrdinal, int? lastModifiedArtifactFileOrdinal, int? lastModifiedThumbnailOrdinal,
        int? effectiveDateOrdinal, int? viewabilityOrdinal, IDataReader dr, ArtifactString entity)
    {

            entity.FormNumber = dr[formNumberOrdinal.Value].ToString();
            entity.FormOwner = dr[formOwnerOrdinal.Value].ToString();
            entity.Description = dr[descriptionOrdinal.Value].ToString();
            entity.DescriptionLong = dr[descriptionLongOrdinal.Value].ToString();
            entity.FormType = dr[formTypeOrdinal.Value].ToString();
            entity.InventoryType = dr[inventoryTypeOrdinal.Value].ToString();
            entity.CreateDate = DateTime.Parse(dr[createDateOrdinal.Value].ToString());
            entity.ThumbnailURL = dr[thumbnailURLOrdinal.Value].ToString();
            entity.OnlineSampleURL = dr[onlineSampleURLOrdinal.Value].ToString();
            entity.LastModifiedMetaData = dr[lastModifiedMetaDataOrdinal.Value].ToString();
            entity.LastModifiedArtifactFile = dr[lastModifiedArtifactFileOrdinal.Value].ToString();
            entity.LastModifiedThumbnail = dr[lastModifiedThumbnailOrdinal.Value].ToString();
            entity.EffectiveDate = dr[effectiveDateOrdinal.Value].ToString();
            entity.Viewability = dr[viewabilityOrdinal.Value].ToString();
    }

这是数据表的 DAL:

        internal static DataTable RetrieveDTByModifiedDate(DateTime modifiedLast)
        {
            DataTable dt= new DataTable("Artifacts");

            try
            {
                using (SqlConnection conn = SecuredResource.GetSqlConnection("Artifacts"))
                {
                    using (SqlCommand command = new SqlCommand("[cache].[Artifacts_SEL_ByModifiedDate]", conn))
                    {
                        command.CommandType = CommandType.StoredProcedure;
                        command.Parameters.Add(new SqlParameter("@LastModifiedDate", modifiedLast));

                        using (SqlDataAdapter da = new SqlDataAdapter(command))
                        {
                            da.Fill(dt);
                        }
                    }
                }
            }
            catch (ApplicationException)
            {
                throw;
            }
            catch (Exception e)
            {
                string errMsg = String.Format("Error in ArtifactDAL.RetrieveByModifiedDate. Date: {0}", modifiedLast);
                Logging.Log(Severity.Error, errMsg, e);
                throw new ApplicationException(errMsg, e);
            }

            return dt;
        }

结果:

对于测试框架内的 10 次迭代

对于测试框架内的 1000 次迭代

这些结果是第二次运行,以减少由于创建连接而产生的差异。


我看到三个问题:

  1. 使用 DataReader 的方式通过将其转换为列表来否定它在内存中的单个项目的巨大优势,
  2. 您在与生产环境显着不同的环境中运行基准测试,其方式有利于数据表,并且
  3. 您花费时间将 DataReader 记录转换为 DataTable 代码中不重复的 Artifact 对象。

DataReader 的主要优点是您不必一次将所有内容加载到内存中。这对于 Web 应用程序中的 DataReader 来说应该是一个巨大的优势,其中内存(而不是 CPU)通常是瓶颈,但通过将每一行添加到通用列表中,您就否定了这一点。这也意味着,即使您将代码更改为一次仅使用一条记录,差异也可能不会显示在基准测试中,因为您在具有大量可用内存的系统上运行它们,这将有利于数据表。此外,DataReader 版本花费时间将结果解析为 DataTable 尚未完成的 Artifact 对象。

要解决 DataReader 使用问题,请更改List<ArtifactString> to IEnumerable<ArtifactString>到处,并在您的 DataReader DAL 中更改此行:

artifactList.Add(artifact);

to this:

yield return artifact;

这意味着您还需要将迭代结果的代码添加到 DataReader 测试工具中以保持公平。

我不知道如何调整基准来创建一个对 DataTable 和 DataReader 都公平的更典型的场景,除了构建页面的两个版本,并在类似的生产级别负载下为每个版本提供一个小时,以便我们确实有内存压力……做一些真正的 A/B 测试。另外,请确保您涵盖了将 DataTable 行转换为 Artifacts...如果争论是您需要为 DataReader 执行此操作,但不需要为 DataTable 执行此操作,那么这完全是错误的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么DataTable比DataReader更快 的相关文章

  • 如何在.NET中使用java.util.zip.Deflater解压缩放气流?

    之后我有一个转储java util zip Deflater 可以确认它是有效的 因为 Java 的Inflater打开它很好 并且需要在 NET中打开它 byte content ReadSample sampleName var inp
  • 如何在 SqlDataReader.Read() 期间从死锁异常中恢复

    我的 NET 应用程序的事件日志显示 它在从 Sql Server 读取数据时偶尔会出现死锁 这种情况通常非常罕见 因为我们已经优化了查询以避免死锁 但有时仍然会发生 过去 我们在调用ExecuteReader函数在我们的SqlComman
  • 如何禁用 Alt + F4 关闭表单?

    What is the best way to disable Alt F4 in a c win form to prevent the user from closing the form 我使用表单作为弹出对话框来显示进度条 但我不希
  • 两个 RichTextBox 具有相同的滚动条

    是否有任何可用的第三方工具有两个富文本框 但两者只有一个共享滚动条 我需要用两种不同的语言实现一些文本 但两个文本框应该同时滚动 public enum ScrollBarType uint SbHorz 0 SbVert 1 SbCtl
  • C# 如何使用 CallNtPowerInformation 和 Interop 来获取 SYSTEM_POWER_INFORMATION

    我正在尝试编写一个小程序 该程序作为服务运行并监视用户是否处于活动状态 如果用户空闲 没有鼠标 键盘 一个小时 则某些进程将被终止 如果用户使用 user32 dll 中的 LASTINPUTINFO 运行它 它就可以工作 但它不能作为服务
  • 为什么在 C# 中使用 String.Concat()?

    我想知道这个问题有一段时间了 为什么使用String Concat 而不是使用 操作员 我明白了String Format因为它是一个空洞使用 运算符并使您的代码看起来更好 例如 string one bob string two jim
  • WCF WebHttp 混合身份验证(基本和匿名)

    所有这些都与 WebHttp 绑定有关 托管在自定义服务主机中 IIS 目前不是一个选项 我已经实现了自定义 UserNamePasswordValidator 和自定义 IAuthorizationPolicy 当我将端点的绑定配置为使用
  • 将 dll 注册到 GAC 或从 ASP.NET 中的 bin 文件夹引用它们是否更好

    如果答案是 视情况而定 您能否提供一个简短的解释 GAC 旨在包含以下组件跨多个应用程序共享 如果是这种情况 您应该对程序集进行强命名并向 GAC 注册 如果不是 请将程序集保留为私有程序集并将其作为项目 dll 引用进行引用 PS 没有真
  • vb.net HtmlAgilityPack 在 div 之后插入字符串

    我试图在 div 末尾直接插入一些我自己的 html 这个 div 里面有其他 div Dim HtmlNode As HtmlNode HtmlNode CreateNode span class Those were the frien
  • Mono 在实际应用中的应用有多广泛?

    跟进评论问题here https stackoverflow com questions 3736101 what applications had better be developed in c over c in todays bus
  • 如何使用 WebResponse 下载 .wmv 文件

    我使用以下代码通过 WebResponse 获取 wmv 文件 我正在使用一个线程来调用这个函数 static void GetPage object data Cast the object to a ThreadInfo ThreadI
  • 如何 XML 序列化 DateTimeOffset 属性?

    The DateTimeOffset当数据表示为 Xml 时 我在此类中拥有的属性不会呈现 我需要做什么来告诉 Xml 序列化将其正确呈现为DateTime or DateTimeOffset XmlRoot playersConnecte
  • project.json 等效于 InternalsVisibleTo

    Net Core 的项目 json https learn microsoft com en us dotnet articles core tools project json copyright允许配置传统 Net 应用程序使用通常放置
  • 忽略挂起的更改中的某些文件

    这是我的问题 我已经更改了解决方案中的某些文件 假设是 Web config 并且永远不想签入 因为这些更改仅涉及我的计算机 有没有办法在 TFS 中忽略某个文件中的更改并将其从挂起的更改窗口中删除 当然 我可以在每次签入时跳过这个文件 但
  • C# 中 DLL 和命名空间的关系

    这里有一个高级问题 今天我花了很多时间自学基本的高级概念 例如 API 静态和动态库 DLL 以及 C 中的编组 获得所有这些知识让我想到了一个看起来非常基本的问题 并且可能表明我对这些概念的理解存在漏洞 我知道的 DLL 可能包含类 这些
  • Wix - 自定义安装目录

    我使用的是 Wix 3 x 用户应该能够选择目标目录 我的Setup wxs目前是这样的 http pastebin com uH1EjbDQ http pastebin com uH1EjbDQ 询问用户自定义目标目录的最简单方法是什么
  • Python tkinter.filedialog Askfolder 干扰 clr

    我主要在 Spyder 中工作 构建需要弹出文件夹或文件浏览窗口的脚本 下面的代码在spyder中完美运行 在 Pycharm 中 askopenfilename工作良好 同时askdirectory什么都不做 卡住了 但是 如果在调试模式
  • 获取从属性构造函数内部应用到哪个属性的成员?

    我有一个自定义属性 在自定义属性的构造函数内 我想将属性的属性值设置为属性所应用到的属性的类型 是否有某种方式可以访问该属性所应用到的成员从我的属性类内部 可以从 NET 4 5 using CallerMemberName Somethi
  • 过期时自动重新填充缓存

    我当前缓存方法调用的结果 缓存代码遵循标准模式 如果存在 则使用缓存中的项目 否则计算结果 在返回之前将其缓存以供将来调用 我想保护客户端代码免受缓存未命中的影响 例如 当项目过期时 我正在考虑生成一个线程来等待缓存对象的生命周期 然后运行
  • 哪些属性有助于运行时 .Net 性能?

    我正在寻找可用于通过向加载器 JIT 编译器或 ngen 提供提示来确保 Net 应用程序获得最佳运行时性能的属性 例如我们有可调试属性 http msdn microsoft com en us library k2wxda47 aspx

随机推荐