以下以一个简单的HelloWord程序为例,来分析csscript脚本引擎的性能。

 class HelloWorld
{
public void SayHello()
{
Console.WriteLine("Hello World, from internal!");
}
}

一、测试环境

运行的机器硬件配置:Intel Dore Duo CPU,内存 4;

开发环境: vs2010;

二、使用程序内部类和使用脚本的性能比较

  static void Main(string[] args)
{
CallFromInternal();
CallFromScript();
} static void CallFromInternal()
{
Console.WriteLine("");
Console.WriteLine("CallFromInternal");
DateTime beginTime = DateTime.Now; HelloWorld hello = new HelloWorld();
TimeSpan span = DateTime.Now - beginTime;
Console.WriteLine("create instance timespan: {0}", span);
beginTime = DateTime.Now;
hello.SayHello(); span = DateTime.Now - beginTime;
Console.WriteLine("call helloWorld timespan: {0}", span);
} static void CallFromScript()
{
Console.WriteLine("");
Console.WriteLine("CallFromScript");
DateTime beginTime = DateTime.Now; dynamic hello = CSScript.Evaluator.LoadFile("HelloWorld.cs");
TimeSpan span = DateTime.Now - beginTime;
Console.WriteLine("load and precompile script file, timespan= {0}", span); beginTime = DateTime.Now;
hello.SayHello(); span = DateTime.Now - beginTime;
Console.WriteLine("call helloWorld timespan: {0}", span);
}

从以上两个函数的输出结果来看,直接调用程序内部函数的时间大概是2ms,而通过脚本引擎来同样一个HelloWorld的时间就达到了835ms,时间差距有400倍。

这835ms中,动态编译及其对象创建就花了814ms,而函数调用则21ms,所以即使抛开动态编译的成本,这个函数调用,由于内部其实是使用反射的机制来实现的,所以性能损失也比较明显。

三、一次动态编译多次调用

测试代码:

 static void CallFromSameScriptLoad1TimeAndCall4Times()
{
Console.WriteLine("");
Console.WriteLine("CallFromSameScriptLoad1TimeAndCall4Times");
DateTime beginTime = DateTime.Now;
TimeSpan span;
dynamic hello = CSScript.Evaluator.LoadFile("HelloWorld.cs");
span = DateTime.Now - beginTime;
Console.WriteLine("load and precompile script file, timespan= {0}", span); for (int i = ; i < ; ++i)
{
beginTime = DateTime.Now;
hello.SayHello();
span = DateTime.Now - beginTime;
Console.WriteLine("call helloWorld {0} time, timespan: {1}", i+, span);
Console.WriteLine("");
}
}
    运行结果如下, 可以看出,第一次调用花了21ms,后面3次调用的时间基本可以忽略。那么推测,第一次是因为需要通过反射的方式找到SayHello方法的引用,后面的几次调用估计已经把该方法的引用缓存了,就可以直接前面查找好的委托,少了一个通过反射查找的过程,所以速度基本和调用本地方法相当。
以上只是推测,后续需要查阅源码分析看看。

四、多次动态编译同一个脚本并调用方法的性能分析

测试代码:

  static void CallFromSameScriptLoadAndCall4Times()
{
Console.WriteLine("");
Console.WriteLine("CallFromSameScriptLoadAndCall4Times"); TimeSpan span;
for (int i = ; i < ; ++i)
{
DateTime beginTime = DateTime.Now;
dynamic hello = CSScript.Evaluator.LoadFile("HelloWorld.cs");
span = DateTime.Now - beginTime;
Console.WriteLine("load and precompile script file, {0}, timespan= {1}", i+, span);
beginTime = DateTime.Now;
hello.SayHello();
) span = DateTime.Now - beginTime;
Console.WriteLine("call helloWorld {0} time, timespan: {1}", i+, span);
Console.WriteLine("");
}
}

测试结果如下,第一次调用的时间花销大,后续的时间花销基本相当于上一节中的第一次调用方法的时间。

那么推测:

(1) 对于同一个cs源文件,第一次编译之后会缓存,会把程序集缓存到内存中,后续再调用LoadFile的时候,实际上加载的是内存中缓存的程序集;

(2)第二次及其后续调用LoadFile("HelloWorld.cs"),实际上都是使用内存中的程序集,但是通过反射的方式找到HelloWorld类还是要每次去做的,所以一般还需要花费3ms左右;

(3)然后由于每个循环中都重新创建了一个新的HelloWord对象,在每个循环中去调用hello.SayHello();的时候,实际上还是实时的使用反射机制去查找hello对象中的SayHello方法,所以这里的时间花销省不了,一般也需要花费7ms左右。

推测:

(1)同一个程序集,多次编译,会使用第一次编译缓存的程序集;

(2)同一个类的多个对象的同一个方法(比如HelloWord类的SayHello方法),每个对象第一次调用该方法时,都需要使用反射方式去查找,所以此时性能较低;

五、动态编译多个不同的脚本的性能分析

测试代码:

 static void CallFromMultiScriptLoadAndCall4Times()
{
Console.WriteLine("");
Console.WriteLine("CallFromMultiScriptLoadAndCall4Times"); TimeSpan span;
for (int i = ; i < ; ++i)
{
DateTime beginTime = DateTime.Now;
string fileName = string.Format("HelloWorld{0}.cs", i + );
dynamic hello = CSScript.Evaluator.LoadFile(fileName);
span = DateTime.Now - beginTime;
Console.WriteLine("load and precompile script file{2}, {0}, timespan= {1}", i+, span, fileName);
beginTime = DateTime.Now;
hello.SayHello();
span = DateTime.Now - beginTime;
Console.WriteLine("call helloWorld {0} time, timespan: {1}", i+, span);
}
}

测试结果如下:

这里分别动态编译了四个源文件,并调用对应的方法。但是只有第一次编译的时候速度慢,后续三次动态编译的速度和上一节动态编译同一个源文件的速度一样快。到这里就推翻了上一节的结论,说明上一节中2~4次的动态编译速度快,不是因为缓存了第一次动态编译的程序集。那么推测可能是因为第一次要动态编译的时候,程序要将.NET的用于动态编译的程序集(CSharpCodeProvider)加载到内存中,这个过程可能比较花时间,而动态编译本身是很快的。

六、结论

(1)在使用cs-script脚本引擎的时候,该程序第一次做动态编译时,需要有个1s左右的初始化时间;

(2)对于脚本中类的对象的方法的调用,在第一次调用某个对象的方法时(比如上文的HelloWorld类的hell对象的SayHello()方法),由于要使用反射方式去查找该犯法的委托,所以相比原生的对象方法调用要多10ms左右,后续的调用则和原生的方法差不多。

(3)cs-script编译一个普通源文件的时间基本是毫秒级别,一般在10ms以内,对于一般脚本数量不是很多(比如几十个)的情况,一般也就是多花几百毫秒,基本可以忽略;

综上,在引入了cs-script脚本引擎之后,在享受了脚本所带来的动态特性的同时,只是在初始化的时候需要多花1s左右的时间,其他情况的性能损失基本可以忽略。

七、相关源码

CSScript系列之(二)——性能评测.zip

本系列包括:

C#脚本引擎 CS-Script 之(一)——初识

C#脚本引擎 CS-Script 之(二)——性能评测

C#脚本引擎CS-Script之(三)——如何部署

C#脚本引擎 CS-Script 之(二)——性能评测的更多相关文章

  1. C#脚本引擎 CS-Script 之(三)——如何部署

    本文不但介绍了CS-Script如何部署,还介绍了CS-Script的部署后面的原理,并用一个框图详细介绍了部署中的各种细节. 一.获取资源 1.从官网上下载编译好的csscript资源:cs-scr ...

  2. C#脚本引擎 CS-Script 之(一)——初识

    最近在做新产品,这个产品需要满足不同项目对于系统的定制性数据处理需求,比如有的要统计一段时间内某开关打开关闭了多少次,有的要统计一段时间内空调的使用率,有的希望根据温度来控制空调的开还是关,有的则是希 ...

  3. [19/04/19-星期五] Java的动态性_脚本(Script,脚本)引擎执行JavaScript代码

    一.概念 Java脚本引擎是jdk 6.0之后的新功能. 使得Java应用程序可以通过一套固定的接口与各种脚本引擎交互,从而达到在Java平台上调用各种脚本语言的目的. Java脚本API是连接Jav ...

  4. 链接脚本(Linker Script)用法解析(二) clear_table & copy_table

    可执行文件中的.bss段和.data段分别存放未赋初值的全局变量和已赋初值的全局变量,两者的特点分别为: (1).bss段:①无初值,所以不占ROM空间:②运行时存储于RAM:③默认初值为0 (2). ...

  5. nginx的脚本引擎(二)rewrite

    其实rewrite指令和上一篇说的if/set/return/break之类的没多大差别,但是rewrite用起来相对复杂,我就把他单独放到了这里.想要弄懂nginx的脚本引擎需要先明白处理reque ...

  6. 复杂多变场景下的Groovy脚本引擎实战

    一.前言 因为之前在项目中使用了Groovy对业务能力进行一些扩展,效果比较好,所以简单记录分享一下,这里你可以了解: 为什么选用Groovy作为脚本引擎 了解Groovy的基本原理和Java如何集成 ...

  7. 使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍

    一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...

  8. 利用Roslyn构建一个简单的C#交互脚本引擎

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 微软的下一代编译器技术Roslyn是一个里程碑的技术,可以给.NET平台带来无限想象空间.比 ...

  9. Nmap脚本引擎原理

    Nmap脚本引擎原理 一.NSE介绍 虽然Nmap内嵌的服务于版本探测已足够强大,但是在某些情况下我们需要多伦次的交互才能够探测到服务器的信息,这时候就需要自己编写NSE插件实现这个功能.NSE插件能 ...

随机推荐

  1. virt manager 提示权限不允许(ubuntu)

    问题描述: 新安装virt manager 打开提示权限不允许(ubuntu 15.04); 提示检查libvirt-bin包是否安装:libvirtd服务是否已运行:当前用户是否在libvirtd组 ...

  2. eclipse 导入新项目后报错:Cannot change version of project facet Dynamic web module to 2.5

    错误原因: 我们用Eclipse创建Maven结构的web项目的时候选择了Artifact Id为maven-artchetype-webapp,由于这个catalog比较老,用的servlet还是2 ...

  3. Java 读取大文件方法

    需求:实际开发中读取文本文件的需求还是很多,如读取两个系统之间FTP发送文件,读取后保存到数据库中或日志文件的数据库中保存等. 为了测试首先利用数据库SQL生成大数据文件. 规则是 编号|姓名|手机号 ...

  4. jetbrains DataGrip 导出Excel

    之前一直不知道如何通过DataGrip 导出Excel,导致每次都是用Navcat来做导出,今天研究了下,原来用TSV格式就能复制到Excel表格,具体步骤如下: ------------------ ...

  5. Swing应用开发实战系列之四:组件内容实时刷新问题

    窗口组件动态刷新问题,在dotnet中根本不算什么问题,用几句代码很轻松就能搞定,但是在Swing中,实现动态刷新组件内容却是一件颇为吃力的事情.譬如针对我们经常用到的刷新JLable.JTextFi ...

  6. Android开发之 Android 的基本组件的概述

    Android是一个为组件化而搭建的平台,它的应用是由一些零散的有联系的组件组成,并通过AndroidManifest.xml文件 把它们绑定起来. Android常用的组件有: Activity(活 ...

  7. try catch finally 用法

    trycatchfinally 1.将预见可能引发异常的代码包含在try语句块中.2.如果发生了异常,则转入catch的执行.catch有几种写法:catch这将捕获任何发生的异常.catch(Exc ...

  8. android SQLiteOpenHelper使用示例

    我们大家都知道Android平台提供给我们一个数据库辅助类来创建或打开数据库,这个辅助类继承自SQLiteOpenHelper类,在该类的 构造器中,调用Context中的方法创建并打开一个指定名称的 ...

  9. windows server 2003下安装.net framework 3.5 一直安装不成功

    安装包是在微软下载中心下的197m的文件 重启了也是不行,最后 找到.net framework 3.5 sp1 一个237m的安装包 安装成功了! Congratulations ! ​

  10. 迅为三星Exynos 4412开发板四核Cortex-A9ARM安卓linux开发板

    开发板光盘资料包含:原理图(PDF格式).底板PCB(Allegro格式).驱动程序源码.芯片和LCD数据手册.开发环境.产品使用手册. 4412开发板简介: iTOP-Exynos4412开发板采用 ...