.net模板引擎 jntemplate v2.0 测试, 要国产,也要速度

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功能上太简单,其实无法和其它引擎相提并论,这里只拿来只是凑个数!

 大家怎么看呢?

posted @ 2021-02-05 22:55  翅膀的初衷  阅读(183)  评论(0编辑  收藏