我注意到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);
}
}发布于 2013-07-24 07:12:39
我认为其中一个问题是使用内存流,相反,您应该尝试使用动态模块和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);
}
}通过使用这种技术,编译只需要96毫秒,在随后的迭代中需要大约3- 15ms。因此,我认为您在第一个加载场景中可能是正确的,增加了一些开销。
抱歉,我不能解释为什么它更快!我自己只是在研究Roslyn,今晚晚些时候我会做更多的挖掘,看看我是否能找到更多的证据来证明ModuleBuilder通过内存流提供了什么。
发布于 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常量的最简单方法是使用反射来设置非公共变量值。在我这边,我将它定义为全天,因为即使我关闭并重新启动应用程序,它也始终保持相同的服务器。
/// <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);
}发布于 2014-01-22 03:03:34
当您调用Compilation.Emit()时,这是您第一次真正需要元数据,因此发生了元数据文件访问。在那之后,它被缓存了。虽然这不应该只占mscorlib的3秒。
https://stackoverflow.com/questions/15365376
复制相似问题