我注意到 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);
}
}
我认为一个问题是使用内存流,相反,您应该尝试使用动态模块和 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 - 15 毫秒。所以我认为你在第一个负载场景增加一些开销方面可能是正确的。
抱歉,我无法解释为什么它更快!我自己正在研究 Roslyn,今晚晚些时候我会做更多的挖掘,看看是否能找到 ModuleBuilder 通过内存流提供的更多证据。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)