- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
更新 3:根据 this announcement ,这已由 EF 团队在 EF6 alpha 2 中解决。
更新 2:我提出了解决此问题的建议。投票给它,go here .
考虑一个带有一个非常简单的表的 SQL 数据库。
CREATE TABLE Main (Id INT PRIMARY KEY)
我用 10,000 条记录填充表。
WITH Numbers AS
(
SELECT 1 AS Id
UNION ALL
SELECT Id + 1 AS Id FROM Numbers WHERE Id <= 10000
)
INSERT Main (Id)
SELECT Id FROM Numbers
OPTION (MAXRECURSION 0)
我为表构建了一个 EF 模型并在 LINQPad 中运行以下查询(我使用的是“C# 语句”模式,因此 LINQPad 不会自动创建转储)。
var rows =
Main
.ToArray();
执行时间约为 0.07 秒。现在我添加 Contains 运算符并重新运行查询。
var ids = Main.Select(a => a.Id).ToArray();
var rows =
Main
.Where (a => ids.Contains(a.Id))
.ToArray();
这个案例的执行时间是20.14 秒(慢了 288 倍)!
起初我怀疑为查询发出的 T-SQL 执行时间较长,因此我尝试将其从 LINQPad 的 SQL Pane 剪切并粘贴到 SQL Server Management Studio 中。
SET NOCOUNT ON
SET STATISTICS TIME ON
SELECT
[Extent1].[Id] AS [Id]
FROM [dbo].[Primary] AS [Extent1]
WHERE [Extent1].[Id] IN (1,2,3,4,5,6,7,8,...
结果是
SQL Server Execution Times:
CPU time = 0 ms, elapsed time = 88 ms.
接下来我怀疑是 LINQPad 导致了问题,但无论我在 LINQPad 还是在控制台应用程序中运行它,性能都是一样的。
因此,问题似乎出在 Entity Framework 的某个地方。
我是不是做错了什么?这是我代码中的时间关键部分,我可以做些什么来提高性能吗?
我正在使用 Entity Framework 4.1 和 Sql Server 2008 R2。
更新 1:
在下面的讨论中,有一些问题是关于延迟是在 EF 构建初始查询时还是在解析收到的数据时发生的。为了对此进行测试,我运行了以下代码,
var ids = Main.Select(a => a.Id).ToArray();
var rows =
(ObjectQuery<MainRow>)
Main
.Where (a => ids.Contains(a.Id));
var sql = rows.ToTraceString();
这会强制 EF 生成查询而不对数据库执行查询。结果是此代码需要约 20 秒才能运行,因此看起来几乎所有时间都花在了构建初始查询上。
那么 CompiledQuery 可以拯救吗?没那么快……CompiledQuery 要求传递给查询的参数是基本类型(int、string、float 等)。它不接受数组或 IEnumerable,因此我不能将它用于 ID 列表。
最佳答案
更新:通过在 EF6 中添加 InExpression,处理 Enumerable.Contains 的性能显着提高。不再需要此答案中描述的方法。
您是对的,大部分时间都花在处理查询的翻译上。 EF 的提供程序模型当前不包含表示 IN 子句的表达式,因此 ADO.NET 提供程序本身不能支持 IN。相反,Enumerable.Contains 的实现将其转换为 OR 表达式树,即 C# 中的内容如下所示:
new []{1, 2, 3, 4}.Contains(i)
...我们将生成一个可以表示如下的 DbExpression 树:
((1 = @i) OR (2 = @i)) OR ((3 = @i) OR (4 = @i))
(表达式树必须平衡,因为如果我们将所有 OR 都放在一个长脊柱上,表达式访问者将更有可能遇到堆栈溢出(是的,我们在测试中确实遇到了))
我们稍后将这样的树发送给 ADO.NET 提供程序,它可以识别此模式并在 SQL 生成期间将其缩减为 IN 子句。
当我们在 EF4 中添加对 Enumerable.Contains 的支持时,我们认为无需在提供程序模型中引入对 IN 表达式的支持就可以做到这一点,老实说,10,000 比我们预期的客户元素数量要多得多将传递给 Enumerable.Contains。也就是说,我知道这很烦人,而且在您的特定场景中,表达式树的操作会使事情变得过于昂贵。
我与我们的一位开发人员讨论了这个问题,我们相信将来我们可以通过添加对 IN 的一流支持来更改实现。我会确保将其添加到我们的积压工作中,但我无法保证何时会完成,因为我们还想进行许多其他改进。
对于线程中已经建议的解决方法,我将添加以下内容:
考虑创建一个方法来平衡数据库往返次数与您传递给 Contains 的元素数量。例如,在我自己的测试中,我观察到针对 SQL Server 的本地实例计算和执行具有 100 个元素的查询需要 1/60 秒。如果您可以这样编写查询,即使用 100 个不同的 id 集执行 100 个查询将得到与具有 10,000 个元素的查询相同的结果,那么您可以在大约 1.67 秒而不是 18 秒内获得结果。
不同的 block 大小应该根据查询和数据库连接的延迟更好地工作。对于某些查询,即如果传递的序列有重复项,或者如果在嵌套条件下使用 Enumerable.Contains,您可能会在结果中获得重复的元素。
这是一个代码片段(抱歉,如果用于将输入分割成 block 的代码看起来有点过于复杂。有更简单的方法可以实现同样的事情,但我试图想出一个模式来保留流序列,我在 LINQ 中找不到类似的东西,所以我可能做得太过火了 :) ):
用法:
var list = context.GetMainItems(ids).ToList();
上下文或存储库的方法:
public partial class ContainsTestEntities
{
public IEnumerable<Main> GetMainItems(IEnumerable<int> ids, int chunkSize = 100)
{
foreach (var chunk in ids.Chunk(chunkSize))
{
var q = this.MainItems.Where(a => chunk.Contains(a.Id));
foreach (var item in q)
{
yield return item;
}
}
}
}
切片可枚举序列的扩展方法:
public static class EnumerableSlicing
{
private class Status
{
public bool EndOfSequence;
}
private static IEnumerable<T> TakeOnEnumerator<T>(IEnumerator<T> enumerator, int count,
Status status)
{
while (--count > 0 && (enumerator.MoveNext() || !(status.EndOfSequence = true)))
{
yield return enumerator.Current;
}
}
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> items, int chunkSize)
{
if (chunkSize < 1)
{
throw new ArgumentException("Chunks should not be smaller than 1 element");
}
var status = new Status { EndOfSequence = false };
using (var enumerator = items.GetEnumerator())
{
while (!status.EndOfSequence)
{
yield return TakeOnEnumerator(enumerator, chunkSize, status);
}
}
}
}
希望这对您有所帮助!
关于c# - 为什么 Contains() 运算符会如此显着地降低 Entity Framework 的性能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7897630/
#include using namespace std; class C{ private: int value; public: C(){ value = 0;
这个问题已经有答案了: What is the difference between char a[] = ?string?; and char *p = ?string?;? (8 个回答) 已关闭
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 7 年前。 此帖子已于 8 个月
除了调试之外,是否有任何针对 c、c++ 或 c# 的测试工具,其工作原理类似于将独立函数复制粘贴到某个文本框,然后在其他文本框中输入参数? 最佳答案 也许您会考虑单元测试。我推荐你谷歌测试和谷歌模拟
我想在第二台显示器中移动一个窗口 (HWND)。问题是我尝试了很多方法,例如将分辨率加倍或输入负值,但它永远无法将窗口放在我的第二台显示器上。 关于如何在 C/C++/c# 中执行此操作的任何线索 最
我正在寻找 C/C++/C## 中不同类型 DES 的现有实现。我的运行平台是Windows XP/Vista/7。 我正在尝试编写一个 C# 程序,它将使用 DES 算法进行加密和解密。我需要一些实
很难说出这里要问什么。这个问题模棱两可、含糊不清、不完整、过于宽泛或夸夸其谈,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开,visit the help center . 关闭 1
有没有办法强制将另一个 窗口置于顶部? 不是应用程序的窗口,而是另一个已经在系统上运行的窗口。 (Windows, C/C++/C#) 最佳答案 SetWindowPos(that_window_ha
假设您可以在 C/C++ 或 Csharp 之间做出选择,并且您打算在 Windows 和 Linux 服务器上运行同一服务器的多个实例,那么构建套接字服务器应用程序的最明智选择是什么? 最佳答案 如
你们能告诉我它们之间的区别吗? 顺便问一下,有什么叫C++库或C库的吗? 最佳答案 C++ 标准库 和 C 标准库 是 C++ 和 C 标准定义的库,提供给 C++ 和 C 程序使用。那是那些词的共同
下面的测试代码,我将输出信息放在注释中。我使用的是 gcc 4.8.5 和 Centos 7.2。 #include #include class C { public:
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我的客户将使用名为 annoucement 的结构/类与客户通信。我想我会用 C++ 编写服务器。会有很多不同的类继承annoucement。我的问题是通过网络将这些类发送给客户端 我想也许我应该使用
我在 C# 中有以下函数: public Matrix ConcatDescriptors(IList> descriptors) { int cols = descriptors[0].Co
我有一个项目要编写一个函数来对某些数据执行某些操作。我可以用 C/C++ 编写代码,但我不想与雇主共享该函数的代码。相反,我只想让他有权在他自己的代码中调用该函数。是否可以?我想到了这两种方法 - 在
我使用的是编写糟糕的第 3 方 (C/C++) Api。我从托管代码(C++/CLI)中使用它。有时会出现“访问冲突错误”。这使整个应用程序崩溃。我知道我无法处理这些错误[如果指针访问非法内存位置等,
关闭。这个问题不符合Stack Overflow guidelines .它目前不接受答案。 我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。 关闭 7 年前。
已关闭。此问题不符合Stack Overflow guidelines 。目前不接受答案。 要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于 Stack Overflow 来说是偏离主题的,因为
我有一些 C 代码,将使用 P/Invoke 从 C# 调用。我正在尝试为这个 C 函数定义一个 C# 等效项。 SomeData* DoSomething(); struct SomeData {
这个问题已经有答案了: Why are these constructs using pre and post-increment undefined behavior? (14 个回答) 已关闭 6
我是一名优秀的程序员,十分优秀!