性能分析
学习如何分析和优化源生成器的性能
📋 文档信息
难度级别: 高级
预计阅读时间: 25 分钟
前置知识:
- 源生成器基础
- 性能分析基础
- C# 性能优化
🎯 学习目标
学完本章节后,你将能够:
- 测量生成器的执行时间
- 识别性能瓶颈
- 应用性能优化策略
- 使用 BenchmarkDotNet 进行基准测试
- 实施性能监控
⚡ 性能分析和优化
测量执行时间
csharp
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classes = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) =>
{
var sw = System.Diagnostics.Stopwatch.StartNew();
var result = ProcessClass(ctx);
sw.Stop();
if (sw.ElapsedMilliseconds > 100)
{
System.Diagnostics.Debug.WriteLine(
$"Slow processing: {sw.ElapsedMilliseconds}ms");
}
return result;
});
}性能分析辅助类
csharp
/// <summary>
/// 性能分析辅助类
/// </summary>
public static class PerformanceAnalyzer
{
private static readonly Dictionary<string, List<long>> Timings = new();
private static readonly object Lock = new();
/// <summary>
/// 测量代码块执行时间
/// </summary>
public static IDisposable Measure(string operationName)
{
return new PerformanceMeasurement(operationName);
}
/// <summary>
/// 获取性能报告
/// </summary>
public static string GetReport()
{
lock (Lock)
{
var sb = new StringBuilder();
sb.AppendLine("=== Performance Report ===");
foreach (var (operation, times) in Timings.OrderByDescending(x => x.Value.Average()))
{
var avg = times.Average();
var min = times.Min();
var max = times.Max();
var count = times.Count;
sb.AppendLine($"{operation}:");
sb.AppendLine($" Count: {count}");
sb.AppendLine($" Avg: {avg:F2}ms");
sb.AppendLine($" Min: {min}ms");
sb.AppendLine($" Max: {max}ms");
sb.AppendLine($" Total: {times.Sum()}ms");
}
return sb.ToString();
}
}
private class PerformanceMeasurement : IDisposable
{
private readonly string _operationName;
private readonly Stopwatch _stopwatch;
public PerformanceMeasurement(string operationName)
{
_operationName = operationName;
_stopwatch = Stopwatch.StartNew();
}
public void Dispose()
{
_stopwatch.Stop();
lock (Lock)
{
if (!Timings.ContainsKey(_operationName))
{
Timings[_operationName] = new List<long>();
}
Timings[_operationName].Add(_stopwatch.ElapsedMilliseconds);
}
}
}
}使用性能分析器
csharp
[Generator]
public class PerformanceAwareGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classes = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateAttribute",
predicate: (node, _) =>
{
using (PerformanceAnalyzer.Measure("Predicate"))
{
return node is ClassDeclarationSyntax;
}
},
transform: (ctx, _) =>
{
using (PerformanceAnalyzer.Measure("Transform"))
{
return GetClassInfo(ctx);
}
});
context.RegisterSourceOutput(classes, (spc, classInfo) =>
{
using (PerformanceAnalyzer.Measure("CodeGeneration"))
{
var code = GenerateCode(classInfo);
spc.AddSource($"{classInfo.Name}.g.cs", code);
}
});
// 输出性能报告
#if DEBUG
var report = PerformanceAnalyzer.GetReport();
File.WriteAllText(
Path.Combine(Path.GetTempPath(), "generator-performance.txt"),
report);
#endif
}
private ClassInfo GetClassInfo(GeneratorAttributeSyntaxContext context)
{
using (PerformanceAnalyzer.Measure("GetClassInfo"))
{
var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
using (PerformanceAnalyzer.Measure("GetProperties"))
{
var properties = classSymbol.GetMembers()
.OfType<IPropertySymbol>()
.ToList();
}
return new ClassInfo(classSymbol.Name);
}
}
private string GenerateCode(ClassInfo classInfo)
{
using (PerformanceAnalyzer.Measure("GenerateCode"))
{
return $"public partial class {classInfo.Name} {{ }}";
}
}
private record ClassInfo(string Name);
}性能报告示例:
=== Performance Report ===
Transform:
Count: 50
Avg: 12.34ms
Min: 5ms
Max: 45ms
Total: 617ms
CodeGeneration:
Count: 50
Avg: 3.21ms
Min: 1ms
Max: 15ms
Total: 160.5ms
Predicate:
Count: 200
Avg: 0.15ms
Min: 0ms
Max: 2ms
Total: 30ms📊 性能优化检查清单
✅ 最佳实践
- 使用增量生成器
csharp
// ✅ 推荐:使用 IIncrementalGenerator
[Generator]
public class MyGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 增量管道...
}
}- 早期过滤
csharp
// ✅ 推荐:在 predicate 中快速过滤
var classes = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateAttribute",
predicate: (node, _) =>
node is ClassDeclarationSyntax cls &&
cls.Modifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)),
transform: (ctx, _) => GetClassInfo(ctx));- 避免重复计算
csharp
// ✅ 推荐:缓存计算结果
private readonly Dictionary<string, ClassInfo> _cache = new();
private ClassInfo GetClassInfo(GeneratorAttributeSyntaxContext context)
{
var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
var key = classSymbol.ToDisplayString();
if (_cache.TryGetValue(key, out var cached))
{
return cached;
}
var info = new ClassInfo(classSymbol.Name);
_cache[key] = info;
return info;
}- 使用 StringBuilder
csharp
// ✅ 推荐:使用 StringBuilder 构建代码
private string GenerateCode(ClassInfo classInfo)
{
var sb = new StringBuilder();
sb.AppendLine($"public partial class {classInfo.Name}");
sb.AppendLine("{");
// ...
sb.AppendLine("}");
return sb.ToString();
}❌ 反模式
- 在 transform 中进行复杂过滤
csharp
// ❌ 避免:在 transform 中过滤
var classes = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateAttribute",
predicate: (node, _) => true, // 不过滤
transform: (ctx, _) =>
{
var symbol = ctx.TargetSymbol as INamedTypeSymbol;
if (symbol == null) return null; // 在这里过滤,性能差
if (!symbol.IsPartialDefinition()) return null;
return GetClassInfo(ctx);
});- 使用字符串拼接
csharp
// ❌ 避免:使用字符串拼接
private string GenerateCode(ClassInfo classInfo)
{
string code = "public partial class " + classInfo.Name + "\n{\n";
// 性能差...
return code;
}- 过度使用语义分析
csharp
// ❌ 避免:不必要的语义分析
private bool IsValidClass(SyntaxNode node, SemanticModel model)
{
// 每次都进行语义分析,性能差
var symbol = model.GetDeclaredSymbol(node);
return symbol != null;
}🔬 使用 BenchmarkDotNet
设置基准测试
csharp
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
[MemoryDiagnoser]
[SimpleJob(warmupCount: 3, targetCount: 5)]
public class GeneratorBenchmarks
{
private Compilation _compilation;
private MyGenerator _generator;
[GlobalSetup]
public void Setup()
{
var source = @"
[Generate]
public class TestClass
{
public string Name { get; set; }
public int Age { get; set; }
}
";
_compilation = CreateCompilation(source);
_generator = new MyGenerator();
}
[Benchmark]
public void RunGenerator()
{
var driver = CSharpGeneratorDriver.Create(_generator);
driver = driver.RunGenerators(_compilation);
}
[Benchmark]
public void RunGeneratorWithLargeInput()
{
var largeSource = GenerateLargeSource(1000);
var compilation = CreateCompilation(largeSource);
var driver = CSharpGeneratorDriver.Create(_generator);
driver = driver.RunGenerators(compilation);
}
private string GenerateLargeSource(int classCount)
{
var sb = new StringBuilder();
for (int i = 0; i < classCount; i++)
{
sb.AppendLine($@"
[Generate]
public class TestClass{i}
{{
public string Property{i} {{ get; set; }}
}}
");
}
return sb.ToString();
}
}
// 运行基准测试
class Program
{
static void Main(string[] args)
{
var summary = BenchmarkRunner.Run<GeneratorBenchmarks>();
}
}基准测试结果示例
| Method | Mean | Error | StdDev | Gen 0 | Allocated |
|-------------------------- |---------:|---------:|---------:|-------:|----------:|
| RunGenerator | 12.5 ms | 0.24 ms | 0.22 ms | 125.00 | 512 KB |
| RunGeneratorWithLargeInput | 125.3 ms | 2.45 ms | 2.29 ms | 1250.0 | 5120 KB |📈 性能监控
实时性能监控
csharp
public class PerformanceMonitor
{
private readonly Dictionary<string, Stopwatch> _timers = new();
private readonly Dictionary<string, long> _counters = new();
public void StartTimer(string name)
{
if (!_timers.ContainsKey(name))
{
_timers[name] = new Stopwatch();
}
_timers[name].Restart();
}
public void StopTimer(string name)
{
if (_timers.TryGetValue(name, out var timer))
{
timer.Stop();
}
}
public void IncrementCounter(string name)
{
if (!_counters.ContainsKey(name))
{
_counters[name] = 0;
}
_counters[name]++;
}
public string GetReport()
{
var sb = new StringBuilder();
sb.AppendLine("Performance Report");
sb.AppendLine("==================");
sb.AppendLine("\nTimers:");
foreach (var (name, timer) in _timers)
{
sb.AppendLine($" {name}: {timer.ElapsedMilliseconds}ms");
}
sb.AppendLine("\nCounters:");
foreach (var (name, count) in _counters)
{
sb.AppendLine($" {name}: {count}");
}
return sb.ToString();
}
}
// 使用
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var monitor = new PerformanceMonitor();
var classInfos = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) =>
{
monitor.StartTimer("Transform");
monitor.IncrementCounter("ClassesProcessed");
var result = GetClassInfo(ctx);
monitor.StopTimer("Transform");
return result;
});
context.RegisterSourceOutput(classInfos, (spc, classInfo) =>
{
monitor.StartTimer("CodeGeneration");
var code = GenerateCode(classInfo);
spc.AddSource($"{classInfo.Name}.g.cs", code);
monitor.StopTimer("CodeGeneration");
// 报告性能数据
spc.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(
"PERF001",
"Performance Report",
monitor.GetReport(),
"Performance",
DiagnosticSeverity.Info,
true),
Location.None));
});
}🔗 相关资源
💡 关键要点
- 使用 Stopwatch 测量执行时间
- 识别瓶颈 找出耗时最多的操作
- 使用增量生成器 提高性能 10-100 倍
- 早期过滤 在 predicate 中进行语法层面的过滤
- 避免重复计算 使用缓存
- 使用 StringBuilder 构建代码
- 使用 BenchmarkDotNet 进行基准测试
- 实施性能监控 持续跟踪性能指标
最后更新: 2025-01-21