Skip to main content

.NET 性能优化指南

性能优化原则

优化准则
  1. 先测量,再优化 - 使用性能分析工具找到瓶颈
  2. 关注热点 - 80/20 原则,优化影响最大的 20%
  3. 权衡取舍 - 性能 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;
}

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
});
}
}
}

最佳实践清单

性能优化清单

内存

  • 避免不必要的对象分配
  • 使用 StringBuilder 拼接字符串
  • 使用 Span<T> 处理内存切片
  • 考虑使用对象池
  • 合理使用值类型

数据库

  • 使用 AsNoTracking 进行只读查询
  • 避免 N+1 查询问题
  • 只查询需要的字段
  • 使用批量操作
  • 启用查询编译

异步

  • 一路异步到底
  • 并行执行独立任务
  • 避免同步阻塞
  • 使用 ValueTask 优化热路径

缓存

  • 缓存热点数据
  • 使用合适的缓存策略
  • 设置合理的过期时间
  • 监控缓存命中率

HTTP

  • 重用 HttpClient
  • 启用响应压缩
  • 使用 HTTP/2
  • 实现请求限流

总结

关键要点
  • 先测量再优化,找到真正的瓶颈
  • 关注内存分配和 GC 压力
  • 优化数据库查询和索引
  • 使用缓存减少重复计算
  • 异步编程提高并发性能
  • 定期进行性能测试和监控

相关资源