性能优化技巧
本文档介绍源生成器开发中的性能优化技巧,包括缓存策略、避免重复计算、增量生成器优化等。
📋 文档信息
- 难度级别: 高级
- 预计阅读时间: 20 分钟
- 前置知识:
- 增量生成器基础
- C# 性能优化基础
🎯 学习目标
完成本文档后,您将能够:
- ✅ 实现高效的缓存策略
- ✅ 避免重复计算
- ✅ 优化增量生成器性能
- ✅ 减少内存分配
- ✅ 使用对象池优化性能
缓存策略
基本缓存示例
csharp
/// <summary>
/// 缓存策略示例
/// </summary>
public class CachingStrategy
{
// ✅ 使用 record 类型支持结构化相等性
public record ClassData(
string Name,
ImmutableArray<string> Properties);
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// 数据会被自动缓存
var classData = context.SyntaxProvider
.ForAttributeWithMetadataName(
"MyAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) =>
{
var classSymbol = (INamedTypeSymbol)ctx.TargetSymbol;
return new ClassData(
classSymbol.Name,
classSymbol.GetMembers()
.OfType<IPropertySymbol>()
.Select(p => p.Name)
.ToImmutableArray());
});
}
}避免重复计算
正确的计算方式
csharp
/// <summary>
/// 避免重复计算示例
/// </summary>
public class AvoidRecomputation
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// ❌ 不好:每次都重新计算
var bad = context.SyntaxProvider
.CreateSyntaxProvider(
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) =>
{
// 昂贵的操作会在每次输入改变时执行
return ExpensiveOperation();
});
// ✅ 好:只在编译改变时计算一次
var good = context.CompilationProvider
.Select((comp, _) => ExpensiveOperation());
}
private string ExpensiveOperation() => "";
}增量生成器优化
最大化利用缓存机制
csharp
using Microsoft.CodeAnalysis;
/// <summary>
/// 增量生成器优化模式
/// 展示如何最大化利用增量生成器的缓存机制
/// </summary>
[Generator]
public class OptimizedIncrementalGenerator : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
// ✅ 模式 1: 使用 ForAttributeWithMetadataName 而不是手动过滤
// 这个 API 经过高度优化,会自动缓存结果
var classesWithAttribute = context.SyntaxProvider
.ForAttributeWithMetadataName(
"MyNamespace.GenerateAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: TransformClass);
// ✅ 模式 2: 使用 Select 转换数据为不可变类型
// 确保数据可以被正确缓存
var transformedData = classesWithAttribute
.Select((data, _) => new GenerationData(
data.ClassName,
data.Properties.ToImmutableArray()));
// ✅ 模式 3: 使用 Collect 收集所有数据
// 只在真正需要所有数据时使用
var allData = transformedData.Collect();
// ✅ 模式 4: 组合多个数据源
var combined = transformedData
.Combine(context.CompilationProvider);
// ✅ 模式 5: 使用 Where 过滤数据
// 过滤操作会被缓存
var filteredData = transformedData
.Where((data, _) => data.Properties.Length > 0);
// 注册输出
context.RegisterSourceOutput(filteredData, GenerateCode);
}
private ClassData TransformClass(GeneratorAttributeSyntaxContext context, CancellationToken ct)
{
var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
// ✅ 只提取需要的数据
// 不要保存整个符号或语法节点
return new ClassData(
classSymbol.Name,
classSymbol.ContainingNamespace.ToDisplayString(),
classSymbol.GetMembers()
.OfType<IPropertySymbol>()
.Where(p => p.DeclaredAccessibility == Accessibility.Public)
.Select(p => new PropertyData(p.Name, p.Type.ToDisplayString()))
.ToList());
}
private void GenerateCode(SourceProductionContext context, GenerationData data)
{
// 生成代码
var code = $@"
// 自动生成的代码
namespace {data.Namespace}
{{
partial class {data.ClassName}
{{
// 生成的成员
}}
}}";
context.AddSource($"{data.ClassName}.g.cs", code);
}
}
/// <summary>
/// 类数据 - 使用 record 确保结构化相等性
/// </summary>
public record ClassData(
string ClassName,
string Namespace,
List<PropertyData> Properties);
/// <summary>
/// 属性数据
/// </summary>
public record PropertyData(string Name, string Type);
/// <summary>
/// 生成数据
/// </summary>
public record GenerationData(
string ClassName,
ImmutableArray<PropertyData> Properties)
{
public string Namespace { get; init; } = string.Empty;
}避免不必要的分配
使用 StringBuilder 和对象池
csharp
using System.Text;
using Microsoft.CodeAnalysis;
/// <summary>
/// 内存优化的代码生成器
/// 展示如何减少内存分配
/// </summary>
public class MemoryOptimizedGenerator
{
// ✅ 重用 StringBuilder
private readonly StringBuilder _builder = new StringBuilder(1024);
// ✅ 使用对象池
private static readonly ObjectPool<StringBuilder> _stringBuilderPool =
new ObjectPool<StringBuilder>(() => new StringBuilder(1024));
/// <summary>
/// 生成代码(优化版本)
/// </summary>
public string GenerateCode(ClassData classData)
{
// ✅ 从池中获取 StringBuilder
var builder = _stringBuilderPool.Get();
try
{
builder.Clear();
// ✅ 使用 AppendLine 而不是字符串连接
builder.AppendLine("// 自动生成的代码");
builder.AppendLine($"namespace {classData.Namespace}");
builder.AppendLine("{");
builder.AppendLine($" partial class {classData.ClassName}");
builder.AppendLine(" {");
// ✅ 使用 Span<T> 处理字符串(如果适用)
foreach (var property in classData.Properties)
{
builder.AppendLine($" public {property.Type} {property.Name} {{ get; set; }}");
}
builder.AppendLine(" }");
builder.AppendLine("}");
return builder.ToString();
}
finally
{
// ✅ 归还到池中
_stringBuilderPool.Return(builder);
}
}
/// <summary>
/// 使用 Span 优化字符串处理
/// </summary>
public string ProcessString(string input)
{
// ✅ 使用 Span 避免分配
ReadOnlySpan<char> span = input.AsSpan();
// 处理字符串
var result = new StringBuilder(input.Length);
for (int i = 0; i < span.Length; i++)
{
if (char.IsUpper(span[i]))
{
result.Append(char.ToLower(span[i]));
}
else
{
result.Append(span[i]);
}
}
return result.ToString();
}
}
/// <summary>
/// 简单的对象池实现
/// </summary>
public class ObjectPool<T> where T : class
{
private readonly ConcurrentBag<T> _objects = new();
private readonly Func<T> _objectGenerator;
public ObjectPool(Func<T> objectGenerator)
{
_objectGenerator = objectGenerator;
}
public T Get()
{
return _objects.TryTake(out var item) ? item : _objectGenerator();
}
public void Return(T item)
{
_objects.Add(item);
}
}性能优化高级技巧
技巧 1: 使用对象池减少内存分配
csharp
public class StringBuilderPool
{
private static readonly ObjectPool<StringBuilder> _pool =
new DefaultObjectPool<StringBuilder>(
new StringBuilderPooledObjectPolicy());
public static StringBuilder Get() => _pool.Get();
public static void Return(StringBuilder sb)
{
sb.Clear();
_pool.Return(sb);
}
}
public class StringBuilderPooledObjectPolicy : IPooledObjectPolicy<StringBuilder>
{
public StringBuilder Create() => new StringBuilder(1024);
public bool Return(StringBuilder obj)
{
if (obj.Capacity > 4096)
{
// 不要保留太大的 StringBuilder
return false;
}
obj.Clear();
return true;
}
}
// 使用
public string GenerateCode(ClassInfo classInfo)
{
var sb = StringBuilderPool.Get();
try
{
sb.AppendLine($"namespace {classInfo.Namespace}");
// ... 生成代码
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
}技巧 2: 延迟计算和惰性求值
csharp
public record ClassInfo(
string Name,
string Namespace,
Lazy<ImmutableArray<PropertyInfo>> Properties,
Lazy<ImmutableArray<MethodInfo>> Methods)
{
// 只在需要时计算属性
public ImmutableArray<PropertyInfo> GetProperties() => Properties.Value;
// 只在需要时计算方法
public ImmutableArray<MethodInfo> GetMethods() => Methods.Value;
}
// 创建时使用延迟计算
private ClassInfo GetClassInfo(GeneratorAttributeSyntaxContext context)
{
var symbol = (INamedTypeSymbol)context.TargetSymbol;
return new ClassInfo(
symbol.Name,
symbol.ContainingNamespace.ToDisplayString(),
new Lazy<ImmutableArray<PropertyInfo>>(() =>
CollectProperties(symbol)),
new Lazy<ImmutableArray<MethodInfo>>(() =>
CollectMethods(symbol)));
}技巧 3: 批量处理和并行化
csharp
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var classInfos = context.SyntaxProvider
.ForAttributeWithMetadataName(
"GenerateAttribute",
predicate: (node, _) => node is ClassDeclarationSyntax,
transform: (ctx, _) => GetClassInfo(ctx))
.Collect();
context.RegisterSourceOutput(classInfos, (spc, classes) =>
{
// 并行生成代码(注意:只有在生成代码是 CPU 密集型时才使用)
var results = classes
.AsParallel()
.WithDegreeOfParallelism(Environment.ProcessorCount)
.Select(classInfo => new
{
FileName = $"{classInfo.Name}.g.cs",
Code = GenerateCode(classInfo)
})
.ToList();
// 串行添加源文件(AddSource 不是线程安全的)
foreach (var result in results)
{
spc.AddSource(result.FileName, result.Code);
}
});
}🔑 关键要点
- 使用 ForAttributeWithMetadataName - 这是最优化的 API
- 使用 record 类型 - 确保结构化相等性和缓存
- 使用 ImmutableArray - 避免可变集合
- 使用对象池 - 减少内存分配
- 延迟计算 - 只在需要时计算
- 批量处理 - 收集所有数据后一次性处理
📚 相关资源
📝 下一步
📝 文档质量保证
本文档遵循以下质量标准:
- ✅ 完整的目录结构
- ✅ 所有代码示例包含详细中文注释
- ✅ 包含最佳实践和反模式对比
- ✅ 包含真实使用场景
- ✅ 包含跨文档引用
- ✅ 内容完整,未因任何限制而精简
最后更新: 2025-01-21