编译C#脚本很慢

时间:2015-07-03 14:01:49

标签: c# .net scripting codedom

我有一个使用"脚本的应用程序"用C#编写,在启动时使用C#CodeDomProvider编译。加载几十个,甚至> 170个脚本都不是问题,我编译它们然后将程序集保存到缓存文件夹中以便下次启动。对于170个脚本,这个初始编译大约需要1.5分钟。

然而,当我尝试加载> 1000个脚本时,花费了一个多小时来编译它们。我添加了一个秒表,并了解到每个脚本的加载时间比之前的要长一些,在170个文件的情况下,它从第一个的约150毫秒到最后一个的> 650毫秒,逐渐增加每个文件。

我知道通过将脚本组合到一个大文件中,我可以大大减少加载时间,但出于几个原因,如果我可以单独编译它们,我会更喜欢它:/它使重新加载它们变得容易很快,我不必担心在发生变化时重新编译整个脚本文件夹,我可以轻松地为编译进度提供进度条等。

现在我的问题是,这里的问题是什么?为什么每个文件的编译时间会随着时间而增加?我能为此做些什么吗?

修改

我会尝试在评论中提供更多信息。

正如我所说的,我正在使用C#CodeDomProvider编译每个脚本,对于我目前的每个171个脚本文件,在循环中基本上都是以下,每个文件包含一个或多个类我在创建程序集后实例化:

var provider = System.CodeDom.Compiler.CodeDomProvider.CreateProvider("CSharp"); ;
var parameters = new System.CodeDom.Compiler.CompilerParameters();
parameters.ReferencedAssemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).Select(a => a.Location).ToArray());
parameters.GenerateExecutable = false;
parameters.GenerateInMemory = false;
parameters.OutputAssembly = tmpFileName + ".compiled";
parameters.TreatWarningsAsErrors = false;
parameters.WarningLevel = 0;
var results = provider.CompileAssemblyFromFile(parameters, tmpFileName);
asm = results.CompiledAssembly;

我使用CS-Script得到了相同的结果,它做了类似的事情。

asm = CSScript.LoadWithConfig(tmpFileName, null, debug, CSScript.GlobalSettings, "/warnaserror- /warn:0");

然后将创建的程序集保存到缓存文件夹中。当脚本文件比程序集旧时,使用保存的程序集,因此我不必再次编译它。

随着时间的推移变得越来越慢,当你坐在它前面时,看着进度条越来越慢,所以我在上面的代码StartNew, Stop, Elapsed周围添加了一个简单的秒表调用,没什么特别的。结果反映了我在进度条上看到的内容,每个附加文件的编译时间都增加了。

timer.Restart();

// compile as shown above

timer.Stop();
Console.WriteLine(asm.Location + ": " + timer.Elapsed);

文件之间的细微差别当然是可取的,这取决于文件的大小和复杂程度,但脚本的大小和复杂程度通常都大致相同,而且我目睹了随着时间的推移不断增加。

Temp\tmpDDD7.tmp.compiled: 00:00:00.1389177
Temp\tmpDE74.tmp.compiled: 00:00:00.1327150
...
Temp\tmpE156.tmp.compiled: 00:00:00.1719746
Temp\tmpE213.tmp.compiled: 00:00:00.1431011
...
Temp\tmpF05C.tmp.compiled: 00:00:00.1696297
Temp\tmpF118.tmp.compiled: 00:00:00.1739564
...
Temp\tmpF7D5.tmp.compiled: 00:00:00.1824292
Temp\tmpF891.tmp.compiled: 00:00:00.1819889
...
Temp\tmp29F1.tmp.compiled: 00:00:00.2912163
Temp\tmp2B2B.tmp.compiled: 00:00:00.2909096
...
Temp\tmp362F.tmp.compiled: 00:00:00.3161408
Temp\tmp3773.tmp.compiled: 00:00:00.3170768
...
Temp\tmpA4C9.tmp.compiled: 00:00:00.5457990
Temp\tmpA6FC.tmp.compiled: 00:00:00.5460514

2 个答案:

答案 0 :(得分:1)

事实上,它可能是这种行为的原因。事实上很可能是。

您可以通过将CSScript.ShareHostRefAssemblies设置为true来轻松更改行为。

虽然在这种情况下,您需要注意引用其他脚本的所有脚本。一种方法是通过提供所需的程序集文件名(在Load(string scriptFile, string assemblyFile...)中为所有'可共享'脚本,然后使用这些文件名作为您加载的所有脚本的输入。

//Assembly Load(string scriptFile, string assemblyFile, bool debugBuild, params string[] refAssemblies)
CSScript.Load("common_scriptA.cs", "asmA.dll", false);
CSScript.Load("common_scriptB.cs", "asmB.dll", false);
CSScript.Load("common_scriptC.cs", "asmC.dll", false);
CSScript.Load("normal_scriptA.cs", null, false, "asmC.dll", "asmB.dll", "asmC.dll");
CSScript.Load("normal_scriptB.cs", null, false, "asmC.dll", "asmB.dll", "asmC.dll");

答案 1 :(得分:0)

编译变得越来越慢的原因是因为每个创建的程序集都被添加为对所有后续脚本文件的引用。在第一个文件上,它可能是10个引用,第二个是11,在第三个是12,依此类推。引用越多,编译时间越长。这就是以下行所做的,以及CS-Script默认执行的操作。

parameters.ReferencedAssemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.IsDynamic).Select(a => a.Location).ToArray());

不幸的是,我希望脚本能​​够互相引用,所以我现在必须提出一个参考系统。但至少我知道问题是什么。也许我会尝试自动化它,记住哪些类型可以在哪个组件中找到,以便自动引用它们。