罗斯林启动时间慢

Bob*_*ght 5 c# roslyn

我注意到Roslyn解析/编译的启动时间是一个相当重要的一次性成本.编辑:我正在使用Roslyn CTP MSI(程序集在GAC中).这是预期的吗?有没有解决方法?

运行下面的代码与1次迭代(约3秒)和300次迭代(约3秒)的时间几乎相同.

[Test]
public void Test()
{
    var iters = 300;
    foreach (var i in Enumerable.Range(0, iters))
    {
        // Parse the source file using Roslyn
        SyntaxTree syntaxTree = SyntaxTree.ParseText(@"public class Foo" + i + @" { public void Exec() { } }");

        // Add all the references we need for the compilation
        var references = new List<MetadataReference>();
        references.Add(new MetadataFileReference(typeof(int).Assembly.Location));

        var compilationOptions = new CompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary);

        // Note: using a fixed assembly name, which doesn't matter as long as we don't expect cross references of generated assemblies
        var compilation = Compilation.Create("SomeAssemblyName", compilationOptions, new[] {syntaxTree}, references);

        // Generate the assembly into a memory stream
        var memStream = new MemoryStream();

        // if we comment out from this line and down, the runtime drops to ~.5 seconds
        EmitResult emitResult = compilation.Emit(memStream);

        var asm = Assembly.Load(memStream.GetBuffer());
        var type = asm.GetTypes().Single(t => t.Name == "Foo" + i);
    }
}
Run Code Online (Sandbox Code Playgroud)

Pet*_*ter 3

我认为一个问题是使用内存流,相反,您应该尝试使用动态模块和 ModuleBuilder。总体而言,代码执行速度更快,但首次加载场景仍然较重。我自己对 Roslyn 还很陌生,所以我不确定为什么这更快,但这是更改后的代码。

        var iters = 300;
        foreach (var i in Enumerable.Range(0, iters))
        {
            // Parse the source file using Roslyn
            SyntaxTree syntaxTree = SyntaxTree.ParseText(@"public class Foo" + i + @" { public void Exec() { } }");

            // Add all the references we need for the compilation
            var references = new List<MetadataReference>();
            references.Add(new MetadataFileReference(typeof(int).Assembly.Location));

            var compilationOptions = new CompilationOptions(outputKind: OutputKind.DynamicallyLinkedLibrary);

            // Note: using a fixed assembly name, which doesn't matter as long as we don't expect cross references of generated assemblies
            var compilation = Compilation.Create("SomeAssemblyName", compilationOptions, new[] { syntaxTree }, references);

            var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(new System.Reflection.AssemblyName("CustomerA"),
            System.Reflection.Emit.AssemblyBuilderAccess.RunAndCollect);

            var moduleBuilder = assemblyBuilder.DefineDynamicModule("MyModule");

            System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
            watch.Start();

            // if we comment out from this line and down, the runtime drops to ~.5 seconds
            var emitResult = compilation.Emit(moduleBuilder);

            watch.Stop();

            System.Diagnostics.Debug.WriteLine(watch.ElapsedMilliseconds);

            if (emitResult.Diagnostics.LongCount() == 0)
            {
                var type = moduleBuilder.GetTypes().Single(t => t.Name == "Foo" + i);

                System.Diagnostics.Debug.Write(type != null);
            }
        }
Run Code Online (Sandbox Code Playgroud)

通过使用这种技术,编译仅花费了 96 毫秒,在后续迭代中大约需要 3 - 15 毫秒。所以我认为你在第一个负载场景增加一些开销方面可能是正确的。

抱歉,我无法解释为什么它更快!我自己正在研究 Roslyn,今晚晚些时候我会进行更多挖掘,看看是否能找到有关 ModuleBuilder 通过内存流提供的内容的更多证据。