性能优化
掌握增量生成器的性能优化技巧和最佳实践
📋 文档信息
| 属性 | 值 |
|---|---|
| 难度 | 高级 |
| 阅读时间 | 35 分钟 |
| 前置知识 | 管道操作、缓存机制 |
| 相关文档 | 缓存机制 |
🎯 学习目标
完成本文档后,你将能够:
- ✅ 识别性能瓶颈
- ✅ 应用性能优化技巧
- ✅ 使用性能分析工具
- ✅ 避免常见的性能陷阱
- ✅ 优化大型项目的生成器性能
📚 快速导航
| 章节 | 说明 |
|---|---|
| 性能优化原则 | 核心优化原则 |
| 使用 ForAttributeWithMetadataName | 最快的过滤方式 |
| 提前过滤 | 在 predicate 中过滤 |
| 避免重复计算 | 缓存昂贵操作 |
| 优化数据结构 | 使用高效的数据结构 |
| 性能测量 | 测量和分析性能 |
性能优化原则
核心原则
- 提前过滤: 尽早排除不需要的数据
- 缓存优先: 充分利用缓存机制
- 避免重复: 不要重复计算相同的结果
- 使用正确的 API: 选择性能最优的 API
- 测量优化: 先测量,再优化
性能优化流程
使用 ForAttributeWithMetadataName
ForAttributeWithMetadataName 是性能最优的过滤方式。
性能对比
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// ForAttributeWithMetadataName 性能对比
/// </summary>
public class PerformanceComparison
{
// ❌ 慢速方式:使用 CreateSyntaxProvider + 手动检查特性
public void SlowApproach(IncrementalGeneratorInitializationContext context)
{
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) =>
{
var classDecl = (ClassDeclarationSyntax)ctx.Node;
var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl);
// 手动检查特性(慢)
if (!symbol.GetAttributes().Any(a =>
a.AttributeClass?.ToDisplayString() == "MyNamespace.MyAttribute"))
return null;
return symbol;
})
.Where(s => s != null);
// 性能:慢 ❌
// 原因:需要为每个类创建语义模型并检查特性
}
// ✅ 快速方式:使用 ForAttributeWithMetadataName
public void FastApproach(IncrementalGeneratorInitializationContext context)
{
var classes = context.SyntaxProvider
.ForAttributeWithMetadataName(
"MyNamespace.MyAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => (INamedTypeSymbol)ctx.TargetSymbol);
// 性能:快 ✅
// 原因:编译器级别的优化,直接访问元数据
}
}性能提升
使用 ForAttributeWithMetadataName 可以获得 10-100 倍的性能提升:
| 方法 | 1000 个类 | 10000 个类 |
|---|---|---|
| CreateSyntaxProvider | 500ms | 5000ms |
| ForAttributeWithMetadataName | 50ms | 500ms |
| 性能提升 | 10x | 10x |
提前过滤
在 predicate 中尽早过滤,避免不必要的转换。
提前过滤示例
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 提前过滤示例
/// </summary>
public class EarlyFilteringDemo
{
// ❌ 不好的做法:在 transform 中过滤
public void BadApproach(IncrementalGeneratorInitializationContext context)
{
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax, // 接受所有类
transform: (ctx, _) =>
{
var classDecl = (ClassDeclarationSyntax)ctx.Node;
// 在 transform 中过滤(晚)
if (!classDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)))
return null;
var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl);
return symbol;
})
.Where(s => s != null);
// 问题:为所有类创建语义模型,然后才过滤
}
// ✅ 好的做法:在 predicate 中过滤
public void GoodApproach(IncrementalGeneratorInitializationContext context)
{
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) =>
{
// 在 predicate 中过滤(早)
if (node is not ClassDeclarationSyntax classDecl)
return false;
// 只接受公共类
return classDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword));
},
transform: (ctx, _) =>
{
var classDecl = (ClassDeclarationSyntax)ctx.Node;
var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl);
return symbol;
});
// 优势:只为公共类创建语义模型
}
}提前过滤的好处
性能提升: 提前过滤可以减少 90% 的 transform 调用。
避免重复计算
缓存昂贵操作
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 避免重复计算示例
/// </summary>
public class AvoidRecomputationDemo
{
// ❌ 不好的做法:在 transform 中执行昂贵操作
public void BadApproach(IncrementalGeneratorInitializationContext context)
{
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) =>
{
// 昂贵操作:每次都重新计算
var expensiveResult = PerformExpensiveOperation();
var classDecl = (ClassDeclarationSyntax)ctx.Node;
return (classDecl, expensiveResult);
});
// 问题:每个类都会执行昂贵操作
}
// ✅ 好的做法:缓存昂贵操作的结果
public void GoodApproach(IncrementalGeneratorInitializationContext context)
{
// 只计算一次
var expensiveResult = context.CompilationProvider
.Select((comp, _) => PerformExpensiveOperation())
.WithTrackingName("ExpensiveOperation");
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => (ClassDeclarationSyntax)ctx.Node);
// 组合缓存的结果
var combined = classes.Combine(expensiveResult);
// 优势:昂贵操作只执行一次,所有类共享结果
}
private string PerformExpensiveOperation()
{
// 模拟昂贵操作
System.Threading.Thread.Sleep(100);
return "result";
}
}缓存策略
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 缓存策略示例
/// </summary>
public class CachingStrategyDemo
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 策略 1: 全局缓存(很少改变的数据)
var globalConfig = context.CompilationProvider
.Select((comp, _) => new GlobalConfig(
comp.AssemblyName,
comp.Options.OutputKind))
.WithTrackingName("GlobalConfig");
// 策略 2: 按类型缓存(偶尔改变的数据)
var typeCache = context.SyntaxProvider
.ForAttributeWithMetadataName(
"MyAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => ExtractTypeInfo(ctx))
.WithTrackingName("TypeCache");
// 策略 3: 组合缓存
var combined = typeCache.Combine(globalConfig);
context.RegisterSourceOutput(combined, (spc, data) =>
{
var (typeInfo, config) = data;
var code = GenerateCode(typeInfo, config);
spc.AddSource($"{typeInfo.Name}.g.cs", code);
});
}
private TypeInfo ExtractTypeInfo(GeneratorAttributeSyntaxContext context)
{
var symbol = (INamedTypeSymbol)context.TargetSymbol;
return new TypeInfo(symbol.Name);
}
private string GenerateCode(TypeInfo typeInfo, GlobalConfig config)
{
return $"// Type: {typeInfo.Name}, Assembly: {config.AssemblyName}";
}
private record GlobalConfig(string AssemblyName, OutputKind OutputKind);
private record TypeInfo(string Name);
}优化数据结构
使用 record 类型
csharp
using System.Collections.Immutable;
/// <summary>
/// 使用 record 类型优化性能
/// </summary>
public class RecordOptimization
{
// ❌ 慢速:使用 class + 手动实现 IEquatable
public class ClassInfoSlow : IEquatable<ClassInfoSlow>
{
public string Name { get; set; }
public ImmutableArray<string> Properties { get; set; }
public bool Equals(ClassInfoSlow other)
{
if (other is null) return false;
return Name == other.Name &&
Properties.SequenceEqual(other.Properties);
}
public override bool Equals(object obj) => Equals(obj as ClassInfoSlow);
public override int GetHashCode()
{
var hash = new HashCode();
hash.Add(Name);
foreach (var prop in Properties)
hash.Add(prop);
return hash.ToHashCode();
}
}
// ✅ 快速:使用 record
public record ClassInfoFast(
string Name,
ImmutableArray<string> Properties);
// 性能对比:
// - record 的 Equals 和 GetHashCode 是编译器优化的
// - record 使用值类型语义,减少装箱
// - record 的代码更简洁,编译器可以更好地优化
}使用 ImmutableArray
csharp
using System.Collections.Generic;
using System.Collections.Immutable;
/// <summary>
/// 使用 ImmutableArray 优化性能
/// </summary>
public class ImmutableArrayOptimization
{
// ❌ 慢速:使用 List
public record ClassInfoSlow(
string Name,
List<string> Properties); // List 使用引用相等性
// ✅ 快速:使用 ImmutableArray
public record ClassInfoFast(
string Name,
ImmutableArray<string> Properties); // ImmutableArray 使用值相等性
public void DemonstratePerformance()
{
// List 性能问题
var list1 = new List<string> { "A", "B", "C" };
var list2 = new List<string> { "A", "B", "C" };
var slow1 = new ClassInfoSlow("User", list1);
var slow2 = new ClassInfoSlow("User", list2);
// 问题:引用不同,即使内容相同
Console.WriteLine(slow1 == slow2); // False
// 结果:缓存失效,重新计算
// ImmutableArray 性能优势
var array1 = ImmutableArray.Create("A", "B", "C");
var array2 = ImmutableArray.Create("A", "B", "C");
var fast1 = new ClassInfoFast("User", array1);
var fast2 = new ClassInfoFast("User", array2);
// 优势:值相等性,内容相同即相等
Console.WriteLine(fast1 == fast2); // True
// 结果:缓存生效,不重新计算
}
}数据结构性能对比
| 数据结构 | 相等性比较 | GetHashCode | 缓存效果 |
|---|---|---|---|
| class + List | 引用 | 慢 | 差 ❌ |
| class + ImmutableArray | 引用 | 中 | 中 |
| record + List | 引用 | 慢 | 差 ❌ |
| record + ImmutableArray | 值 | 快 | 好 ✅ |
性能测量
使用 WithTrackingName
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 使用 WithTrackingName 进行性能分析
/// </summary>
public class PerformanceTrackingDemo
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => (ClassDeclarationSyntax)ctx.Node)
.WithTrackingName("FindClasses"); // 添加跟踪名称
var publicClasses = classes
.Where((classDecl, _) =>
classDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)))
.WithTrackingName("FilterPublicClasses"); // 添加跟踪名称
var classNames = publicClasses
.Select((classDecl, _) => classDecl.Identifier.Text)
.WithTrackingName("ExtractClassNames"); // 添加跟踪名称
context.RegisterSourceOutput(classNames, (spc, name) =>
{
var code = $"// Class: {name}";
spc.AddSource($"{name}.g.cs", code);
});
}
}性能测量工具
csharp
using System.Diagnostics;
using Microsoft.CodeAnalysis;
/// <summary>
/// 性能测量工具
/// </summary>
public class PerformanceMeasurementDemo
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) =>
{
var sw = Stopwatch.StartNew();
var classDecl = (ClassDeclarationSyntax)ctx.Node;
var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl);
var result = new ClassInfo(symbol.Name);
sw.Stop();
// 记录慢速转换
if (sw.ElapsedMilliseconds > 10)
{
Debug.WriteLine(
$"Slow transform for {symbol.Name}: {sw.ElapsedMilliseconds}ms");
}
return result;
})
.WithTrackingName("TransformClasses");
context.RegisterSourceOutput(classes, (spc, info) =>
{
var sw = Stopwatch.StartNew();
var code = GenerateCode(info);
spc.AddSource($"{info.Name}.g.cs", code);
sw.Stop();
// 记录慢速生成
if (sw.ElapsedMilliseconds > 5)
{
Debug.WriteLine(
$"Slow generation for {info.Name}: {sw.ElapsedMilliseconds}ms");
}
});
}
private string GenerateCode(ClassInfo info)
{
return $"// Class: {info.Name}";
}
private record ClassInfo(string Name);
}性能优化检查清单
优化前检查
检查清单
- [ ] 使用
ForAttributeWithMetadataName而不是CreateSyntaxProvider - [ ] 在
predicate中提前过滤 - [ ] 使用
record类型 - [ ] 使用
ImmutableArray而不是List - [ ] 缓存昂贵操作到
CompilationProvider - [ ] 避免在
transform中执行昂贵操作 - [ ] 添加
WithTrackingName进行性能分析 - [ ] 避免不必要的
Collect操作 - [ ] 使用
Combine共享全局数据
🔑 关键要点
性能优化优先级
- 使用 ForAttributeWithMetadataName (10-100x 提升)
- 提前过滤 (减少 90% 的计算)
- 使用 record + ImmutableArray (提升缓存效率)
- 缓存昂贵操作 (避免重复计算)
- 添加 WithTrackingName (识别瓶颈)
性能优化公式
总性能 = 过滤效率 × 缓存效率 × 数据结构效率- 过滤效率: 使用 ForAttributeWithMetadataName + 提前过滤
- 缓存效率: 使用 record + ImmutableArray
- 数据结构效率: 避免重复计算 + 共享全局数据
🔗 相关资源
🚀 下一步
最后更新: 2026-02-05