.NET 性能优化指南
性能优化原则
优化准则
- 先测量,再优化 - 使用性能分析工具找到瓶颈
- 关注热点 - 80/20 原则,优化影响最大的 20%
- 权衡取舍 - 性能 vs 可维护性
内存优化
1. 避免不必要的对象分配
- ❌ 性能差
- ✅ 性能好
// 差:每次调用都创建新对象
public string GetFullName(string firstName, string lastName)
{
return string.Format("{0} {1}", firstName, lastName);
}
// 差:字符串拼接产生临时对象
public string BuildQuery(List<string> items)
{
string result = "";
foreach (var item in items)
{
result += item + ",";
}
return result;
}
// 好:使用字符串插值
public string GetFullName(string firstName, string lastName)
{
return $"{firstName} {lastName}";
}
// 好:使用 StringBuilder
public string BuildQuery(List<string> items)
{
var sb = new StringBuilder();
foreach (var item in items)
{
sb.Append(item).Append(',');
}
return sb.ToString();
}
2. 使用 Span<T> 和 Memory<T>
// 传统方式:创建子字符串会分配新内存
public string GetDomain(string email)
{
var index = email.IndexOf('@');
return email.Substring(index + 1);
}
// 优化:使用 Span<T> 避免分配
public ReadOnlySpan<char> GetDomain(string email)
{
var index = email.IndexOf('@');
return email.AsSpan(index + 1);
}
// 使用示例
var email = "user@example.com";
var domain = GetDomain(email);
// domain 是 "example.com" 的视图,没有分配新内存
3. 对象池化
// 使用 ArrayPool 重用数组
public void ProcessData(int size)
{
var pool = ArrayPool<byte>.Shared;
byte[] buffer = pool.Rent(size);
try
{
// 使用 buffer
ProcessBuffer(buffer, size);
}
finally
{
pool.Return(buffer);
}
}
// 自定义对象池
public class ObjectPool<T> where T : class, new()
{
private readonly ConcurrentBag<T> _objects = new();
private readonly Func<T> _factory;
public ObjectPool(Func<T> factory = null)
{
_factory = factory ?? (() => new T());
}
public T Rent()
{
return _objects.TryTake(out var item) ? item : _factory();
}
public void Return(T item)
{
_objects.Add(item);
}
}
4. 值类型 vs 引用类型
// 大量小对象用 struct 可以减少 GC 压力
public readonly struct Point
{
public int X { get; }
public int Y { get; }
public Point(int x, int y)
{
X = x;
Y = y;
}
}
// 注意:struct 大小超过 16 字节时考虑使用 class
数据库优化
1. EF Core 查询优化
// ❌ 差:N+1 查询
var users = await _context.Users.ToListAsync();
foreach (var user in users)
{
var orders = await _context.Orders
.Where(o => o.UserId == user.Id)
.ToListAsync();
}
// ✅ 好:使用 Include 预加载
var users = await _context.Users
.Include(u => u.Orders)
.ToListAsync();
// ✅ 好:只查询需要的字段
var users = await _context.Users
.Select(u => new
{
u.Id,
u.Name,
OrderCount = u.Orders.Count
})
.ToListAsync();
// ✅ 好:只读查询使用 AsNoTracking
var users = await _context.Users
.AsNoTracking()
.ToListAsync();
2. 批量操作
// ❌ 差:逐条插入
foreach (var user in users)
{
_context.Users.Add(user);
await _context.SaveChangesAsync();
}
// ✅ 好:批量插入
_context.Users.AddRange(users);
await _context.SaveChangesAsync();
// ✅ 更好:使用 BulkExtensions
await _context.BulkInsertAsync(users);
await _context.BulkUpdateAsync(users);
3. 编译查询
private static readonly Func<AppDbContext, int, Task<User>> _getUserById =
EF.CompileAsyncQuery((AppDbContext context, int id) =>
context.Users.FirstOrDefault(u => u.Id == id));
public async Task<User> GetUserByIdAsync(int id)
{
return await _getUserById(_context, id);
}
异步优化
1. 避免同步阻塞
// ❌ 差:阻塞线程
public void ProcessData()
{
var data = GetDataAsync().Result; // 死锁风险
Process(data);
}
// ✅ 好:一路异步
public async Task ProcessDataAsync()
{
var data = await GetDataAsync();
Process(data);
}
2. 并行执行独立任务
// ❌ 差:顺序执行
var user = await GetUserAsync(userId);
var orders = await GetOrdersAsync(userId);
var products = await GetProductsAsync();
// 总耗时 = time1 + time2 + time3
// ✅ 好:并行执行
var userTask = GetUserAsync(userId);
var ordersTask = GetOrdersAsync(userId);
var productsTask = GetProductsAsync();
await Task.WhenAll(userTask, ordersTask, productsTask);
var user = userTask.Result;
var orders = ordersTask.Result;
var products = productsTask.Result;
// 总耗时 = max(time1, time2, time3)
3. ValueTask 优化
// 可能同步完成的操作使用 ValueTask
public ValueTask<User> GetUserAsync(int id)
{
// 检查缓存,可能同步返回
if (_cache.TryGetValue(id, out var user))
{
return new ValueTask<User>(user);
}
// 缓存未命中,异步加载
return new ValueTask<User>(LoadUserAsync(id));
}
集合优化
1. 选择合适的集合类型
场景 | 推荐集合 | 理由 |
---|---|---|
顺序访问 | List<T> | 内存连续,访问快 |
频繁查找 | Dictionary<K,V> | O(1) 查找 |
去重 | HashSet<T> | O(1) 去重 |
有序 | SortedSet<T> | 自动排序 |
线程安全 | ConcurrentDictionary<K,V> | 并发友好 |
2. 预分配容量
// ❌ 差:默认容量,需要多次扩容
var list = new List<int>();
for (int i = 0; i < 10000; i++)
{
list.Add(i);
}
// ✅ 好:预分配容量
var list = new List<int>(10000);
for (int i = 0; i < 10000; i++)
{
list.Add(i);
}
3. 避免不必要的枚举
// ❌ 差:多次枚举
var users = GetUsers().Where(u => u.IsActive);
var count = users.Count(); // 第一次枚举
var first = users.First(); // 第二次枚举
// ✅ 好:缓存结果
var users = GetUsers().Where(u => u.IsActive).ToList();
var count = users.Count;
var first = users.First();
HTTP 客户端优化
1. 重用 HttpClient
// ❌ 差:每次创建新实例
public async Task<string> GetDataAsync()
{
using var client = new HttpClient();
return await client.GetStringAsync("https://api.example.com/data");
}
// ✅ 好:使用 IHttpClientFactory
public class DataService
{
private readonly IHttpClientFactory _clientFactory;
public DataService(IHttpClientFactory clientFactory)
{
_clientFactory = clientFactory;
}
public async Task<string> GetDataAsync()
{
var client = _clientFactory.CreateClient();
return await client.GetStringAsync("https://api.example.com/data");
}
}
// 注册
builder.Services.AddHttpClient();
2. 启用 HTTP/2
builder.Services.AddHttpClient("MyApi", client =>
{
client.BaseAddress = new Uri("https://api.example.com");
client.DefaultRequestVersion = HttpVersion.Version20;
});
缓存策略
1. 响应缓存
builder.Services.AddResponseCaching();
app.UseResponseCaching();
// Controller
[ResponseCache(Duration = 60)] // 缓存 60 秒
[HttpGet]
public ActionResult<List<Product>> GetProducts()
{
return _productService.GetAll();
}
2. 内存缓存
public class ProductService
{
private readonly IMemoryCache _cache;
public async Task<List<Product>> GetProductsAsync()
{
return await _cache.GetOrCreateAsync("products", async entry =>
{
entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
return await _repository.GetAllAsync();
});
}
}
3. 分布式缓存
public class ProductService
{
private readonly IDistributedCache _cache;
public async Task<Product> GetProductAsync(int id)
{
var cacheKey = $"product:{id}";
var cached = await _cache.GetStringAsync(cacheKey);
if (cached != null)
{
return JsonSerializer.Deserialize<Product>(cached);
}
var product = await _repository.GetByIdAsync(id);
await _cache.SetStringAsync(
cacheKey,
JsonSerializer.Serialize(product),
new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30)
});
return product;
}
}
JSON 序列化优化
使用 System.Text.Json
// ✅ 好:使用 System.Text.Json(更快)
var json = JsonSerializer.Serialize(data);
var obj = JsonSerializer.Deserialize<MyClass>(json);
// 使用源生成器(.NET 6+)
[JsonSerializable(typeof(User))]
public partial class MyJsonContext : JsonSerializerContext { }
var options = new JsonSerializerOptions
{
TypeInfoResolver = MyJsonContext.Default
};
var json = JsonSerializer.Serialize(user, options);
性能监控
1. 使用 BenchmarkDotNet
dotnet add package BenchmarkDotNet
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
[MemoryDiagnoser]
public class StringBenchmark
{
private const int N = 1000;
[Benchmark]
public string StringConcat()
{
var result = "";
for (int i = 0; i < N; i++)
{
result += i.ToString();
}
return result;
}
[Benchmark]
public string StringBuilder()
{
var sb = new StringBuilder();
for (int i = 0; i < N; i++)
{
sb.Append(i);
}
return sb.ToString();
}
}
// 运行基准测试
BenchmarkRunner.Run<StringBenchmark>();
2. 使用诊断工具
// 使用 DiagnosticSource
public class PerformanceMonitor
{
private readonly DiagnosticListener _diagnosticListener;
public PerformanceMonitor()
{
_diagnosticListener = new DiagnosticListener("MyApp.Performance");
}
public async Task<T> MeasureAsync<T>(string operationName, Func<Task<T>> operation)
{
var stopwatch = Stopwatch.StartNew();
try
{
return await operation();
}
finally
{
stopwatch.Stop();
_diagnosticListener.Write(operationName, new
{
Duration = stopwatch.ElapsedMilliseconds
});
}
}
}
最佳实践清单
性能优化清单
总结
关键要点
- 先测量再优化,找到真正的瓶颈
- 关注内存分配和 GC 压力
- 优化数据库查询和索引
- 使用缓存减少重复计算
- 异步编程提高并发性能
- 定期进行性能测试和监控