Hi anh em, chúc mọi người một ngày làm việc hiệu quả và đây năng lượng. Hôm nay mình sẽ nói về một số kinh nghiệm của mình khi làm việc với LINQ C#.
Không để anh em chờ lâu, cùng bắt đầu vào bài viết nào. Bài viết này mình sẽ nói các tool để theo dõi performance khi sử dụng linq và một số lưu ý về việc viết linq trong C#. Về cơ bản linq anh em có thể xem ở bài viết trước của mình: https://viblo.asia/p/linq-trong-c-6J3ZgpgxlmB
1. Source code Linq của Microsoft
- Github: https://github.com/microsoft/referencesource/tree/master/System.Core/System/Linq
- Anh em có thể vào đọc để hiểu thêm cách linq hoạt động và code c# của các method linq.
Ví dụ: ta có thể xem method Count() viết như thế nào
2. Một số tool để monitor performance khi sử dụng linq
-
SQL Server Profiler: Cho phép ghi lại và phân tích câu truy vấn được tạo ra từ Linq
-
LINQPad: https://www.linqpad.net/ một công cụ giúp viết và test linq query, phân tích performance và thời gian execute của query.
-
Stopwatch class: Đo thời gian chạy trước và sau khi query thực thi.
-
BenchMark Dotnet: cho phép so sánh thời gian chạy giữa nhiều function. https://github.com/dotnet/BenchmarkDotNet
3. Sử dụng IQueryable thay cho IEnumerable
- Iqueryable cho phép build query và execute query dựa trên database thay vì thực thi trên memory nếu sử dụng IEnumerable. Điều này giúp cải thiện performance khi query với data lớn.
Ví dụ:
- Ta có list chứa 1000000 customer và ta so sánh peformance khi sử dụng IQueryable và IEnumerable
var customers = new List<Customer>();
for (int i = 0; i < 1000000; i++)
{ customers.Add(new Customer { FirstName = $"First{i}", LastName = $"Last{i}" });
}
//Sử dụng IEnumerable
var stopwatch = new Stopwatch(); stopwatch.Start();
var results = customers.Where(c => c.LastName.StartsWith("S")).ToList();
stopwatch.Stop(); Console.WriteLine($"IEnumerable took {stopwatch.ElapsedMilliseconds}ms"); //Sử dụng IQueryable
stopwatch.Restart();
var results2 = customers.AsQueryable().Where(c => c.LastName.StartsWith("S")).ToList();
stopwatch.Stop(); Console.WriteLine($"IQueryable took {stopwatch.ElapsedMilliseconds}ms");
**=> Kết quả: **
IEnumerable took 256ms
IQueryable took 24ms
4. Sử dụng method syntax thay vì query syntax
- Sử dụng method syntax nhanh hơn query syntax bởi vì query syntax sẽ được C# compiler chuyển đổi thành method syntax trước khi nó thực thi.
- Tuy nhiên performance giữa method syntax và query syntax không đáng kể, nhưng best practice khi làm việc với linq là sử dung method syntax đặc biệt với lượng data lớn.
Ví dụ:
- Ta có list 1000000 orders
var orders = new List<Order>();
for (int i = 0; i < 1000000; i++)
{ orders.Add(new Order { Total = i * 0.01m });
}
var stopwatch = new Stopwatch(); //Query syntax
stopwatch.Start();
var querySyntaxResult = from o in orders where o.Total > 1000 select o;
stopwatch.Stop(); Console.WriteLine($"Query syntax took {stopwatch.ElapsedMilliseconds}ms"); //Method syntax
stopwatch.Restart();
var methodSyntaxResult = orders.Where(o => o.Total > 1000);
stopwatch.Stop(); Console.WriteLine($"Method syntax took {stopwatch.ElapsedMilliseconds}ms");
=> Kết quả:
Query syntax took 71ms
Method syntax took 6ms
5. Cẩn thận khi sử dụng GroupBy
- Group by tốn rất nhiều chi phí nên nếu ta lạm dụng có thể làm cho performance không tốt.
Ví dụ:
- Ta có một list orders của các customers và mỗi customer có thể có ít nhất 1 order. Yêu cầu: lấy ra tổng số lượng order của mỗi customer.
- Ta sẽ viết theo 2 cách và dùng benchMark để so sánh performance:
//Group by query
var orders = dbContext.Orders .GroupBy(o => o.Customer) .Select(g => new { Customer = g.Key, OrderCount = g.Count() });
//Target query
var customers = dbContext.Customers .Where(c => c.Orders.Any()) .Select(c => new { Customer = c, OrderCount = c.Orders.Count() });
=> Kết quả:
6. Sử dụng AsNoTracking trong Linq Entity framework
- Sử dụng AsNoTracking, Entity framework sẽ không track lại những thay đổi của entites. Điều này giúp cải thiện performance và giảm sử dụng bộ nhớ khi get dữ liệu bởi vì get dữ liệu không cần phải track lại những thay đổi. Ví dụ:
using System;
using System.Diagnostics;
using System.Linq; namespace AsNoTrackingBenchmark
{ class Program { static void Main(string[] args) { using (var db = new MyDbContext()) { // warmup db.Customers.AsNoTracking().Where(c => c.LastName == "Smith").ToList(); db.Customers.Where(c => c.LastName == "Smith").ToList(); var stopwatch = new Stopwatch(); // AsNoTracking stopwatch.Start(); for (int i = 0; i < 1000; i++) { db.Customers.AsNoTracking().Where(c => c.LastName == "Smith").ToList(); } stopwatch.Stop(); Console.WriteLine($"AsNoTracking: {stopwatch.ElapsedMilliseconds} ms"); // tracking stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < 1000; i++) { db.Customers.Where(c => c.LastName == "Smith").ToList(); } stopwatch.Stop(); Console.WriteLine($"Tracking: {stopwatch.ElapsedMilliseconds} ms"); } } }
}
=> Kết quả:
AsNoTracking: 194 ms
Tracking: 298 ms
7. Sử dụng Parallel LINQ
- Sử dụng Parallel LINQ có thể cải thiện performance của linq. Sau đây là 1 ví dụ:
var numbers = Enumerable.Range(1, 1000000);
var stopwatch = new Stopwatch(); // Measure time for LINQ query
stopwatch.Start();
var result1 = numbers.Where(n => n % 2 == 0).Sum();
stopwatch.Stop();
Console.WriteLine($"Time for LINQ query: {stopwatch.ElapsedMilliseconds} ms"); // Measure time for PLINQ query
stopwatch.Reset();
stopwatch.Start();
var result2 = numbers.AsParallel().Where(n => n % 2 == 0).Sum();
stopwatch.Stop();
Console.WriteLine($"Time for PLINQ query: {stopwatch.ElapsedMilliseconds} ms");
=>Kết quả:
Time for LINQ query: 11 ms
Time for PLINQ query: 3 ms
8. Linq performance trong .NET 7
- Ở phần này mình sẽ dùng benchmark so sánh performance giữa .net 6 và .net 7. Ở đây mình tham khảo page: https://code-maze.com/linq-performance-dotnet7/
- Kết quả của 1 số methods:
-
Min(), Max(), Average(), Count(), và Sum()
-
First() và Last()
-
Filtering Methods – Distinct() và Where()
-
9. Tổng kết
- Qua bài viết này hy vọng mọi người hiểu thêm về LINQ và giúp ích được anh em với một số tips.
- Cảm ơn mọi người đã xem bài viết. Chúc anh em một ngày làm việc hiệu quả và đầy năng lượng. Nếu có thắc mắc về các phần trong bài này mọi người có thể inbox qua facebook:https://www.facebook.com/FriendsCode-108096425243996 Mình sẽ giải đáp thắc mắc trong tầm hiểu biết. Cảm ơn mọi người!
- Hoặc liên hệ mình qua facebook cá nhân: https://www.facebook.com/Flamesofwars/