Skip to content

性能优化

掌握增量生成器的性能优化技巧和最佳实践

📋 文档信息

属性
难度高级
阅读时间35 分钟
前置知识管道操作、缓存机制
相关文档缓存机制

🎯 学习目标

完成本文档后,你将能够:

  • ✅ 识别性能瓶颈
  • ✅ 应用性能优化技巧
  • ✅ 使用性能分析工具
  • ✅ 避免常见的性能陷阱
  • ✅ 优化大型项目的生成器性能

📚 快速导航

章节说明
性能优化原则核心优化原则
使用 ForAttributeWithMetadataName最快的过滤方式
提前过滤在 predicate 中过滤
避免重复计算缓存昂贵操作
优化数据结构使用高效的数据结构
性能测量测量和分析性能

性能优化原则

核心原则

  1. 提前过滤: 尽早排除不需要的数据
  2. 缓存优先: 充分利用缓存机制
  3. 避免重复: 不要重复计算相同的结果
  4. 使用正确的 API: 选择性能最优的 API
  5. 测量优化: 先测量,再优化

性能优化流程


使用 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 个类
CreateSyntaxProvider500ms5000ms
ForAttributeWithMetadataName50ms500ms
性能提升10x10x

提前过滤

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 共享全局数据

🔑 关键要点

性能优化优先级

  1. 使用 ForAttributeWithMetadataName (10-100x 提升)
  2. 提前过滤 (减少 90% 的计算)
  3. 使用 record + ImmutableArray (提升缓存效率)
  4. 缓存昂贵操作 (避免重复计算)
  5. 添加 WithTrackingName (识别瓶颈)

性能优化公式

总性能 = 过滤效率 × 缓存效率 × 数据结构效率
  • 过滤效率: 使用 ForAttributeWithMetadataName + 提前过滤
  • 缓存效率: 使用 record + ImmutableArray
  • 数据结构效率: 避免重复计算 + 共享全局数据

🔗 相关资源


🚀 下一步


最后更新: 2026-02-05

基于 MIT 许可发布