以下以一个简单的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. UVa 109 - SCUD Busters(凸包计算)

    题目来源:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=3&pa ...

  2. 2013年 蓝桥杯预赛 java 本科A 题目

    1.标题: 世纪末的星期 曾有邪教称1999年12月31日是世界末日.当然该谣言已经不攻自破. 还有人称今后的某个世纪末的12月31日,如果是星期一则会.... 有趣的是,任何一个世纪末的年份的12月 ...

  3. zju 1937 初涉——深度优先搜索

    #include "stdio.h" int a[11],b[11]; int k,flag,n,s; void DFS(); int main() { int i; while( ...

  4. 【node.js】安装express后,'express' 不是内部或外部命令的问题

    因express默认安装是最新的版本,已经是4.x.x的版本.而最新express4.0+版本中将命令工具分出来了,所以必须要安装express-generator,执行: D:\TOOLS\Node ...

  5. HttpURLConnection发送POST请求(可包含文件)

    import java.io.BufferedReader; import java.io.DataOutputStream; import java.io.File; import java.io. ...

  6. Solr字段配置错误

    在站内搜索Solr Schema设计时,有个FTS_URL字段(之前设计url也会参与检索和打分),因此其配置信息如下: <field name="FTS_URL" type ...

  7. USACO section1.2 Miking cows

    /* ID: vincent63 LANG: C TASK: milk2 */ #include <stdio.h> #include<stdlib.h> #include&l ...

  8. 使用jsonpath解析json内容

    JsonPath提供的json解析非常强大,它提供了类似正则表达式的语法,基本上可以满足所有你想要获得的json内容.下面我把官网介绍的每个表达式用代码实现,可以更直观的知道该怎么用它. 一.首先需要 ...

  9. OpenCV的安装与系统环境变量

    OpenCV的安装与系统环境变量 安装OpenCV本来是很简单的一件事,但配置却很麻烦.而且在配置过程中尤为重要的步骤就是系统环境变量的配置.我使用的是CodeBlick13.12与OpenCV1.0 ...

  10. 第2章 面向对象的设计原则(SOLID):3_依赖倒置原则(DIP)

    3. 依赖倒置原则(Dependence Inversion Principle,DIP) 3.1 定义 (1)要依赖抽象,不要依赖具体的实现类.简单的说就是对抽象(或接口)进行编程,不要依赖实现进行 ...