谈表达式树的缓存(6):五种缓存方式的性能比较

开始还债,因为还有至少两个可写的重要话题依赖在这个系列上,不解决就难以前进。

目前我们已经涉及了五种不同的缓存实现,它们分别是:

SimpleKeyCache:构造字符串作为Key,使用字典作为存储。

PrefixTreeCache:使用前缀树进行存储。

SortedListCache:使用排序列表或二叉搜索树进行存储。

HashedListCache:先对表达式树取散列值,再从字典中取出二叉搜索树。

DictionaryCache:实现了散列值和表达式树的比较方法,直接使用字典进行存储。

如果要从一个已经包含n个表达式树的存储中,查找一个有m个节点的表达式树,根据几篇文章的分析 ,从理论上说除了HashedListCache的时间复杂度是O(m * log(n))之外,其它几种实现的时间复杂度都是 O(m)。不过,理论上的结果和实际使用中的效果完全符合吗?如果完全符合的话,那么我们在构建第一个 SimpleKeyCache,获得了一种既简单直观又“高效”(达到了理论上最好的时间复杂度O(m))的实现之后 为什么还要继续设计剩下的方案呢?如果您看完了文章还没有想到,这说明您的.NET编程“常识”还需要 加强。

那么我们就写一个程序,让数据说话。

这是一个控制台应用程序,接受用户参数,并由此生成试验数据,或进行性能比较。

生成试验数据

需要进行测试,自然要准备试验数据,而这里所需要的试验数据自然是大量的表达式树。

表达式树的种类非常纷繁,如果要构造各种类型的树,其代价也是非常昂贵的。因此在这里,我们只 构建所谓的“整数的四则运算”表达式进行试验。对于这样的表达式,每个运算符占用一个节点,每个数 字又会占用另一个节点,因此表达式数的节点个数m便是操作符的个数p,与数字的个数q之和。而由于每 个元算符都是二元运算符,因此p等于q - 1。于是我们就可以得出m与p之间的关系:

m = 2p + 1

知道了这个关系,我们便可以获得一定规模的试验数据。于是我们写一个简单的小程序来随机输出一 个表达式:

private static void WriteSingleExpression(
   TextWriter writer, Random random, int opCount)
{
   string ops = "+-*/";

   writer.Write(random.Next(100));
   while (opCount-- > 0)
   {
     writer.Write(" ");
     writer.Write(ops[random.Next(4)]);
     writer.Write(" ");
     writer.Write(random.Next(100));
   }
   writer.WriteLine();
}

时间: 2024-12-22 17:07:46

谈表达式树的缓存(6):五种缓存方式的性能比较的相关文章

谈表达式树的缓存(7):五种缓存方式的总体分析及改进方案

终于到了这个系列的最后一篇文章了,这个系列的文章本是许多话题的基础,却拖了那么长时间还没 有完结.这篇文章主要讨论五种缓存方式各自的优劣,以及他们的性能关键在什么地方,如果要进行改进 又有什么可选方案.在这个问题上,老赵的思考可能会有遗漏,如果您有任何补充,也请不吝指出. SimpleKeyCache SimpleKeyCache是最直接的缓存方案:将表达式树进行完整编码,将其转化为一个独一无二的字符串 ,然后作为Key存放到字典中. 从上一篇文章的实验来看,无论从耗时还是对GC造成的压力上来说

30分钟行完960公里 第五种交通方式

近日,据相关http://www.aliyun.com/zixun/aggregation/894.html">媒体报道,美国科技狂人艾伦?马斯克研发的第五种交通方式"超级回路"(也称"超级火车")终于有了重大进展.马斯克在自己的"推特"页面宣称,将于8月12日公布"超级回路"的设计图纸.如果设计成功,人们从旧金山市中心到洛杉矶市中心(约960公里路程)只需30分钟,比坐飞机还要快得多. 据报道,"超

市场推广宝典之:网站推广的五种基本方式

搜索引擎推广是指利用搜索引擎.分类目录等具有在线检索信息功能的网络工具进行网站推广的方法.由于搜索引擎的基本形式可以分为网络蜘蛛型搜索引擎(简称搜索引擎)和基于人工分类目录的搜索引擎(简称分类目录),因此搜索引擎推广的形式也相应地有基于搜索引擎的方法和基于分类目录的方法,前者包括搜索引擎优化.关键词广告.竞价排名.固定排名.基于内容定位的广告等多种形式,而后者则主要是在分类目录合适的类别中进行网站登录.随着搜索引擎形式的进一步发展变化,也出现了其他一些形式的搜索引擎,不过大都是以这两种形式为基础

谈表达式树的缓存(5):引入散列值

到目前为止,我们已经实现了三种缓存方式:首先我们设法构建唯一字符串,但是由于它的代价较高 ,于是我们使用了前缀树进行存储:又由于前缀树在实际操作中所花的时间和空间都有不令人满意之处, 我们又引入了二叉搜索树.那么二叉搜索树又有什么缺点呢?其实前文已经谈到过了,那就是从理论上来 说,它的时间复杂度相对前两个要高,在最坏情况下将会出现O(m * log(n))的时间复杂度--每次比较 两个前缀树需要耗费O(m),共比较O(log(n))次. 很显然,与最理想的时间复杂度O(m)相比,其差距就在于n,

谈表达式树的缓存(1):引言

表达式树(Expression Tree)是.NET 3.5中引入的一种表达方式.表达式树的运用十分广泛,可以直 观地表现出各种"数据",甚至"逻辑"和"行为".再者,表达式树是强类型的,因此合理地使用这个 新特性可以让代码编写变得优雅,方便.一个最简单而常见的例子便是,某些朋友目前就已经喜欢使用表 达式树来代替传统的ByXxx方法,尤其是在访问一些直接支持表达式树的数据源时(例如IEnumerable或 LINQ to SQL).如下: pub

谈表达式树的缓存(4):使用二叉搜索树(AVL树)

上一篇文章中谈到的前缀树实现方式,时间复杂度从理论上来讲已经达到了最优,而空间复杂度理论 上也可以做到较优.但是理论和实际是有差别的,而对于上文前缀树的实现来说,这两方面并不是非常理 想: 时间:前缀树时间复杂度为O(m)的前提是每次哈希表查找操作的时间复杂度为O(1),不过这个O(1)与 一次数值比较相比,从性能上来说还是有比较明显的差距. 空间:前缀树空间复杂度较优的前提是"精细"地实现该数据结构,如果像上文般粗枝大叶,那么会 形成大量稀疏的哈希表,反而造成空间浪费. 因此,虽然事

谈表达式树的缓存(3):使用前缀树

在上一篇文章里我们设法将前缀树构造为一个唯一的字符串,然后使用字符串作为key缓存在字典中. 这个想法非常直接,做法也不困难(在遍历时记录详细信息便可).不过事实上,老赵在思考表达式树的 缓存问题时,这种字符串拼接的方式只存在于脑海当中,而上文的实现是为了这一系列文章的完整性而特 地编写的.这是因为它的缺点较为明显,正如上文所述,字符串拼接操作较为耗时耗资源,且很容易生成 一个长度可观的字符串(并非不能优化,不过实现就复杂了).于是我们现在设法选择另一个解决方案来 处理这个问题. 不过现在,我们

谈表达式树的缓存(2):由表达式树生成字符串

谈到使用表达式树作为key进行缓存,您脑海中最早浮现出来的解决方案是什么?老赵看来,大部分朋 友的第一反应自然就是将作为key的表达式树,使用一定规则生成一个字符串.简而言之,这个生成字符 串的规则F需要能够保证: 在同一个缓存空间内,同样的表达式树能够生成相同的字符串. 在同一个缓存空间内,不同的表达式树生成不同的字符串. 似乎有些罗嗦,朋友们明白便是.其中"在同一个缓存空间"的前提,其实只是放宽了后续要求的条 件.因为在不同的缓存空间内,即使不同的表达式树生成了同样的字符串,它们也

浅谈基于SQL Server分页存储过程五种方法及性能比较

在SQL Server数据库操作中,我们常常会用到存储过程对实现对查询的数据的分页处理,以方便浏览者的浏览. 创建数据库data_Test : create database data_Test GO use data_Test GO create table tb_TestTable --创建表 ( id int identity(1,1) primary key, userName nvarchar(20) not null, userPWD nvarchar(20) not null, u