罗斯林慢启动时间 [英] Roslyn slow startup time
问题描述
我已经注意到,启动时间罗斯林解析/编译是一个相当显著的一次性成本。编辑:我使用的是罗斯林CTP MSI(装配在GAC)。难道这正常吗?有没有什么解决方法吗?
运行下面的代码需要几乎相同的时间量与1迭代(〜3秒),与300次迭代(〜3秒)。
[测试]
公共无效测试()
{
VAR iters = 300;
的foreach(VAR我在Enumerable.Range(0,iters))
{
//使用解析罗斯林$ B $源文件B SyntaxTree syntaxTree = SyntaxTree.ParseText(@公共类富+ 1 + @{公共无效执行exec(){}});
//添加我们需要编译
变种引用=新的List<的所有引用; MetadataReference>();
references.Add(新MetadataFileReference(typeof运算(int)的.Assembly.Location));
变种compilationOptions =新CompilationOptions(outputKind:OutputKind.DynamicallyLinkedLibrary);
//注意:使用固定装配的名字,这并不重要,只要不希望产生的组件
VAR编译= Compilation.Create(SomeAssemblyName的交叉引用,compilationOptions,新】{} syntaxTree,参考文献);
//生成组装成内存流
变种memStream =新的MemoryStream();
//如果我们从这一行注释掉上下,运行时降至〜0.5秒
EmitResult emitResult = compilation.Emit(memStream);
VAR ASM =的Assembly.Load(memStream.GetBuffer());
变种类型= asm.GetTypes()单(T => t.Name ==富+ I);
}
}
我想到一个问题是使用内存流,而不是你应该尝试使用动态模块和ModuleBuilder来代替。总体而言,代码执行速度更快,但仍然有较重的第一负载场景。我是很新,罗斯林自己,所以我不知道为什么这是快,但这里是改变的代码。
VAR iters = 300;
的foreach(VAR我在Enumerable.Range(0,iters))
{
//使用解析罗斯林$ B $源文件B SyntaxTree syntaxTree = SyntaxTree.ParseText(@公共类富+ 1 + @{公共无效执行exec(){}});
//添加我们需要编译
变种引用=新名单,LT的所有引用; MetadataReference>();
references.Add(新MetadataFileReference(typeof运算(int)的.Assembly.Location));
变种compilationOptions =新CompilationOptions(outputKind:OutputKind.DynamicallyLinkedLibrary);
//注意:使用固定的程序集的名称,这不要紧,只要我们不期望产生装配
VAR编译= Compilation.Create(SomeAssemblyName跨引用,compilationOptions,新的[] {} syntaxTree,参考文献);
VAR assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(新System.Reflection.AssemblyName(CustomerA),
System.Reflection.Emit.AssemblyBuilderAccess.RunAndCollect);
VAR moduleBuilder = assemblyBuilder.DefineDynamicModule(MyModule的);
System.Diagnostics.Stopwatch手表=新System.Diagnostics.Stopwatch();
watch.Start();
//如果我们从这个线路和下行注释掉,运行时间降到〜0.5秒
VAR emitResult = compilation.Emit(moduleBuilder);
watch.Stop();
System.Diagnostics.Debug.WriteLine(watch.ElapsedMilliseconds);
如果(emitResult.Diagnostics.LongCount()== 0)
{
变种类型= moduleBuilder.GetTypes()单(T =方式> t.Name == 富+ I);
System.Diagnostics.Debug.Write(A型!= NULL); $ B $黑} $ B $黑}
通过使用这种技术的编译只花了96(毫秒)在后续的迭代大约需要3 - 15毫秒。所以,我认为,你可以在第一负载情况下,添加一些开销而言是正确的。
对不起,我无法解释为什么它的速度更快!我只是研究罗斯林自己,会做更多的挖掘今晚稍后,看看我能找到什么样的ModuleBuilder提供过任何的MemoryStream更多的证据。
I've noticed that the startup time for Roslyn parsing/compilation is a fairly significant one-time cost. EDIT: I am using the Roslyn CTP MSI (the assembly is in the GAC). Is this expected? Is there any workaround?
Running the code below takes almost the same amount of time with 1 iteration (~3 seconds) as with 300 iterations (~3 seconds).
[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);
}
}
I think one issue is using a memory stream, instead you should try using a dynamic module and ModuleBuilder instead. Overall the code is executing faster but still has a heavier first load scenario. I'm pretty new to Roslyn myself so I'm not sure why this is faster but here is the changed code.
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);
}
}
By using this technique the compilation took just 96 milliseconds, on subsequent iterations it takes around 3 - 15ms. So I think you could be right in terms of the first load scenario adding some overhead.
Sorry I can't explain why it's faster! I'm just researching Roslyn myself and will do more digging later tonight to see if I can find any more evidence of what the ModuleBuilder provides over the memorystream.
这篇关于罗斯林慢启动时间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!