【问题标题】:SubSonic & LinqTemplate: Why is read so slow here?SubSonic & LinqTemplate:为什么这里读得这么慢?
【发布时间】:2011-05-07 01:52:30
【问题描述】:

在亚音速工作人员关闭论坛并说使用 stackoverflow 时,不确定从哪里获得这些信息,所以我们开始吧。

我一直在努力使用 ORM 实现最佳性能。我喜欢亚音速,它的批量插入、更新、删除在这种情况下消除了实体框架,但是对于直接简单的读取,EF 以 5 倍的速度将亚音速吹出水面。

我已经剥离了两者,删除了更改跟踪、外部属性、导航属性,一切都归结为 poco。

EF 在大约 4.8 中执行 1M 行的简单选择,SubSonic Linq 是 5 倍...

查看 SQL 探查器,调用略有不同: 实体框架:SELECT TOP (1000000) [c].[Id] AS [Id], [c].[ProjectIDL] AS [ProjectIDL], [c].[DescriptorIDL] AS [DescriptorIDL], [c].[FieldIDL] AS [FieldIDL], [c].[Data] AS [Data], [c].[OptionId] AS [OptionId] FROM [dbo].[DescriptorFieldValues] AS [c]

亚音速:SELECT TOP (1000000) [t0].[Data], [t0].[DescriptorIDL], [t0].[FieldIDL], [t0].[Id], [t0].[OptionId], [t0].[ProjectIDL] FROM [dbo].[DescriptorFieldValues] AS t0

SQL 分析器正在运行跟踪并在此处显示巨大的持续时间差异。
我在两个查询之前检查了审计登录,它们是相同的......

如果我在 sql management studio 中运行相同的查询,这两个查询都需要 11 秒

查看亚音速 3.04 源代码,我无法弄清楚我会在哪里进行调整以使其与 EF 匹配,但这里的查询语法真的有那么大的不同吗,或者我正在发生一些魔法不知道?

感谢您的帮助!

【问题讨论】:

  • “EF 在大约 4.8 中执行 1M 行的简单选择,SubSonic Linq 是 5 倍……” 至少,我们应该看到这个断言来自的代码,以及它是在什么条件下观察到的。我记得读过(但找不到源代码)SubSonic 使用了一些动态代码生成,因此最初编译的时间成本可能很高。另外,问题是,如果在管理工作室中运行需要 11 秒,您认为 EF 如何在“4.8”(4.8 什么,顺便说一句?)中返回查询?
  • 我指的是来自 SQL 监视器的时间 - 查看从 SQL Server 接收并处理它到返回结果的时间,至少我认为是这样的......跨度>
  • 你还没有建立任何真正的比较基础,我仍然对你从哪里得到数字感到很困惑。您是说 4.8 和“5x that”来自 SQL Monitor 报告的内容吗?如果您要询问性能,那么非常明确和详细会有所帮助。如果没有更多详细信息,我将无法再提供帮助。
  • 顺便说一句 - 我们构建了一些自定义模板并稍微编辑了 SubSonic,并且能够从中获得出色的性能。如果您需要帮助,请随时与我联系。

标签: subsonic subsonic3


【解决方案1】:

除了您的 SubSonic 查询似乎没有选择 ID 字段这一事实之外,这两个 SQL 语句的所有意图和目的都是相同的。发现这两个查询在 SQL Management Studio 中执行的时间相同,这似乎支持了这一点。

这似乎表明 SubSonic 实际上确实比 EF 需要更长的查询时间。 SubSonic is known to have some issues with performance 可以肯定地解释这种差异。

不过,实际上,我们需要更多关于您的确切用法的详细信息,才能真正确定为什么此查询对您来说速度较慢的答案。

另外,如果您正在寻找当前的活动并帮助使用 SubSonic,您应该尝试their Google Group

SubSonic 的创造者 Rob Conery 很久以前就停止了它的工作,并且“SubSonic 团队”还没有发布任何实质性更新(如果有的话,我认为他们没有,但我是不完全确定)。这是一个您不妨将其视为“已完成”的项目,因为该项目的工作已经停止(出于所有实际目的),但实际上并不需要更多的工作(它确实有效)。

顺便说一句,那个说 SubSonic 论坛的页面已关闭并来到 StackOverflow - 嗯,这些页面已经有一年多没有更新了,可能是 2 年。

【讨论】:

  • 虽然我很欣赏你的回答 qes,但它并没有解决我的实际问题,即在这种情况下,其中一个查询比另一个查询更快,也没有解决我如何能够在亚音速其他方面进行这种增强而不是与死去的亚音速队交谈。然而,这是朝着一个方向迈出的一大步,但希望获得有关查询性能问题的更多信息。
  • 好吧,您实际上并没有提供足够的具体信息来确定您看到性能差异的原因,因此任何人都可以做的最好的事情就是提供一般信息。也就是说,我意识到我的链接错误并更新了它。
【解决方案2】:

在被 Rob Corney 斥责为什么 SubSonic 比 EF 慢,并告诉我们想知道为什么 SubSonic 坏了是愚蠢的,我们相信我们的团队已经确定了这些性能问题的几个地方:

  1. 在 Extensions/Database.cs 中,加载使用每行、每个属性反射将数据行序列化为具体对象。
  2. 在 Extensions/Database.cs ToEnumerable 中,所有 DataReader 转换都在 1 个连续的 while 循环中完成。
  3. 在 ExecutionBuilder 中 - 此查询编译器是 Alpha 版的直接复制粘贴,仅用于教育目的的查询编译器,使用 DynamicInvoke 已严重过时。

我们的团队计划按以下顺序进行以下修改:

  1. 在 Extensions/Database.cs ToEnumerable 中将属性信息拉出一次并将其传递给负载,这被认为对性能的影响最小,但可能对内存利用率产生了不错的影响。

  2. 修改 ToEnumerable 以多线程实现大型数据集。

  3. 在对象工厂中添加一个方法,以允许在不使用反射的情况下从数据行构造对象,而是提前使用 t4 模板代码生成。
  4. 在加载内部,检查对象激活器是否有此接口方法,如果是则使用此,如果不是,则默认返回基于反射的序列化。
  5. 更新 ExecutionBuilder 以避免使用 DynamicInvoke。

希望这能实现我们的性能需求。

感谢 Jeff V、Ken I 和 QES 的帮助。很遗憾看到 SubSonic Rob Corney 的创造者对 SubSonic 的表现如此防御,而它似乎很容易解决。 ~JT

【讨论】:

  • 很高兴我能帮上忙。如果你最终让它更快,请发布补丁或 GitHub 拉取请求。 -JeffV (rally25rs)
  • 杰夫,再次感谢。为了做一些测试,我们自动生成了一个工厂,可以从数据读取器构造任何一种主要类型。我们正在检查物化以查看它是否是基本类型,如果是,则将其发送到工厂并绕过所有反射。我们看到性能超过了我们所见过的任何其他产品,并且内存利用率也降至最低。我们能够在一秒钟内轻松读取 1M。在空闲时间,我们将尝试将这一代提升到一个新的水平并使用反射.emit。我很乐意尽快发布我们的更改。
【解决方案3】:

Subsonic 3 之所以这么慢,是因为它需要compile again and again and again the same thinks 才能访问 sql server 并询问结果。

这个编译发生在 linq 部分。

例如像"Select * FROM Products WHERE ProductID > 100" 这样的简单静态命令,在subsonic 3 中输入Products.Find(x => (x.ProductID > 100) 首先需要很长时间才能转换为字符串命令,我的意思是很长时间。

这就是亚音速这么慢的主要原因,也是亚音速3一文不值的原因。

在 linq 中,诀窍是 CompiledQuery.Compile 函数,它编译一次并在内存中记住它。对我来说,我的标准也很慢。

现在回到亚音速 2,这是一个非常好的想法,但内部仍需要大量优化。显然他们并没有想太多的速度。无论如何,通过一些优化,亚音速 2 可以超快,接近简单的 ado 命令。

我一年前的测试: Benchmark Linq2SQL, Subsonic2, Subsonic3 - Any other ideas to make them faster?

我的想法是回到亚音速 2,让它变得更好更快,然后放弃亚音速 3。我感谢他们提供亚音速 2 和 3,但在第 3 版中他们失败了,没关系,我也做了那些年有很多程序,并不是所有的程序都使用过。没什么大不了的。

【讨论】:

  • 绝对是关于亚音速和动态查询编译的有效观点。在我的特定用例中,尽管我所有的读取都非常庞大,然后我主要进行小型轻量级插入/更新/删除调用。到目前为止,我发现对我来说最大的性能增强是扩展高级模板以为每个接受 DataReader 的类生成一个构造函数。然后在埋在亚音速的反序列化中,如果对象很简单,我将调用该数据读取器构造函数,而不是使用反射来填充对象。这让我轻松地将读取速度提高了 20 倍,几乎没有使用内存。
猜你喜欢
  • 1970-01-01
  • 2014-08-31
  • 2021-09-03
  • 1970-01-01
  • 1970-01-01
  • 2011-03-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多