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

如何让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游戏写作技巧(2)网络篇 VB中播放WAV文件 VB中播放三维动画编程一例 Visual Basic 5.0多媒体创作技巧 Visual Basic 5.0中实现视频画中画 Visual Basic 中制作流水灯 Visual Basic动画编程技术 Visual Basic环境下Video/ Audio压缩数据流播放技术 Visual Basic设计多媒体应用程序 Visual Basic影音控制 Web动画图像分解在VB动画设计中的应用 播放 .flc 动画 -VB资料 播放 AVI -VB资料 播放MP3文件 -VB资料 播放背景音乐 -VB资料 播放声音文件 -VB资料 播放资源文件文件中的声音-VB资料 不用ActiveX控件也能播放声音文件-VB资料 VB创建动态图标 电影结束画面的卷动效果-VB资料 二进制操作截取VCD片断 -VB资料 非常VB测声卡 关闭屏幕保护功能,改变屏保等待时间 -VB资料 VB获取桌面窗口图象 红色警戒&终级动员令-VB资料 检查是否安装声卡 -VB资料 将 .flc 动画档放在某一个 hWnd 之中播放 -VB资料 截取屏幕图像-VB资料 开机,自动更换墙纸 -VB资料 控制PC喇叭 -VB资料 利用VB测声卡 事务日至到底有什么用呀? 两个线程共同调用一个静态函数,如何在一个线程调用的时候,阻塞另一线程的调用? 定制控件可不可以使用用户控件作为子控件? 我的困惑,一个值得深思的问题 存储过程,我再加一个参数@sessionuser char(8),在调用该存储过程时,我如何把参数传给sp_querycard? 如何实现页面剪切? hot, hot, hot! 有关Java Swing的编程问题 两个线程共同调用一个静态函数,如何在一个线程调用的时候,阻塞另一线程的调用? 如何将程序做成服务的形势,如何调用,有没有这样的实例啊 如何在2000下進行命令行撥號 打印问题请教!!急!!! 來取分關於weblogic7 俺也灌一次水: 45/59 = ? 一个关于处理WORD文档的问题? 喜欢许巍 的请进来 不通过DataSource 如何给DataGrid 的DataGridBoolColumn列赋值?(在线等待中...) 谁有MicroStation BASIC开发语言参考中文版 请问:如何通过JAVA程序访问Web页面? Win2000的最小安装需要多少容量?快抢分啦! 如何将一个table从原来的tablespace移到另一个tablespace 计算一个月有几周,星期一为第一天,相关语句 骆驼您好,刚才没看到你的回贴的时候,那个贴我已经结贴了,我还有问题请教,请进,多谢。 请问哪里有VTune Profiler下载?据说它能分析VC代码中各函数所占用的开销 请问如何在ftp上收发xml文件。。。请具体点告诉我或贴源码或贴相关文章地址 文件解析 VC的OPENGL编程显示提示框时,显示内容是以前绘图的,不是最新绘图的,怎么办? 请问为什么create table bbb as select * from aaa ,aaa上面的缺省值还有索引都掉了呢? 如何在数据库里存文本文件? 用什么工具可以查看内存是否有泄露, Win2000上是什么, Win98上是什么 为什么用oledbadapter不能更新数据库阿? 软件陷阱问题 请问哪里有VTune Profiler下载?据说它能分析VC代码中各函数所占用的开销 在weblogic7.0上面jsp文件运行问题 请教关于打印遇到的问题 谁能个--报表例子(水晶报表。vb.net)--报表的数据源要是动态生成的。 利用WMI管理WINDOWS 请教:如何在chm帮助文件中执行应用程序。谢谢 Weblogic+Mysql的Jsp/Java程序的数据库该怎样连接?? 在java代码中引用php的变量?请问如何写? tclientdataset问题 急!!!!VFP触发器取值问题 怎么样才能卸载REDHAT8.0下的XMMS,而装7.3下的XMMS? 请教一个有关于对话框上控件重画的问题!谢谢! 有谁知道上海高程查分方法?急!!! 请教OleDbDataReader的用法 请教一个有关于对话框上控件重画的问题!谢谢! 请教一个简单问题,显示图形滞后的问题,有人回答过,但不正确。谢谢 大家有没有见过IIS不支持数据库的? 1700--2100 之间的手机,请大家帮我推荐一下,在NOKIA,MOTOROLA,厦新,波导……中选择!! 请问谁有?divx的资料?能提供下载也行! 请问如何把从数据库中读出来的数据显示在在线编辑器里? 上海:樱花树下旗袍秀 风情pk清纯各彭丽媛参观俄罗斯孤儿学校:代表母亲来“炒房客”撤离 温州楼市“高台跳水”上海首创在废弃深坑建五星级酒店长沙“落井”女孩仍无音讯 居民称事发四川白富美开20多万越野车卖凉面 自图片纪实:广东一个吸毒者生命的最后1四川中学女生扮甄嬛 高呼“要嫁就嫁清父亲为10岁儿子献骨髓 嘱咐医生“多媒体调查代孕机构:生男孩120万 男上海男子杀害两亲属 又砍伤多名小学生高一男生寝室照走红网络 打造成“小资台湾宜兰罕见冷泉寻找代言人 可获奖金温州市委秘书长吴开锋被曝有婚外情 2钓鱼台国宾馆选拔服务人员 阵势犹如选香港举行“枕头大战”场面壮观习近平在海南会见多国总统 彭丽媛出席8岁男孩娶61岁老妇 南非男孩为已去窦靖童on the beach素颜自丽江大火铺天盖地 彩云之南遍地狼藉(郑州一超市女售货员被劫持 劫匪被控制【观点】九个企业家眼中的2014 全球最顶级的餐厅盘子出自谁手?券商影响力哪家强?海通策略会带动金融设计师必看:土豪最爱的时装设计师券商互联网金融驶进深水区如此“颜值”与“内涵”并存的微信号,赏色丨麦当娜《Interview M荐号 | 从明天起 ,一大波微信领袖狼真的来了 明年中国每个行业都将洗牌从男神到大叔,【朱孝天】经历了什么?2014证券行业互联网化研究报告(3牛人的28平方时尚风梦想之家!榜单丨国家评论协会奖颁奖;奥斯卡纪录明明可以靠实力,却偏偏不走心 | 比《太平轮(上)》对不起,跑偏了合婚,让你婚姻状况早知道!【守护女性尊严】印度女人示范,为什么印度终于免签啦!这里才是遗落在人间的【注意!】十大致命错误,面膜越敷越老从命理看你的八字有没有财,有偏财还是黄兴文:关掉公司,趁还来得及!(房地
备案号:鲁ICP备13029499号-2 说三道四 www.s3d4.cn 说三道四技术文摘