瞎扯淡 Rails 有那么慢吗

hooopo · 2012年11月24日 · 最后由 SharpX 回复于 2013年04月20日 · 5460 次阅读

开发模式?这么好看的图白画了

我以前有个奇葩同事做性能测试。。。测出的结果是 7 request per second,然后发现他开的是 dev 模式,所以我搞不懂 2.4 是如何做到的?

两年前的文章都让你挖出来了...

#3 楼 @fresh_fish 这篇里提到的:http://www.aqee.net/php-needs-to-die-what-will-replace-it/

射手官网测试比较了下 php 和 ror 的性能,结果 php 赢得很彻底阿。

我就搜到了这篇文章,赢得很彻底。

测试了一下,用 passenger start -p 4000 -e production --max-pool-size 100 --min-instances 10 启动

ab -c 10 -n 100 http://localhost:4000/

按原文的代码

Server Software:        nginx/1.2.3
Server Hostname:        localhost
Server Port:            4000

Document Path:          /
Document Length:        512451 bytes

Concurrency Level:      10
Time taken for tests:   21.563 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      51319400 bytes
HTML transferred:       51245100 bytes
Requests per second:    4.64 [#/sec] (mean)
Time per request:       2156.277 [ms] (mean)
Time per request:       215.628 [ms] (mean, across all concurrent requests)
Transfer rate:          2324.22 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       0
Processing:  1273 2107 311.6   2128    2994
Waiting:     1273 2106 311.6   2128    2994
Total:       1273 2107 311.7   2128    2994

Percentage of the requests served within a certain time (ms)
  50%   2128
  66%   2290
  75%   2336
  80%   2362
  90%   2460
  95%   2544
  98%   2747
  99%   2994
 100%   2994 (longest request)

把 view 里面的循环移到 controller

class MainController < ApplicationController
  def index
    sleep(0.2)
    @content = 128000.times.map{rand(8999)+1000}.join
  end
end
<%= @content %>

结果有了不少改善

Server Software:        nginx/1.2.3
Server Hostname:        localhost
Server Port:            4000

Document Path:          /
Document Length:        512451 bytes

Concurrency Level:      10
Time taken for tests:   4.817 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      51319400 bytes
HTML transferred:       51245100 bytes
Requests per second:    20.76 [#/sec] (mean)
Time per request:       481.690 [ms] (mean)
Time per request:       48.169 [ms] (mean, across all concurrent requests)
Transfer rate:          10404.33 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       0
Processing:   363  466  97.4    442     768
Waiting:      362  465  97.2    442     768
Total:        363  466  97.4    442     768

Percentage of the requests served within a certain time (ms)
  50%    442
  66%    480
  75%    502
  80%    513
  90%    593
  95%    735
  98%    767
  99%    768
 100%    768 (longest request)

结论是

  1. View 里的循环多花了很多时间
  2. 他的电脑没我的快

循环放在 View 里面的日志

Started GET "/" for 127.0.0.1 at 2012-11-24 22:20:45 +0800
Processing by MainController#index as */*
  Rendered main/index.html.erb within layouts/application (1670.9ms)
Completed 200 OK in 1873ms (Views: 1672.4ms | ActiveRecord: 0.0ms)

移出后的日志

Started GET "/" for 127.0.0.1 at 2012-11-24 22:23:24 +0800
Processing by MainController#index as */*
  Rendered main/index.html.erb within layouts/application (1.6ms)
Completed 200 OK in 410ms (Views: 3.1ms | ActiveRecord: 0.0ms)

可见效率是有差异的。不过我觉得一般不会用到 128000 这么大的循环,View 按正常写就行了。

顺便,我把模板从 erb 换到 slim,发现性能有提升

- 128000.times do
  = rand(8999)+1000
Server Software:        nginx/1.2.3
Server Hostname:        localhost
Server Port:            4000

Document Path:          /
Document Length:        512450 bytes

Concurrency Level:      10
Time taken for tests:   6.919 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      51319300 bytes
HTML transferred:       51245000 bytes
Requests per second:    14.45 [#/sec] (mean)
Time per request:       691.885 [ms] (mean)
Time per request:       69.188 [ms] (mean, across all concurrent requests)
Transfer rate:          7243.47 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       0
Processing:   454  672 103.5    677     911
Waiting:      454  670 103.8    673     910
Total:        454  672 103.5    677     911

Percentage of the requests served within a certain time (ms)
  50%    677
  66%    709
  75%    753
  80%    778
  90%    808
  95%    864
  98%    880
  99%    911
 100%    911 (longest request)
Started GET "/" for 127.0.0.1 at 2012-11-24 22:26:31 +0800
Processing by MainController#index as */*
  Rendered main/index.html.slim within layouts/application (703.5ms)
Completed 200 OK in 905ms (Views: 704.9ms | ActiveRecord: 0.0ms)
  Rendered main/index.html.slim within layouts/application (482.9ms)
Completed 200 OK in 685ms (Views: 484.5ms | ActiveRecord: 0.0ms)

#7 楼 @Rei 是哇 这种测试和正常的应用程序是没有可比性的。 这个测试实际上是比生成随机数的效率和模板里循环的效率了...

为什么我突然 de ja vu 的觉得虎炮以前吐槽过这个帖子...

因为 view 里的 ruby 代码是通过 eval 执行的 所以会极慢

#11 楼 @jasl 不对,view 里的 ruby 代码可以是编译过的,erb 和 slim 甚至可以比某些版本的 velocity 更快..

@luikore 细节没有去关注过,但是@xdite在 rubyconfchina 上提过这点 就现在的 erb 来说 运行期会被编译?

#13 楼 @jasl erb 在编译时通过 eval 把整个模板转换成一个方法 (方法名字一般是 render, 可以自己改的), 后面渲染就只是在调用这个方法而已。

@luikore 也就是说 第一次运行某个页面的时候会较慢 以后直接使用编译好的 view 这也就呵 jsp -》servlet 的模式就类似了 我理解的对吧?

#15 楼 @jasl 是的,我忘了具体是启动编译还是首次访问编译了...

#16 楼 @luikore https://speakerdeck.com/m_seki/rails-is-a-follower 这个 slide 里第三页有提到《The ERB Book》但是我搜不到这本书..

@luikore 推测是首次访问编译,这个可以看 log 的 view 的执行时间推测 但我有疑问 xdite 提到过代码放 erb 里的执行效率不如放 helper 里 并建议让 view 尽可能的轻 复杂的判断可以放到 helper 里 如果 erb 会被转换成一个方法的话 那这样的看法就是说不通的了啊

#18 楼 @jasl #16 楼 @luikore Rails3 使用的是 erubis ,和 ERB 不知到有没有什么差别

#19 楼 @hooopo 现在已经没依赖 erubis 了,效率几乎一样了...

@hooopo http://www.kuwata-lab.com/erubis/users-guide.05.html#rails-preprocessing 处理模板的方式 guide 里似乎没有涉及 不过这节似乎有些用处 If you use '[%= %]' instead of '<%= %>', preprocessor evaluate it only once when template is loaded.

@hooopo 虽然这章是面向 rails2 的 但是 [%= %] 语法 应该在 3 中可用

#1 楼 @luikore 怎么看出来这是 development 模式的?根据测试结果猜的?

根据我自己项目经验,一般的请求处理应该在 50ms 以内,这方面有真实场景的同学不妨说说自己的数字

#23 楼 @fsword 乱猜的... 不过两年前也有可能真的是产品模式...

不知道 python 做会怎么样

#25 楼 @luikore 哦,原来如此,不过两年前也可以做到很好的性能,仅利用“普及型”知识

我看,没有更好的,只有更适用的,rails 也可以做到 php 那样,只要你写的够好,如果没有到非得去优化性能或者通过系统管理员架构调优去节约成本,做其他得优化都是空谈

#2 楼 @hooopo rails 是慢一点,可是没慢到那种地步,评测的作者特别是国内的评测作者大部分都是有倾向性的,拼了命地维护自己会的东西,JE 的肉饼做了个 Rails 和.Net 的产品级性能对比,对比的结果是 RoR 秒一切,所以说这种评测没什么实际意义

我个人觉得最好的评测是 debian 小组里的一个人做的数学计算效率测试,结果是 Ruby 和 Python 在那种测试下性能没什么区别,虽然 debian 最後选择的还是 perl 和 python,但是这种中立的态度非常难得

#29 楼 @Iacob 我自己做的大部分测试中,ruby 都比 python 差一些,但是差距并不大,一般是 5% 上下

#17 楼 @hooopo 那本《The ERB Book》看起来像是 P 上去的...

PHP 比 ruby 运行快,从项目来看,好像是这样。 很多细节就决定了的。就像写完了 ruby,再来写 php 要想吐一样,这个也是其语言本身很多细节就决定了的。没有人去跟 c 比。 ==>>正常情况下,就不必深究这个了。要是有人诋毁,就喷他~~

#33 楼 @fantaxy025025 ruby? rails? sinatra?

rails。 实事求是,说法不够精确。没有像射手应用那样搞测试。只是从使用中的感觉来比较的。 从语言和框架的设计角度来看,也应该如此。 另外,搞这种对比的东西,最好是能深入理解对比对象的原理,思想,运行方式,优化方式等。 一般的二流公司不太适合搞咨询。

看你关注什么了,关注性能的话,用 Rails 可能不是最好的选择。 对于一般网站来说更看重的是开发成本。性能是在做大之后才考虑的事情。 公司做大了,有钱了再换语言嘛。。 http://www.csdn.net/article/2012-10-08/2810589

#36 楼 @murphy 文章里说用 Node.js 替换的是 REST API,rails 偏重用户界面,不是用来做 Rest API 的,国外还有用 MongoDB 做缓存的

#8 楼 @Rei 我记得你说过在 view 循环里面不要用 render,用 render collection替代,可以提高性能?, 根据我的测试,render 一个大模板,和 render 若干个小模板,时间差不多

多次

一次

需要 登录 后方可回复, 如果你还没有账号请 注册新账号