以下以一个简单的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. Effective Java 65 Don't ignore exceptions

    Principle An empty catch block defeats the purpose of exceptions, which is to force you to handle ex ...

  2. MySQL的诡异同步问题-重复执行一条relay-log

    MySQL的诡异同步问题 近期遇到一个诡异的MySQL同步问题,经过多方分析和定位后发现居然是由于备份引发的,非常的奇葩,特此记录一下整个问题的分析和定位过程. 现象 同事扩容的一台slave死活追不 ...

  3. Python开发之【用户登录锁定】

    用户登录验证,输入密码错误三次,锁定此用户. 将用户信息存入文件 文件内容格式如下: {','login_flag':True} {','login_flag':True} {','login_fla ...

  4. Azure 上为Liunx VM 挂载File类型的存储。

    1. Create a storage account in Azure, copy the storage account endpoint URL (with postfix of "f ...

  5. beeline vs hive cli

    近期,大数据开发环境升级为cloudera 5.3. 配套的hive版本升级为0.13.1.可以使用心仪已久的分析开窗函数了.但在使用的过程中发现一些问题,仅记于此. 1.在使用hive命令的时候,发 ...

  6. 使用LVS实现负载平衡之Windows Server 2008配置

    LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统.本项目在1998年5月由章文嵩博士成立,是中国国内最早出现的自由软件项目之一.承载于 II ...

  7. HTML5性能优化

    HTML5性能优化 在看完这两章内容之后,我意犹未尽,于是乎从网上搜索关键字“Java Web高性能”,在IBM社区找到两篇不错的文章,而让人更意外的是我发现那两篇文章的内容跟<高性能HTML5 ...

  8. 半径无关单核单线程最快速高斯模糊实现(附完整C代码)

    之前,俺也发过不少快速高斯模糊算法. 俺一般认为,只要处理一千六百万像素彩色图片,在2.2GHz的CPU上单核单线程超过1秒的算法,都是不快的. 之前发的几个算法,在俺2.2GHz的CPU上耗时都会超 ...

  9. codeforces 487C C. Prefix Product Sequence(构造+数论)

    题目链接: C. Prefix Product Sequence time limit per test 1 second memory limit per test 256 megabytes in ...

  10. HDU 4419 Colourful Rectangle --离散化+线段树扫描线

    题意: 有三种颜色的矩形n个,不同颜色的矩形重叠会生成不同的颜色,总共有R,G,B,RG,RB,GB,RGB 7种颜色,问7种颜色每种颜色的面积. 解法: 很容易想到线段树扫描线求矩形面积并,但是如何 ...