首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Roslyn启动时间较慢

Roslyn启动时间较慢
EN

Stack Overflow用户
提问于 2013-03-12 23:31:14
回答 4查看 2.6K关注 0票数 7

我注意到Roslyn解析/编译的启动时间是相当可观的一次性成本。编辑:我正在使用Roslyn CTP MSI (程序集在GAC中)。这是意料之中的吗?有什么变通方法吗?

运行下面的代码1次迭代(~3秒)与300次迭代(~3秒)所需的时间几乎相同。

代码语言:javascript
复制
[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);
    }
}
EN

回答 4

Stack Overflow用户

发布于 2013-07-24 07:12:39

我认为其中一个问题是使用内存流,相反,您应该尝试使用动态模块和ModuleBuilder。总体而言,代码的执行速度更快,但仍然存在较重的首次加载情况。我自己也是Roslyn的新手,所以我不确定为什么这样更快,但这是修改后的代码。

代码语言:javascript
复制
        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);
            }
        }

通过使用这种技术,编译只需要96毫秒,在随后的迭代中需要大约3- 15ms。因此,我认为您在第一个加载场景中可能是正确的,增加了一些开销。

抱歉,我不能解释为什么它更快!我自己只是在研究Roslyn,今晚晚些时候我会做更多的挖掘,看看我是否能找到更多的证据来证明ModuleBuilder通过内存流提供了什么。

票数 2
EN

Stack Overflow用户

发布于 2016-09-10 03:55:23

我在使用ASP.net的Microsoft.CodeDom.Providers.DotNetCompilerPlatform包时也遇到了同样的问题。原来这个包启动的是csc.exe,它使用VBCSCompiler.exe作为编译服务器。默认情况下,VBCSCompiler.exe服务器的寿命为10秒,其引导时间约为3秒。这就解释了为什么运行一次或多次代码需要大约相同的时间。Microsoft似乎也在Visual Studio中使用此服务器,以避免您每次运行编译时都要花费额外的启动时间。

使用这个包,您可以监视您的进程,并会找到一个类似于csc.exe /keepalive:10的命令行

好的方面是,如果该服务器保持活动状态(即使在应用程序的两个会话之间),您可以始终获得快速编译。

不幸的是,Roslyn包并不是真正可定制的,我发现更改此keepalive常量的最简单方法是使用反射来设置非公共变量值。在我这边,我将它定义为全天,因为即使我关闭并重新启动应用程序,它也始终保持相同的服务器。

代码语言:javascript
复制
    /// <summary>
    /// Force the compiler to live for an entire day to avoid paying for the boot time of the compiler.
    /// </summary>
    private static void SetCompilerServerTimeToLive(CSharpCodeProvider codeProvider, TimeSpan timeToLive)
    {
        const BindingFlags privateField = BindingFlags.NonPublic | BindingFlags.Instance;

        var compilerSettingField = typeof(CSharpCodeProvider).GetField("_compilerSettings", privateField);
        var compilerSettings = compilerSettingField.GetValue(codeProvider);

        var timeToLiveField = compilerSettings.GetType().GetField("_compilerServerTimeToLive", privateField);
        timeToLiveField.SetValue(compilerSettings, (int)timeToLive.TotalSeconds);
    }
票数 1
EN

Stack Overflow用户

发布于 2014-01-22 03:03:34

当您调用Compilation.Emit()时,这是您第一次真正需要元数据,因此发生了元数据文件访问。在那之后,它被缓存了。虽然这不应该只占mscorlib的3秒。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/15365376

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档