- A+
jntemplate 是一款国产的文本解析引擎(模板引擎),最近2.0的版本进行了重构,笔者在Gihub上挑了几个模板引擎做了一个简单的小对比测试,看下国产的质量怎么样!
环境说明:
操作系统:Windows 10.0.19041.746
CPU: Intel Xeon CPU E3-1231 v3 3.40GHz
内存:8G
.NET 框架: net 5
测试工具:BenchmarkDotNet 0.12.1
参与测试的引擎:
JinianNet.JNTemplate 2.0.0
Mustachio 2.1.0
RazorEngineCore 2020.10.1
RazorLight 2.0.0-rc.3
Scriban 3.4.2
说明一下,Razor官方是视图引擎,不方便直接测试,所以找了二款基于Razor的第三方引擎来参与测试,其中RazorEngine因为版本太老无法支持.net 5,故选择了RazorLight 与RazorEngineCore
以 上文件均从nuget安装。语法参照官文档编写并尽量统一。
测试一:输出一个变量或者属性
测试用代码:
using BenchmarkDotNet.Attributes; using JinianNet.JNTemplate; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Test { [MemoryDiagnoser] public class TestVariable { [Benchmark] public void RunJntemplate() { var template = Engine.CreateTemplate("Hello $model.Id !"); template.Set("model", new UserInfo { Id = 10, Name = "your name!" }); var value = template.Render(); } [Benchmark] public void RunMustachio() { var template = Mustachio.Parser.Parse("Hello {{Id}} !"); dynamic model = new System.Dynamic.ExpandoObject(); model.Id = 10; model.Name = "your name!"; var value = template(model); } [Benchmark] public void RunScriban() { var template = Scriban.Template.Parse("Hello {{Id}} !"); var value = template.Render(new UserInfo { Id = 10, Name = "your name!" }); } [Benchmark] public void RunRazorEngineCore() { //RazorEngineCore.IRazorEngineCompiledTemplate template; var razorEngine = new RazorEngineCore.RazorEngine(); var template = razorEngine.Compile("Hello @Model.Id"); var value = template.Run(new UserInfo { Id = 10, Name = "your name!" }); } [Benchmark] public void RunRazorLight() { var engine = new RazorLight.RazorLightEngineBuilder() // required to have a default RazorLightProject type, // but not required to create a template from string. .UseEmbeddedResourcesProject(typeof(UserInfo)) //.SetOperatingAssembly(typeof(UserInfo).Assembly) .UseMemoryCachingProvider() .Build(); string template = "Hello @Model.Id"; var model = new UserInfo { Id = 10, Name = "your name!" }; string result = engine.CompileRenderStringAsync("templateKey", template, model).GetAwaiter().GetResult(); } } }
测试结果:
这里可以看到二个razor引擎所需要的时间是最长的,这是因为它们是编译型引擎,初始运行需要进行编译,所以会占用较多时间。
不过为什么jntemplate也是编译型引擎,所占时间却最少呢?
测试二:测试一的代码一共运行10万次
即然一次说明不了什么,接下来我们进行第二项测试,把上面的代码各运行10万次。
测试代码:
using BenchmarkDotNet.Attributes; using JinianNet.JNTemplate; using RazorEngineCore; using System.Collections.Concurrent; namespace Test { [MemoryDiagnoser] public class TestVariableMultiple { private int Max = 100000; [Benchmark] public void RunJntemplate() { string text = "Hello $model.Id"; var hashCode = text.GetHashCode().ToString(); for (var i = 0; i < Max; i++) { var template = JinianNet.JNTemplate.Engine.CreateTemplate(hashCode, text); template.Set("model", new UserInfo { Id = 10, Name = "your name!" }); var value = template.Render(); } } [Benchmark] public void RunScriban() { Scriban.Template template = Scriban.Template.Parse("Hello {{Id}} !"); for (var i = 0; i < Max; i++) { var value = template.Render(new UserInfo { Id = 10, Name = "your name!" }); } } [Benchmark] public void RunMustachio() { var template = Mustachio.Parser.Parse("Hello {{Id}} !"); for (var i = 0; i < Max; i++) { dynamic model = new System.Dynamic.ExpandoObject(); model.Id = 10; model.Name = "your name!"; var value = template(model); } } [Benchmark] public void RunRazorEngineCore() { var TemplateCache = new ConcurrentDictionary<int, IRazorEngineCompiledTemplate>(); string text = "Hello @Model.Id"; int hashCode = text.GetHashCode(); for (var i = 0; i < Max; i++) { IRazorEngineCompiledTemplate compiledTemplate = TemplateCache.GetOrAdd(hashCode, i => { RazorEngine razorEngine = new RazorEngine(); return razorEngine.Compile(text); }); var value = compiledTemplate.Run(new UserInfo { Id = 10, Name = "your name!" }); } } [Benchmark] public void RunRazorLight() { var engine = new RazorLight.RazorLightEngineBuilder() .UseEmbeddedResourcesProject(typeof(UserInfo)) .UseMemoryCachingProvider() .Build(); string template = "Hello @Model.Id"; var model = new UserInfo { Id = 10, Name = "your name!" }; for (var i = 0; i < Max; i++) { if (i==0) { string result = engine.CompileRenderStringAsync("templateKey", template, model).GetAwaiter().GetResult(); } else { var cacheResult = engine.Handler.Cache.RetrieveTemplate("templateKey"); if (cacheResult.Success) { var templatePage = cacheResult.Template.TemplatePageFactory(); string result = engine.RenderTemplateAsync(templatePage, model).GetAwaiter().GetResult(); } } } } } }
测试结果:
运行10万次后,编译型引擎的速度明显占优
测试三:foreach 遍历10万次
测试代码:
using BenchmarkDotNet.Attributes; using JinianNet.JNTemplate; using RazorEngineCore; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Test { /// <summary> /// / /// </summary> [MemoryDiagnoser] public class TestForeach { private int[] arr; private int max = 100000; public TestForeach() { arr = new int[max]; for(var i = 0; i < max; i++) { arr[i] = i; } } [Benchmark] public void RunScriban() { var template = Scriban.Template.Parse(@" <ul id='products'> {{ for product in products }} <li>{{ product }}</li> {{ end }} </ul> "); var result = template.Render(new { Products = arr }); } [Benchmark] public void RunJntemplate() { string text = @" <ul> $for(node in list) <li>$node</li> $end </ul> "; var hashCode = text.GetHashCode().ToString(); var template = JinianNet.JNTemplate.Engine.CreateTemplate(hashCode, text); template.Set("list", arr); var value = template.Render(); } [Benchmark] public void RunRazorEngineCore() { var razorEngine = new RazorEngineCore.RazorEngine(); var TemplateCache = new ConcurrentDictionary<int, IRazorEngineCompiledTemplate>(); string text = @" <ul> @{ foreach (var item in Model) { <li>@item</li> } } </ul> "; var template = razorEngine.Compile(text); var value = template.Run(arr); } [Benchmark] public void RunRazorLight() { var engine = new RazorLight.RazorLightEngineBuilder() // required to have a default RazorLightProject type, // but not required to create a template from string. .UseEmbeddedResourcesProject(typeof(UserInfo)) //.SetOperatingAssembly(typeof(UserInfo).Assembly) .UseMemoryCachingProvider() .Build(); string text = @" <ul> @{ foreach (var item in Model) { <li>@item</li> } } </ul> "; string result = engine.CompileRenderStringAsync("templateKey", text, arr).GetAwaiter().GetResult(); } } }
测试结果:
mustachio没有找到怎么foreach的语法,这里就没测试,Scriban 支持不了这么大的数组,所以没有结果。
还有一些复杂的用法,今天暂时就不测了,结论我就不用说了,当然本次测试并不完整,毕竟一个引擎涉及到的地方很多,仅仅一二个用法不能代表不了全部,所以结果仅供参考。
另外mustachio功能上太简单,其实无法和其它引擎相提并论,这里只拿来只是凑个数!
大家怎么看呢?