- 浏览: 137839 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (140)
- ruby on rails (23)
- 随笔 (1)
- 部署 (3)
- ubuntu源 (2)
- linux (28)
- web (9)
- IT (3)
- linux,数据库 (3)
- MOOC (4)
- ubuntu (20)
- win7 (2)
- git (6)
- github (2)
- ubuntu,python (1)
- java,JDK (1)
- ubuntu,qq (1)
- vagrant (3)
- virtualbox (2)
- sass (1)
- centos (3)
- Sublime (1)
- nginx (4)
- passenger (1)
- VPN (0)
- mysql (4)
- VIM (1)
- bbb (1)
- 编码设置 (2)
- mongo (4)
- edx (2)
- ssh (1)
- python (1)
- phpmyadmin (1)
- libreoffice (2)
- docker (4)
- pg (1)
- PostgreSQL (2)
- 系统时间设置 (1)
- ansible (1)
- Sinatra (1)
- 硬盘挂载 (1)
- npm (1)
- smtp (1)
- docker 镜像 (1)
- Memcached (1)
最新评论
作者详细描述了他是如何把一个Ruby项目的运行时间从20秒优化到1.5秒。值得开发者注意的是,在Ruby中调用方法很影响速度,所以作者对代码进行了模块化处理和重复使用。下面是笔者对原文的翻译:
这篇文章主要介绍了我是如何把ruby gem contracts.ruby速度提升10倍的。
contracts.ruby在我项目里用来添加代码合约(code contracts)到Ruby中。看起来差不多是这样的:
1
2
3
4
Contract Num, Num => Num
def add(a, b)
a + b
end
只要add方法被调用,参数和返回值都会被检查。
20秒
本周末,我对该库进行了测试,发现其性能非常糟:
user system total real
testing 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函数,该函数是个空函数,下面是这个函数的整个定义:
1
2
def self.success_callback(data)
end
原来函数调用在Ruby中是非常昂贵的,仅删除这个调用,就节省了8秒钟:
user system total real
testing 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语句,我节约了一些时间,但每次调用这个函数时,我仍然耗费了不必要的时间在仔细检查这个判定树上面:
1
2
3
4
5
if contract.is_a?(Class)
# check arg
elsif contract.is_a?(Hash)
# check arg
...
当定义合约和构建lambda时,对树只做一次检查:
1
2
3
4
if contract.is_a?(Class)
lambda { |arg|
# check arg }
elsif contract.is_a?(Hash)
lambda { |arg|
# check arg }
然后,我将完全绕过逻辑分支,通过将参数传递给预计算的lambda来进行验证,这样就节约了1.2秒时间。
user system total real
testing 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 real
testing 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 real
testing add 0.510000 0.000000 0.510000 ( 0.507554)
testing contracts add 5.090000 0.010000 5.100000 ( 5.099530)
结果证明:
1
args.zip(contracts).each do |arg, contract|
上面的代码要比下面这个慢:
1
args.each_with_index do |arg, i|
要比下面这个更慢:
1
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 real
testing 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秒
最后,我改变了调用重写方法的方式,我先前是使用引用:
1
2
3
4
5
6
7
8
# simplification
old_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方法:
1
2
3
4
5
6
alias_method :"original_#{name}", name:"original_#{name}", name
class_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 real
testing 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 real
testing read 1.200000 1.330000 2.530000 ( 2.521314)
testing contracts read 1.530000 1.370000 2.900000 ( 2.903721)
稍微慢了点!add函数是个例外,我决定不再使用alias_method方法,因为它污染了命名空间,并且这些别名函数会到处出现(文档、IDE的自动完成等)。
其它原因:
在Ruby中调用方法很慢,我喜欢将代码模块化和重复使用,但或许是时候将更多的代码进行内联了。
测试你的代码!删掉一个简单的未使用的方法时间从20秒缩短到了12秒。
其它尝试
1.方法选择器
Ruby 2.0里缺少方法选择器这一特性,否则你还可以这样写:
1
2
3
4
5
6
7
8
9
10
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
endend
endend
这样可能会更加容易编写decorator,并且运行速度也会加快。
2.关键字old
Ruby 2.0里缺乏的另一特性是引用重写方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Foo Foo
def bardef bar
'Hello''Hello'
endend
end end
class Fooclass Foo
def bardef bar
old + ' World'+ ' World'
endend
endend
Foo.new.bar
# => 'Hello World'Foo.new.bar # => 'Hello World'
3.使用redef重新定义方法:
Matz曾说过:
为了消除alias_method_chain,我们引入了Module#prepend,prepend前面加#号,这样就没机会在语言里加入冗余特性。
所以如果redef是冗余特征,也许prepend可以用来写decorator?
4.其它实现
目前为止,这些都已经在YARV做过测试。
Via adit.io
原文:http://www.iteye.com/news/28259
这篇文章主要介绍了我是如何把ruby gem contracts.ruby速度提升10倍的。
contracts.ruby在我项目里用来添加代码合约(code contracts)到Ruby中。看起来差不多是这样的:
1
2
3
4
Contract Num, Num => Num
def add(a, b)
a + b
end
只要add方法被调用,参数和返回值都会被检查。
20秒
本周末,我对该库进行了测试,发现其性能非常糟:
user system total real
testing 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函数,该函数是个空函数,下面是这个函数的整个定义:
1
2
def self.success_callback(data)
end
原来函数调用在Ruby中是非常昂贵的,仅删除这个调用,就节省了8秒钟:
user system total real
testing 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语句,我节约了一些时间,但每次调用这个函数时,我仍然耗费了不必要的时间在仔细检查这个判定树上面:
1
2
3
4
5
if contract.is_a?(Class)
# check arg
elsif contract.is_a?(Hash)
# check arg
...
当定义合约和构建lambda时,对树只做一次检查:
1
2
3
4
if contract.is_a?(Class)
lambda { |arg|
# check arg }
elsif contract.is_a?(Hash)
lambda { |arg|
# check arg }
然后,我将完全绕过逻辑分支,通过将参数传递给预计算的lambda来进行验证,这样就节约了1.2秒时间。
user system total real
testing 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 real
testing 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 real
testing add 0.510000 0.000000 0.510000 ( 0.507554)
testing contracts add 5.090000 0.010000 5.100000 ( 5.099530)
结果证明:
1
args.zip(contracts).each do |arg, contract|
上面的代码要比下面这个慢:
1
args.each_with_index do |arg, i|
要比下面这个更慢:
1
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 real
testing 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秒
最后,我改变了调用重写方法的方式,我先前是使用引用:
1
2
3
4
5
6
7
8
# simplification
old_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方法:
1
2
3
4
5
6
alias_method :"original_#{name}", name:"original_#{name}", name
class_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 real
testing 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 real
testing read 1.200000 1.330000 2.530000 ( 2.521314)
testing contracts read 1.530000 1.370000 2.900000 ( 2.903721)
稍微慢了点!add函数是个例外,我决定不再使用alias_method方法,因为它污染了命名空间,并且这些别名函数会到处出现(文档、IDE的自动完成等)。
其它原因:
在Ruby中调用方法很慢,我喜欢将代码模块化和重复使用,但或许是时候将更多的代码进行内联了。
测试你的代码!删掉一个简单的未使用的方法时间从20秒缩短到了12秒。
其它尝试
1.方法选择器
Ruby 2.0里缺少方法选择器这一特性,否则你还可以这样写:
1
2
3
4
5
6
7
8
9
10
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
endend
endend
这样可能会更加容易编写decorator,并且运行速度也会加快。
2.关键字old
Ruby 2.0里缺乏的另一特性是引用重写方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
class Foo Foo
def bardef bar
'Hello''Hello'
endend
end end
class Fooclass Foo
def bardef bar
old + ' World'+ ' World'
endend
endend
Foo.new.bar
# => 'Hello World'Foo.new.bar # => 'Hello World'
3.使用redef重新定义方法:
Matz曾说过:
为了消除alias_method_chain,我们引入了Module#prepend,prepend前面加#号,这样就没机会在语言里加入冗余特性。
所以如果redef是冗余特征,也许prepend可以用来写decorator?
4.其它实现
目前为止,这些都已经在YARV做过测试。
Via adit.io
原文:http://www.iteye.com/news/28259
发表评论
-
ruby 回复功能
2015-03-24 21:44 645http://www.sitepoint.com/realti ... -
测试驱动开发(TDD)
2015-03-02 17:00 660测试驱动开发的基本过程如下: 1) 明确当前要完成的功能。可 ... -
详解rails命令行
2015-02-02 14:32 906http://blog.58share.com/?cat=7 ... -
Ruby is Big in China
2014-08-12 15:35 549http://stylesror.github.io/#rub ... -
ruby http get post
2014-07-31 11:22 769uri = URI('url') Net::H ... -
使用者認證
2014-07-29 11:18 428转自: http://ihower.tw/rails3/aut ... -
bundle exec rake i18n:check
2014-07-03 11:45 526bundle exec rake i18n:check -
ruby on rails+nginx+passenger+ubuntu
2014-06-06 00:19 862ruby 使用rvm安装 在使用rmv安装的ruby时候必 ... -
用 Ruby on Rails 实现适应各种平台的在线 Office 文档预览
2014-05-28 09:20 965前言 在许多Web应用中 ... -
`gem install nokogiri -v '1.5.6' 报错
2014-05-18 13:07 600libxml2 is missing. please vis ... -
卸载指定版本 bundle
2014-05-05 18:04 2697gem uninstall bundler -v=1.6.2 ... -
使用Vagrant在Windows下部署开发环境
2014-03-06 10:34 699http://blog.smdcn.net/article/1 ... -
安装rails时的一个小注意
2013-12-17 10:08 860在ubuntu上安装ROR环境时候,我总是忘记一件事情,就是设 ... -
GitHub使用指南!(ubuntu)
2013-12-06 00:03 1080<!-- @page { margin: 2cm } P ... -
关于在win7上安装Ruby On Rails 环境的几点注意
2013-12-05 18:31 1482在window下安装Ruby On Rails 环境须知: 1 ... -
Ubuntu安装Ruby On Rails多版本
2013-11-29 23:44 881写Ruby程序的时候,可能 ... -
ruby数组基本操作
2013-11-08 14:52 719#创建数组的几种方法#字面量创建的方法 a = [1,2,3 ... -
Rails2.2新特性:本地化与国际化(2008-12-15 14:21:41)
2013-11-01 11:05 841http://fsjoy.blog.51cto.com/318 ... -
浅析Ruby on Rails部署方案
2013-09-27 15:16 756http://blog.csdn.net/jrckkyy/ar ... -
How to install Ruby on Rails in Ubuntu 12.04 LTS
2013-08-27 11:10 639http://blog.sudobits.com/2012/0 ...
相关推荐
NULL 博文链接:https://l-d.iteye.com/blog/1488038
### Ruby开发的基本流程 #### 一、Ruby开发环境搭建 Ruby是一种动态的、面向对象的解释型编程语言,因其简洁易读的语法和强大的库支持而在Web开发领域中备受青睐。开发Ruby应用的第一步是搭建合适的开发环境。推荐...
元编程能力让Ruby具备了在运行时修改自身的能力,这在构建灵活的框架时非常有用。 接下来,我们进入Rails的世界。Rails的核心理念是“约定优于配置”,这意味着它有一套默认的约定,减少了开发者需要编写配置文件的...
在IT行业中,管理和切换Ruby版本是一项常见的任务,特别是在开发环境中,不同的项目可能依赖于不同版本的Ruby。`RVM`(Ruby Version Manager)是解决这一问题的利器,它允许开发者在多个Ruby版本之间轻松切换。本文...
Ruby-RDoc是Ruby编程语言中一个非常重要的工具,主要用于生成项目的HTML和命令行文档。它使得开发者能够方便地...无论你是初学者还是经验丰富的开发者,熟练掌握RDoc的使用都将极大地提升你的开发效率和项目的质量。
Ruby 1.9 的发布对Ruby社区带来了显著的变化,尤其是其与之前的1.8版本之间的不兼容性。这种不兼容性源于对Ruby语言及其核心库的深入修改,目的是提升语言性能和规范。Ruby 1.9.0的推出并非完全稳定,Matz,即Ruby的...
10. **实践项目**:理论学习后,实践是巩固知识的最佳方式。通过创建小型应用或参与开源项目,可以深入理解Ruby的实际应用。 压缩包中的图像文件可能包含了教学过程中的示例图解,例如"Ruby??????\????-chen.png"和...
总结来说,Ruby-Yard是一款不可或缺的Ruby开发工具,它通过自动化文档生成和强大的定制能力,让代码的文档编写变得简单且高效。无论是个人项目还是团队合作,使用Yard都能有效地提高代码的可读性和团队间的沟通效率...
【Ruby on Rails手順】是关于使用Ruby编程语言和Ruby on Rails框架进行Web开发的一系列步骤。Ruby是一种面向对象的、动态的编程语言,而Ruby on Rails(简称Rails)是基于Ruby构建的一个开源Web应用程序框架,它遵循...
总结来说,`Ruby-rubybuild`是Ruby开发中非常实用的环境管理工具,通过`ruby-build`我们可以方便地编译和安装各种版本的Ruby,配合`rbenv`实现多版本的灵活切换,确保项目的稳定性和兼容性。无论你是初学者还是经验...
在Ruby的世界里,管理不同的Ruby实现(如MRI、JRuby、Rubinius、MagLev和MRuby)是非常重要的,这有助于开发者根据项目需求选择最适合的运行时环境。`ruby-install`就是这样一个工具,它允许用户方便地安装和管理...
总的来说,Ruby-DingTalkBot为Ruby开发者提供了与阿里钉钉自定义机器人互动的便利工具,它降低了对接钉钉API的复杂度,让开发者能更专注于业务逻辑的实现,提升工作效率。对于需要在Ruby项目中集成钉钉通知功能的...
5. **使用带宽优化工具**:如`aria2`,这是一个支持多源、多线程下载的工具,可以提升大文件的下载速度。 描述中的“ruby下载太慢,上传一个”可能意味着提供者已经将Ruby的特定版本(如2.6.6)下载并打包,供他人...
### Ruby 教程《The Book of Ruby》知识...对于想要学习Ruby或进一步提升Ruby技能的开发者来说,这是一本不可多得的好书。通过本书的学习,不仅可以掌握Ruby编程的基本技能,还能深入了解其背后的编程思想和设计理念。
Ruby 3.1.1不仅适用于初学者,也是经验丰富的开发者升级现有项目的理想选择。它提供了更好的性能和新的编程模式,使得开发者能够更高效地编写出可维护和可扩展的代码。通过学习和掌握Ruby 3.1.1的新特性,你将能够...
它的核心目标是让Ruby程序员能够利用TensorFlow的强大功能,同时保持Ruby语言的优雅和简洁。这个项目在设计时特别注重与TensorFlow的相似性,以便于熟悉TensorFlow的开发者能快速上手。 **1. 纯Ruby实现** Ruby-...
这些书籍可能包括教程、指南、参考手册以及实战案例分析,旨在帮助读者掌握Ruby的基础知识,理解其核心概念,如类、模块、块、元编程等,并进一步提升在实际项目中的应用能力。 在标签中,我们只看到了"ruby",这...
首先,让我们来看看Ruby 1.8.6在Windows上的安装。文件名"ruby186-26 (1).exe"表明这是一个针对Ruby 1.8.6的Windows安装程序,版本号为26。安装过程通常包括以下步骤: 1. 下载:访问官方网站或第三方资源下载Ruby ...
Ruby则有RubyMine作为IDE,以及RVM(Ruby Version Manager)来管理不同版本的Ruby,Bundler用于管理项目依赖,而Rake作为构建工具。 《Pragmatic Bookshelf.From.Java.to.Ruby.Jun.2006.eBook-BBL》这个文件很可能...