加速 CSharpCompilation.Emit?

Imp*_*rks 6 c# compilation roslyn

我正在将一个应用程序从 .NET Framework 移植到 .NET Core,它使用运行时代码生成。

这是旧代码:

var refs = new []
{
    typeof(Foo).Assembly.Location,
    typeof(Enumerable).Assembly.Location
};
var options = new CompilerParameters(refs)
{
    GenerateExecutable = false,
    GenerateInMemory = true
};
var provider = new CSharpCodeProvider();
var result = provider.CompileAssemblyFromSource(options, rawText);
return result.CompiledAssembly;
Run Code Online (Sandbox Code Playgroud)

使用 Roslyn 的新代码如下所示:

var root = Path.GetDirectoryName(typeof(object).Assembly.Location);
var typeRefs = new[]
{
    typeof(object).Assembly.Location,
    Path.Combine(root, "System.Collections.dll"),
    Path.Combine(root, "System.Runtime.dll"),
    typeof(Foo).Assembly.Location,
    typeof(Enumerable).Assembly.Location
};

var source = Measure("SourceText", () => SourceText.From(rawText));
var parseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp7);
var syntaxTree = Measure("Parse", () => SyntaxFactory.ParseSyntaxTree(source, parseOptions));

var compilation = Measure("Compile", () => CSharpCompilation.Create(
    "Bar.dll",
    new[] {syntaxTree},
    typeRefs.Select(x => MetadataReference.CreateFromFile(x)),
    new CSharpCompilationOptions(
        OutputKind.DynamicallyLinkedLibrary,
        optimizationLevel: OptimizationLevel.Debug
    )
));

using var stream = new MemoryStream();

var emitResult = Measure("Emit", () => compilation.Emit(stream));
if(!emitResult.Success)
    throw new Exception("Failed to compile parser code!");

stream.Seek(0, SeekOrigin.Begin);
return Assembly.Load(stream.ToArray());
Run Code Online (Sandbox Code Playgroud)

问题是:新代码的运行速度至少慢了 2 倍。我使用了一个非常简单的Measure函数来追踪瓶颈:

private static T Measure<T>(string caption, Func<T> generator)
{
    var now = DateTime.Now;
    var result = generator();
    var end = DateTime.Now - now;
    Debug.WriteLine($"{caption}: {end.TotalMilliseconds} ms");
    return result;
}
Run Code Online (Sandbox Code Playgroud)

这是它打印的内容:

SourceText: 4,7926 ms
Parse: 598,5749 ms
Compile: 43,4261 ms
Emit: 5256,0411 ms
Run Code Online (Sandbox Code Playgroud)

如您所见,生成程序集compilation.Emit需要花费 5.2 秒之多。

有什么办法可以加快这个速度吗?也许选项中的一些标志会影响编译时间?我尝试了以下操作,但没有成功:

  • OptimizationLevel.Debug在和之间更改OptimizationLevel.Release
  • 将单个源拆分为多个源部分,每个类一个(这实际上使编译速度变慢)

有任何想法吗?