说三道四技术文摘-感悟人生的经典句子
说三道四 > 文档快照

如何让Ruby项目速度提升10倍

HTML文档下载 WORD文档下载 PDF文档下载
如何把一个Ruby项目的运行时间从20秒优化到1.5秒?值得开发者注意的是,在Ruby中调用方法很影响速度,所以作者对代码进行了模块化处理和重复使用。

作者详细描述了他是如何把一个Ruby项目的运行时间从20秒优化到1.5秒。值得开发者注意的是,在Ruby中调用方法很影响速度,所以作者对代码进行了模块化处理和重复使用。下面是笔者对原文的翻译:

这篇文章主要介绍了我是如何把ruby gem contracts.ruby速度提升10倍的。

contracts.ruby在我项目里用来添加代码合约(code contracts)到Ruby中。看起来差不多是这样的:

Contract Num, Num => Numdef add(a, b)  a + bend
只要add方法被调用,参数和返回值都会被检查。

20秒

本周末,我对该库进行了测试,发现其性能非常糟:

                                   user     system      total        realtesting add                      0.510000   0.000000   0.510000 (  0.509791)testing contracts add           20.630000   0.040000  20.670000 ( 20.726758)
这是在随机输入下,运行1000次以后的结果。

所以,当给一个函数加入合约功能后,运行速度明显下降(约40倍这样),对此,我进行了深入的研究。

8秒

我取得了较大的进展,当传递合约时,我调用success_callback函数,该函数是个空函数,下面是这个函数的整个定义:

def self.success_callback(data)end  

原来函数调用在Ruby中是非常昂贵的,仅删除这个调用,就节省了8秒钟:

                                  user     system      total        realtesting add                     0.520000   0.000000   0.520000 (  0.517302)testing contracts add           12.120000   0.010000  12.130000 ( 12.140564)
删除其它一些附件函数的调用,时间花费开始从9.84-> 9.59-> 8.01秒,该库的速度马上提升到以前的两倍了。

现在,事情变的有点复杂了。

5.93秒

这里有许多年种定义一个合约的方式:匿名(lambdas)、类 (classes)、简单旧数据(plain ol’ values)等。 我有个很长的case语句,用来检测合约的类型。在此合约类型基础之上,我可以做不同的事情。通过把它改为if语句,我节约了一些时间,但每次调用这个函数时,我仍然耗费了不必要的时间在仔细检查这个判定树上面:

if contract.is_a?(Class)  # check argelsif contract.is_a?(Hash)  # check arg...

当定义合约和构建lambda时,对树只做一次检查:

if contract.is_a?(Class)  lambda { |arg| # check arg }elsif contract.is_a?(Hash)  lambda { |arg| # check arg }

然后,我将完全绕过逻辑分支,通过将参数传递给预计算的lambda来进行验证,这样就节约了1.2秒时间。

                                  user     system      total        realtesting add                      0.510000   0.000000   0.510000 (  0.516848)testing contracts add            6.780000   0.000000   6.780000 (  6.785446)
预计算一些其它的If语句,差不多又节省了1秒时间:

                                   user     system      total        realtesting add                      0.510000   0.000000   0.510000 (  0.516527)testing contracts add            5.930000   0.000000   5.930000 (  5.933225)
5.09秒

将.zip转换为.times又为我节省了1秒时间:

                                   user     system      total        realtesting add                      0.510000   0.000000   0.510000 (  0.507554)testing contracts add            5.090000   0.010000   5.100000 (  5.099530)
结果证明:

args.zip(contracts).each do |arg, contract|
上面的代码要比下面这个慢:

args.each_with_index do |arg, i|
要比下面这个更慢:

args.size.times do |i|
.zip要花费不必要的时间复制和创建新的数组。而我认为,.each_with_index之所以慢,是因为它受制于背后的.each,所以它涉及到两个限制而不是一个。

4.23秒

下面再看些细节的东西,contracts库在工作时,它会为每一个方法添加class_eval(class_eval要比define_method快)的新方法,这个新方法里有一个对老方法的引用,当调用新方法时,它会检查参数,然后根据参数调用老方法,然后再检查返回值,并且返回值。所有这些都会调用Contract class的check_args和check_result两个方法。我取消了这两个方法的调用,并且对新方法进行正确检查,结果又节省了0.9秒:

                                   user     system        total        realtesting                          0.530000   0.000000   0.530000 (0.523503)testing contracts add            4.230000   0.000000   4.230000 (  4.244071)

2.94秒

在上面,我已经解释了如何基于Contract类型创建lambda,然后使用这些来检验参数。现在,我换了种方法,用生成代码来替代,当我使用class_eval创建新方法时,它就会从eval中获得结果。一个可怕的漏洞,但它避免了一大堆方法调用,并且节省了1.25秒:

                            user    system     total   real    testing add            0.520000 0.000000 0.520000 ( 0.519425)    testing contracts add  2.940000 0.000000 2.940000 ( 2.942372)

1.57秒

最后,我改变了调用重写方法的方式,我先前是使用引用:

# simplificationold_method = method(name)= method(name)class_eval %{%{    def #{name}(*args)def #{name}(*args)        old_method.bind(self).call(*args).bind(self).call(*args)    endend}}
我进行了修改,并使用alias_method方法:

alias_method :"original_#{name}", name:"original_#{name}", nameclass_eval %{%{    def #{name}(*args)def #{name}(*args)        self.send(:"original_#{name}", *args)self.send(:"original_#{name}", *args)      endend}}

惊喜,又节省了1.4秒。我不知道为什么aliaa_method会如此地快,我猜是因为它跳过了一个方法的调用和绑定到.bindbind。

                                 user     system      total        realtesting add                      0.520000   0.000000   0.520000 (  0.518431)testing contracts add            1.570000   0.000000   1.570000 (  1.568863)

结果

我们成功的将时间从20秒优化到1.5秒,我不认为还有比这更好的结果的了。我所编写的 这个测试脚本表明,一个被封装过的add方法要比常规的add方法慢3倍,所以这些数字已经足够好了。

想要验证上面的结论很简单,大量的时间花在调用方法上是只慢3倍的原因,这里有个更现实的例子:一个函数读一个文件100000次:

                                  user     system      total        realtesting read                     1.200000   1.330000   2.530000 (  2.521314)testing contracts read           1.530000   1.370000   2.900000 (  2.903721)

稍微慢了点!add函数是个例外,我决定不再使用alias_method方法,因为它污染了命名空间,并且这些别名函数会到处出现(文档、IDE的自动完成等)。

其它原因:

  1. 在Ruby中调用方法很慢,我喜欢将代码模块化和重复使用,但或许是时候将更多的代码进行内联了。
  2. 测试你的代码!删掉一个简单的未使用的方法时间从20秒缩短到了12秒。

其它尝试

1.方法选择器

Ruby 2.0里缺少方法选择器这一特性,否则你还可以这样写:

class Foo Foo  def bar:beforedef bar:before    # will always run before bar, when bar is called# will always run before bar, when bar is called  endend  def bar:afterdef bar:after    # will always run after bar, when bar is called# will always run after bar, when bar is called    # may or may not be able to access and/or change bar's return value# may or may not be able to access and/or change bar's return value  endendendend
这样可能会更加容易编写decorator,并且运行速度也会加快。

2.关键字old

Ruby 2.0里缺乏的另一特性是引用重写方法:

class Foo Foo  def bardef bar    'Hello''Hello'  endendend end class Fooclass Foo  def bardef bar    old + ' World'+ ' World'  endendendendFoo.new.bar # => 'Hello World'Foo.new.bar # => 'Hello World'
3.使用redef重新定义方法:

Matz曾说过:

为了消除alias_method_chain,我们引入了Module#prepend,prepend前面加#号,这样就没机会在语言里加入冗余特性。

所以如果redef是冗余特征,也许prepend可以用来写decorator?

4.其它实现

目前为止,这些都已经在YARV做过测试。

来自:adit.io

VB建立快捷方式-VB资料 VB建立文件关联 -VB资料 将RichTextBox 之中被选取的内容复制到剪贴簿-VB资料 将我的程序的文本直接送到WORD中(不用粘贴)-VB资料 开启文件属性窗口 -VB资料 快速读取 TextBox 第 N 行的资料 -VB资料 快速选择里List全部项目-VB资料 VB利用 App.Path 读取「应用程序所在之目录」 VB利用 EM_LINESCROLL 信息控制 TextBox 的卷动。 命名的技巧 -VB资料 目录所占的字节数-VB资料 VB判断文件是否在IE的缓存中 屏蔽文本框的右键菜单-VB资料 VB取得长文件名 VB取得临时文件名 VB取得某个目录底下所有文件大小总和 VB取得文件的扩展名 VB取得文件内容 确定 TextBox 有几行-VB资料 确定是 WINDOWS 的可执行文件-VB资料 让打印机只打印一行-VB资料 VB如何把批处理文件转换成EXE文件? VB如何调用 Office VB如何将文件删除到回收站 VB如何快速移动文件? VB如何让文本框输入完后,直接跳入下一行? VB如何使用vb取得一个文件的控制权 VB如何使用文件复制对话框? VB如何使用资源文件 VB如何用Dir()函数来列出C下所有TMP文件并且用文本框输出 VB如何在 VB 中使用 Winzip 来压缩文件? 显示图片 数据库搜索的一个怪问题 共享上网问题!急~!!!! 句柄和指针有什么区别吗? 大虾快进来,帮帮兄弟们!!!再现等待!高分!! 请问C++Builder的程序中能不能使用*.mdb类型的数据库? 帮帮忙!!个位大哥 请问用java编程能实现数据采集的程序并进行传输(即c/s模式)吗? 请问什么命令找开一个窗体的? 请问:如何从文件中获取icon并得到HBITMAP句柄? 如何在客户端打开一个word文件 MVP是个什么概念???? linux启动不了,Bringup loopback interface时出错,请问是什么原因? 帮帮忙!! 我海军一艘潜艇失事 70名官兵不幸全部遇难!! 明天朋友结婚,征集整人的法子 是谁删除了我贴?? 而且没有跟我说一声!!! 白天工作,晚上溜狗,爽呀!比大暑天喝了冰镇雪碧还爽! 我的jsp服务器是iis+TOMCAT+win2000pro 不知怎的每次用不了多久就显示出 网页无法显示 目前访问该 Web 站点的用户过多,我只是自已用怎么 终于有星了,高兴,散分(第一次散分,请斑竹手下留情) 我的刚装的2000进入启动界面时,进度条读到2/3时就蓝屏了! 请问在运行时向ActiveX控件中添加ActiveX控件的方法是什么? 高手请进:如何在WinNT以上系统进行BIOS读取! 毕业设计的问题,请各位大哥帮小弟一个忙 备份与恢复 数据备份与恢复 我用win2k,下载了php4.3.1,extension_dir是c:\php\ext 可是里面没有php_gd.dll,怎么办,到哪里可以down一个补上? 帮我一下:解决中文问题 新手问题:怎么在用ATL COM WIZARD已生成的dll加入MFC 请问如何删除系统? warton给大家散分来了! 求助,请问哪儿提供的域名空间最好? 新手上路,VB中用ODBC连接ACCESS 关于ORACLE中(多)表的备份和恢复问题 MM``狂给分^*^ 找人一同研究JUnit对于数据库的测试架构 怎样在程序中动态生成数据库(内存库)?求救高手 请问,怎样用本机作web服务器? 这样的夜晚 想起一些故人.... 各位的jbuilder是用什么命令运行的 刚学者求问 python+CGI 如何获得客户端IP Win2000上, 对话框没能弹出到桌面顶层 在静态上下文中不能引用非静态变量? 有没有高手可以举例讲解一下self的各种用法? 一个很困惑的问题:文字处理软件中,字体大小改变时,字符的高度,宽度怎么变化? 移动硬盘在重装系统时忘记拔下来。结果给误删了,求助!没有格式化的! 怎样创建自己的 Cursor ? 请问一下 这个是什么意思 public Properties() { this(null)} 在SQL7中日志满了,如何清除日志. ?????????? 一个编程习惯上的问题 求一UFO电影,内容是这样的,前面忘了,女的是外星人,最后男的在田里造了一个麦田圈,然后飞碟就来了,把女的接走了.字打打完了,补充也是内容. 阿姨在超市里买了4千克苹果和5千克西瓜,共付出24.7元.已知西瓜的单价是苹果的二分之一 关计算于圆台的表面积问题圆 台的表面积=上底面+下底面+侧面上底面的半径为 下底面半径为 r那么上底面的面积=pai * 的2次幂下底面的面积= pai * r 的2次幂上底面的周长=2 * pai * 下底面 如图,木椅木床等木家具上的联接木条的金属片叫做什么? Please go to there.Are you know the boy's name?Please take the CDs Miss Brown.这些句子哪错了 哪个品牌的有机皂是纯有机的?什么品牌的有机皂是可以给宝宝用的? 160万用英语怎么说1600000 用英语该怎么说呢? lf you get to there,please let me know 哪里错了 有机皂什么品牌的好?WOWMOM品牌的有机皂是不是健康的品牌? 随便吃些什么  用英语怎么说? 有机皂是什么组成的?想知道是不是所有的有机皂都是纯有机物组成的 有机皂哪个品牌的好?什么品牌的有机皂适合宝宝的皮肤? very后面可不可以加ly(形容词)? 肥皂里的油怎么提炼?3Q 请给出答案与理由 .As you can see,the number of our roads ——rising these days.A,was keeping B,keep C,keeps D,were keeping请问,B和C有什么区别?英语很笨呀! cad,已知四边形四条边长度和面积,怎么画出四边形 肥皂为什么会气泡?3Q ____of students in our school is rising.A.Many B.A lot C.A number D.The number 四边形四条边长度一样 面积一样吗?四边形是不规则的或者平形的.四条边长度确定,面积确定吗?四条边的长度确认,就是四条边的长度是确定的,角度变化.例如四边形四条边150,86,87,153这个四边 质量为0.025kg的质点,受力F=ti的作用,式中t为时间,t=0时该质点以v=2j的速度通过坐标原点,则该质点任意时刻的位置矢量是~~ 如图是某商场一楼与二楼之间的手扶电梯示意图.其中AB,CD分别表示一楼,二楼地面的水平线,∠ABC=150°BC的长是8m,则乘电梯从点B到点C上升的高度h是(过程) I do not like these jeans.Could you show me another one?错在哪里 已知四条边能不能算四边形面积 (1/2)物理:质量为0.25kg的质点,受力F=ti(SI)的作用,当t=0时,该质点以v=2jm/s的速度通过原点,则该...(1/2)物理:质量为0.25kg的质点,受力F=ti(SI)的作用,当t=0时,该质点以v=2jm/s的速度通过原点,则该质 I do not like these trousers. Can you show me( )A、one anotherB、another oneC、any other oneD、any others请各位把原因也写上,谢谢 麦田圈到底是外星人所为还是人为炒作? 质量m=2kg的质点,在f=7i-6j作用下,从坐标原点静止开始运动,则前2s内f力做功为?,第2s末的功率为?大学物理积分 very一词的用法 为什么直接在名词前我看到一个句子 the very house 这个very在译文中没翻译出来,在这有什么作用 麦田圈的存在是人为还是外星人所为? 如何计算圆台体的体积… 求有关古诗词的题目 麦田圈是怎麽一回事,这究竟是不是外星人所为还是磁场呢? 圆台的表面积怎么算阿?上面小;下面大;的圆管的表面积的计算公式! I'm busy these days.I have____to do .A.so many B.too many C.too much D.very much 请问:不规则四边形的四个边的长度已知,不知道各个角的角度,怎么样求该四边形的面积? 为什么有专门给孩子用的有机皂?想知道为什么有机皂是专门给孩子用 一道简单英语题求可填的词和分析句子结构(第3题,第4题) 不规则四边形面积但四条边的长度知道,角度不知, 为什么孩子特别喜欢有机皂呢? I'd give you most of it because you have been so good to me all these 将物重为20N的物体静止在粗糙的水平地面上,物体所受的摩擦力为 ▲ N;当用15N的水平推力恰能使将物重为20N的物体静止在粗糙的水平地面上,物体所受的摩擦力为 ▲ N;当用15N的水平推力恰 你们的孩子喜欢有机皂还是无机皂?大家的孩子比较喜欢有机皂一点还是无机皂一点 1.翻译:你的朋友比你要外向的多吗?Is your friend ___ ___ ___than you2.我的父母更严肃 my parents are ___ ___ 1一个圆柱的体积是1.8立方厘米,把它削成一个最大的圆锥体,应削去()立方厘米2一个圆柱的体积是234立方厘米,与它等低等高的圆锥体积是()立方厘米3一个圆柱和一个圆锥,它们的底面积和 圆台的体积,表面积(简单点)加上意思 妈妈买了二分之一千克苹果,也就是买了50%千克苹果. 我也十分想要《经典常考公文写作与处理试题集萃》,大哥可不可以给我也发一下, 圆台的表面积和体积的程序圆台的上底面半径r,下底面半径R,高h.求圆台的表面积和体积,写出程序. Bon Jovi的Have a nice day.MP3格式,放空间里用的. 如图所示,在梯形ABCD中,AB⊥AC,AB=CD,∠B=60°,AD=7,BC=14,求梯形ABCD的周长 请问怎么来选择有机皂的呢?想知道一般来说有机皂是怎么去选择的 有个节目叫:比手划脚,就是:一个人在做动作,然后让别人来猜出这是那个成语或是词语.寻找可以用来做节目的词语 电场强度为E方向与x轴正方向成135°现有一个电荷量为q质量为m的重力不计的负离子从原点O以初速度v0射入电场,v0与x轴正方向成45°求离子通过x轴的坐标及在该处的速率 怎么挑选有机皂?市面上的有机皂品牌琳琅满目什么品牌的好? 这个诗词该定个什么题目,千里名炀 万里齐聚 望店奇观上有客房 下设美食 中夹美容另俱桑拿 层次分明 装饰豪华可与碧海为一拼 须常来 缓精神之压 品美食之肴 此地如此之雅引无数高朋竟 bamboo有没有复数 不知道洁肤皂应该如何来选择呢?因为之前都没有选择过洁肤皂,不知道应该怎么来选择好一点? I have a tomato怎么变单数 落回原点速度是为什么是-vo 不倒翁为什么不会倒? 安倍窜访土耳其 搅局土中军购“抗衡中国说”凸现安倍“同盟困境”奥巴马医改网站承包商高管是第一夫人同斯诺登曝美国一月内监听西班牙6000英国发生强烈暴风雨天气已致4死 逾6“手机门”困扰德美关系法国总统称新加坡是“战略伙伴”德黑兰“敏感时刻”撤下街头反美海报 波兰前总理在华沙去世 被赞波兰史上最美国政府“秋后”问责 摩根大通掏钱“日抗议中国海警船巡航钓鱼岛 遭驻日公美议员称监听欧洲是“救命”中国将带动全球天然气需求增长西北非国家着手保护海鱼资源落实“2+7合作框架”要看三个着力点印度拨款12亿加固中印边界设施 新设美4囚犯上演浴室版“越狱” 2人被捕日抗议中国4海警船巡航钓鱼岛 遭驻日布伦特油价28日大幅走高鸟叔诉苦称进军海外压力大:两首歌走了日拟购百余无人机监视钓鱼岛掘金大数据 各种应用逐渐渗透各行各业穗农田保护:补贴资金实行差别化分类 促孵化器3年\"倍增\"计划审议 每新疆喀什打造中巴经济走廊廊桥已下先手市民灭蚊不积极 陈建华:本周灭蚊日增南水北调供水近700万方 解困平顶山日本男足新规:禁谈巴西世界杯 集体吃200名特殊教育教师获得“特教园丁奖重庆巫溪古路中学1700余名被洪水围中国首次拍摄到野生东北虎家族式活动影中土两国加强经贸交流共建丝绸之路经济深圳率先全国制定商事登记权责清单阿拉斯加小城开支数千美元 只为市长消俄媒摄影记者被证实殉职 普京向其家人浙江温岭:小偷将253万人民币扔洒一漫谈桥牌与外交:二者有诸多共性 都是深圳率先全国制定商事登记权责清单2014年越南对中国出口总额有望达1北京男女篮不再与赞助商续约 金隅时代小偷将装200多万元保险柜扔水中 失9月3日:央企薪酬改革,“官商通吃”
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘