C#脚本引擎 CS-Script 之(二)——性能评测
以下以一个简单的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("");
}
}

四、多次动态编译同一个脚本并调用方法的性能分析
测试代码:
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左右的时间,其他情况的性能损失基本可以忽略。
七、相关源码
本系列包括:
C#脚本引擎 CS-Script 之(二)——性能评测的更多相关文章
- C#脚本引擎 CS-Script 之(三)——如何部署
本文不但介绍了CS-Script如何部署,还介绍了CS-Script的部署后面的原理,并用一个框图详细介绍了部署中的各种细节. 一.获取资源 1.从官网上下载编译好的csscript资源:cs-scr ...
- C#脚本引擎 CS-Script 之(一)——初识
最近在做新产品,这个产品需要满足不同项目对于系统的定制性数据处理需求,比如有的要统计一段时间内某开关打开关闭了多少次,有的要统计一段时间内空调的使用率,有的希望根据温度来控制空调的开还是关,有的则是希 ...
- [19/04/19-星期五] Java的动态性_脚本(Script,脚本)引擎执行JavaScript代码
一.概念 Java脚本引擎是jdk 6.0之后的新功能. 使得Java应用程序可以通过一套固定的接口与各种脚本引擎交互,从而达到在Java平台上调用各种脚本语言的目的. Java脚本API是连接Jav ...
- 链接脚本(Linker Script)用法解析(二) clear_table & copy_table
可执行文件中的.bss段和.data段分别存放未赋初值的全局变量和已赋初值的全局变量,两者的特点分别为: (1).bss段:①无初值,所以不占ROM空间:②运行时存储于RAM:③默认初值为0 (2). ...
- nginx的脚本引擎(二)rewrite
其实rewrite指令和上一篇说的if/set/return/break之类的没多大差别,但是rewrite用起来相对复杂,我就把他单独放到了这里.想要弄懂nginx的脚本引擎需要先明白处理reque ...
- 复杂多变场景下的Groovy脚本引擎实战
一.前言 因为之前在项目中使用了Groovy对业务能力进行一些扩展,效果比较好,所以简单记录分享一下,这里你可以了解: 为什么选用Groovy作为脚本引擎 了解Groovy的基本原理和Java如何集成 ...
- 使用Lua脚本语言开发出高扩展性的系统,AgileEAS.NET SOA中间件Lua脚本引擎介绍
一.前言 AgileEAS.NET SOA 中间件平台是一款基于基于敏捷并行开发思想和Microsoft .Net构件(组件)开发技术而构建的一个快速开发应用平台.用于帮助中小型软件企业建立一条适合市 ...
- 利用Roslyn构建一个简单的C#交互脚本引擎
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 微软的下一代编译器技术Roslyn是一个里程碑的技术,可以给.NET平台带来无限想象空间.比 ...
- Nmap脚本引擎原理
Nmap脚本引擎原理 一.NSE介绍 虽然Nmap内嵌的服务于版本探测已足够强大,但是在某些情况下我们需要多伦次的交互才能够探测到服务器的信息,这时候就需要自己编写NSE插件实现这个功能.NSE插件能 ...
随机推荐
- Cocos2d-X-3.0之后的版本的环境搭建
由于cocos2d游戏开发引擎更新十分频繁,官方文档同步不够及时和完善.所以不要照着官方文档来照做生成工程. <点击图片就能进入网站> 具体的步骤: 1.获取cocos2d-X的源码v3. ...
- sql 存储过程中top 后面跟参数的问题
之前存储过程中有top的情况,都是拼接sql,然后通过exec执行,进行查询结果,很不方便. 今天研究了,原来top后面是可以直接写参数的. 只需要top 后面的参数加上小括号就好了 eg: TOP ...
- 安装和配置JDK,并给出安装、配置JDK的步骤。
1.从orcal网站下载安装包. 2.安装目录不要有汉字或空格 3.配置环境变量,增加·JAVA_HOME=jdk的全路径,修改path+%JAVA_HOME%\bin;%JAVA_HOME%\jre ...
- Navicat for Mysql远程连接数据时报(1045错误)Access denied for user 'root'@'localhost' (using password yes);
原因:用户访问被拒绝,更改用户赋予密码即可 mysql命令行执行语句如下 //使用mysql,读取表信息 //更改用户赋予登录密码 //更新权限 注意点:使用flush privileges是为了刷新 ...
- 如何解决ajax跨域问题(转)
由 于此前很少写前端的代码(哈哈,不合格的程序员啊),最近项目中用到json作为系统间交互的手段,自然就伴随着众多ajax请求,随之而来的就是要解决 ajax的跨域问题.本篇将讲述一个小白从遇到跨域不 ...
- javascript图片库
将图片发布到网上的办法很多,可以简单地把所有图片都放在一个网页中,但是会导致这个网页过于庞大.为每张图片分别创建一个网页的解决办法值得考虑,但是制作过程需要花费非常多的时间和精力. 如果想要两全其美, ...
- 第十章 MySQL 常用函数
第十章 MySQL 常用函数 第一节:日期和时间函数 1,CURDATE() 返回当前日期:2,CURTIME() 返回当前时间:3,MONTH(d) 返回日期 d 中的月份值,范围是 1~12 第二 ...
- 对Jena的简单理解和一个例子
本文简单介绍Jena(Jena 2.4),使用Protégé 3.1(不是最新版本)创建一个简单的生物(Creature)本体,然后参照Jena文档中的一个例子对本体进行简单的处理,输出本体中的Cla ...
- poj 1144 Network 图的割顶判断模板
Network Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 8797 Accepted: 4116 Descripti ...
- openstack排错
一.排错方法: 1.查看日志路径为/var/log,具体哪个组件出了问题进入其目录查看. 2.debug root@sc-ctrl01:~# keystone --debug user-list ro ...