构建linq查询以获得最快性能的正确方法?
这里也提出了类似的问题,但没有一个符合我的需要。
我做了测试用例,看看哪个更快。 但我觉得我的linq代码仍然很慢。 如何构建linq代码以获得更快的性能?
其他人说使用double .Tolist()
会导致操作变慢,当我测试它时,它表明它比任何其他测试都快。
测试:
Preparation --------------------------------------------------------------- return Properties of UserInfo(userinf, true){ UserID = userinf.UserID; FirstName = userinf.user.FirstName; MiddleName = userinf.user.MiddleName; LastName = userinf.user.LastName; LoginID = userinf.user.LoginID; Birthday = userinf.Birthday; } skip = 0; take = 100; total table records = 304; Linq to Entity Framework
提琴手:v2.4.0.0
https://127.0.0.1/..../RetrieveUserInfo?skip=0&take=100 { "client":{ "SessionID":"5433ab64-7e0d-444f-b886-a901ea9a0601" }, "session":{ "SessionID":"35b75daa-25ad-45a4-9f99-0e69ec3b66a4" } }
//Test 1 //1) 00:00:15.3068755 -- Attempt1 //2) 00:00:13.8207905 -- Attempt2 //3) 00:00:16.2489294 -- Attempt3 var list = (from usr in dbase.userinfoes select usr).OrderBy(i => i.UserID).Skip(skip).Take(take).ToList(); userlist = (from i in list select new UserInfo(i, true)).ToList(); ///Test 2 //1) 00:00:15.3908803 //2) 00:00:14.8818512 //3) 00:00:19.4761140 var list = (from usr in dbase.userinfoes.AsEnumerable().OrderBy(i => i.UserID).Skip(skip).Take(take).ToList() select new UserInfo(usr, true)).ToList(); //Test 3 //1) 00:00:30.1937270 //2) 00:00:24.1003784 //3) 00:00:28.8806519 var list = dbase.userinfoes.OrderBy(i => i.UserID).Skip(skip).Take(take).ToList(); userlist = (from i in list select new UserInfo(i, true)).ToList(); //Test 4 //1) 00:00:57.2652754 //2) 00:00:54.4051118 //3) 00:00:55.3251644 var list = (from usr in dbase.userinfoes select usr).ToList(); userlist = (from i in list select new UserInfo(i, true)).OrderBy(i => i.UserID).Skip(skip).Take(take).ToList(); //Test 5 //1) 00:01:06.8378229 //2) 00:01:01.2845053 //3) 00:00:55.0721499 var list = from usr in dbase.userinfoes select usr; userlist = (from i in list.AsEnumerable() select new UserInfo(i, true)).OrderBy(i => i.UserID).Skip(skip).Take(take).ToList(); // Test 6 // VERY LONG. It tooks all records first and construct UserInfo one by one before doing the skip and take var list = (from usr in dbase.userinfoes.AsEnumerable() select new UserInfo(usr, true)).OrderBy(i => i.UserID).Skip(skip).Take(take).ToList(); //Test 7 // VERY LONG. It tooks all records first and construct UserInfo one by one before doing the skip and take var list = from usr in dbase.userinfoes.AsEnumerable() select new UserInfo(usr);
适合快速搜索的代码。 感谢casperOne
指出, 在服务器上执行的排序,跳过和取得更快。
这是最终的代码:
var list = (from usr in dbase.userinfoes .OrderBy(i => i.UserID) .Skip(skip) .Take(take) .AsEnumerable() select new UserInfo(usr, true)).ToList(); 1) 00:00:10.9210513 2) 00:00:10.8270973 3) 00:00:10.8250151
感谢Richard Neil Ilagan
的最终代码。
这就是为什么每个人都表现得如此以及为什么你会看到你所看到的:
测试1:
///Test 2 //1) 00:00:15.3068755 -- Attempt1 //2) 00:00:13.8207905 -- Attempt2 //3) 00:00:16.2489294 -- Attempt3 var list = (from usr in dbase.userinfoes select usr).OrderBy(i => i.UserID).Skip(skip).Take(take).ToList(); userlist = (from i in list select new UserInfo(i, true)).ToList();
这绝对是最快的。 它是最快的,因为订购,跳过和取消都在服务器上执行。 因为你可能有索引,所以服务器很强大(function强大)等等。它可以比你在客户端实现整个集合然后在那里执行操作更快地处理这些操作。
UserInfo
仅在后处理列表上构建。
测试2:
///Test 2 //1) 00:00:15.3908803 //2) 00:00:14.8818512 //3) 00:00:19.4761140 var list = ( from usr in dbase.userinfoes.AsEnumerable(). OrderBy(i => i.UserID).Skip(skip).Take(take).ToList() select new UserInfo(usr, true) ).ToList();
这应该具有与测试7相同的性能影响; 对AsEnumerable
的调用强制所有后续操作都在内存中执行(对OrderBy
的调用将要求您在订购之前实现所有实例)。
这有点像exception。 我很想知道发送到服务器的SQL是什么(假设您正在使用SQL服务器或一些基于SQL的后端),以确保它正在选择所有记录。
UserInfo
仅在后处理列表上构建。
测试3:
//Test 3 //1) 00:00:30.1937270 //2) 00:00:24.1003784 //3) 00:00:28.8806519 var list = dbase.userinfoes.OrderBy(i => i.UserID). Skip(skip).Take(take).ToList(); userlist = (from i in list select new UserInfo(i, true)).ToList();
同样,order by,skip和take正在服务器上进行。 你实现了两次列表(你有两次ToList
调用),这是我可以看到的开销的唯一解释。
UserInfo
仅在后处理列表上构建。
测试4:
//Test 4 //1) 00:00:57.2652754 //2) 00:00:54.4051118 //3) 00:00:55.3251644 var list = (from usr in dbase.userinfoes select usr).ToList(); userlist = (from i in list select new UserInfo(i, true)). OrderBy(i => i.UserID).Skip(skip).Take(take).ToList();
你现在正在内存中实现整个列表,所以现在有更多的开销。
UserInfo
在预处理列表上构建。
测试5:
//Test 5 //1) 00:01:06.8378229 //2) 00:01:01.2845053 //3) 00:00:55.0721499 var list = from usr in dbase.userinfoes select usr; userlist = (from i in list.AsEnumerable() select new UserInfo(i, true)). OrderBy(i => i.UserID).Skip(skip).Take(take).ToList();
与测试二相同,您在客户端执行所有操作。
UserInfo
在预处理列表上构建。
测试6:
// Test 6 // VERY LONG. It tooks all records first and construct // UserInfo one by one before doing the skip and take var list = (from usr in dbase.userinfoes.AsEnumerable() select new UserInfo(usr, true)). OrderBy(i => i.UserID).Skip(skip).Take(take).ToList();
UserInfo
在预处理列表上构建。
再次,在客户端执行所有操作。
// Test 7 // VERY LONG. It tooks all records first and construct // UserInfo one by one before doing the skip and take var list = from usr in dbase.userinfoes.AsEnumerable() select new UserInfo(usr);
再次,在客户端执行所有操作。
UserInfo
在预处理列表上构建。
在所有这些测试中我注意到了一个区别,那就是你调用UserInfo
实例的构造函数的地方。 在性能良好的地方,你推迟尽可能晚地构建UserInfo
的实例(在执行命令,执行,跳过操作之后),而当性能不好时,你是在UserInfo
构建UserInfo
实例,在进行这些操作之前(通常会对UserInfo
构造函数进行更多调用)。
也就是说,似乎您的性能问题可能存在于UserInfo
类的构造函数中,而不是LINQ中。 通常,当您让IQueryable
提供程序对基础数据源执行操作时, 通常比在客户端的内存中执行这些操作更快。
虽然没有看到构造函数代码,但这是不可能的,但是你的数字肯定表明问题存在于那里,而不是在LINQ中。