看了这个帖子 https://ruby-china.org/topics/22558

里面有个 ppt: https://speakerdeck.com/a_matsuda/is-rails-slow

这个 ppt 的内容很好, 下面是来自 @lgn23st 对此 ppt 的总结,

  • Ruby 2.0 到 2.1 的 GC 改进和性能优化
  • Rails 4.1 到 4.2 的性能优化
  • 各种 Template engine 以及坑爹的 url helpers 对性能的影响
  • 如何用最潮的 benchmark 工具和方法
  • 顺便给 hamlx 打了个广告

一个 PPT 中把这些全部展示出来了,值得每个 Rubyist 亲自动手跟着 PPT 实践一遍!

我看了好几遍 ppt, 想动手对这个 ppt 实践一遍, 动手前先了解下我们要达到的目的吧,

  1. 体验 Ruby 2.0 到 2.1 的 GC 改进和性能优化
  2. 体验 Rails 4.1 到 4.2 的性能优化
  3. 感受下 各种 Template engine 以及坑爹的 url helpers 对性能的影响
  4. 体验最潮的 benchmark 工具和方法
  5. 体验下 hamlx
  6. 体验 haml 分别在 rails 和 sinatra 下显著地性能差异,如果改成 erb 会怎样呢?

现在我们开始动手实践,

第一回合

  • 建立一个 rails 项目
 % rails new rails_is_slow
  • 配置环境变量 SECRET_KEY_BASE
% export SECRET_KEY_BASE=abc123

若以 production 环境启动 rails 应用,必须配置 SECRET_KEY_BASE, 可以在 config/secrets.yml 文件里找到 secret_key_base。

  • 生成控制器和配置路由
% rails g controller hello index

config/routes.rb,


Rails.application.routes.draw do
  get 'hello' => 'hello#index'
end

  • 启动 rails server
  rails s -e production
  • 使用浏览器访问 http://localhost:3000/hello

访问 5 次, 9ms, 12ms, 12ms, 11ms, 11ms 平均每次的响应时间是 11 ms

  • 建立一个 sinatra 项目
  % mkdir rails_is_slow_sinatra

Gemfile

  source 'https://rubygems.org'
  gem 'sinatra'

app.rb

  require 'bundler/setup'
  Bundler.require

  get '/hello' do
    'Hello, World!'
  end
  • 启动 sinatra
  RACK_ENV=production bundle ex ruby app.rb
  • 使用浏览器访问 http://localhost:4567/hello

访问 5 次, 9ms, 11ms, 9ms, 10ms, 9ms 平均每次的响应时间是 9.6ms

在不使用数据库,不使用视图模板时,即两个应用都为裸应用时, rails 和 sinatra 两者的性能几乎相同

查看 rails 的 production.log,

I, [2014-11-09T21:23:05.124508 #14822]  INFO -- : Started GET "/hello" for 127.0.0.1 at 2014-11-09 21:23:05 +0800
I, [2014-11-09T21:23:05.125675 #14822]  INFO -- : Processing by HelloController#index as HTML
I, [2014-11-09T21:23:05.127045 #14822]  INFO -- :   Rendered text template (0.0ms)
I, [2014-11-09T21:23:05.127308 #14822]  INFO -- : Completed 200 OK in 1ms (Views: 0.5ms | ActiveRecord: 0.0ms)
I, [2014-11-09T21:23:12.206147 #14822]  INFO -- : Started GET "/hello" for 127.0.0.1 at 2014-11-09 21:23:12 +0800
I, [2014-11-09T21:23:12.207563 #14822]  INFO -- : Processing by HelloController#index as HTML
I, [2014-11-09T21:23:12.208128 #14822]  INFO -- :   Rendered text template (0.0ms)
I, [2014-11-09T21:23:12.208380 #14822]  INFO -- : Completed 200 OK in 1ms (Views: 0.3ms | ActiveRecord: 0.0ms)

我们注意到在 rails 中, hello 这个请求只花费了 1ms

第二回合

  • 建立一个 scaffold, 并且使用 haml 模板

Gemfile

  +gem 'haml-rails'
  % rails g scaffold user name account email phone zip address birthday:datetime age:integer company bio:text admin:boolean
  % RAILS_ENV=production rake db:migrate
  • 通过 seeds 准备 10,000 条记录

db/seeds.rb

date_range = Date.parse('1960/1/1')..(10000.days.since(Date.parse('1960/1/1')))
[*1..10000].each do |i|
  User.create! name: "User %05d" % i,
  account: "user_%05d" % i,
  email: "user%05d@example.com" % i,
  phone: "%011d" % i,
  zip: "%03d" % (i / 10),
  address: "#{i}-#{i} Tokyo, Japan" % i,
  birthday: rand(date_range),
  age: i / 365,
  company: "#{i}Signals",
  bio: 'Lorem ipsum dolor sit amet, consectetur adipisicing
elit, sed do eiusmod tempor incididunt ut labore et dolore
magna aliqua. Ut enim ad minim veniam, quis nostrud exerci
tation ullamco laboris nisi ut aliquip ex ea commodo
consequat. Duis aute irure dolor in reprehenderit in
voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proiden
t, sunt in culpa qui officia deserunt mollit anim id est
laborum.',
  admin: true
end

% RAILS_ENV=production bin/rake db:seed
  • 启动 rails 服务, 并且查看日志
  % tail -f log/production.log | grep Completed

使用浏览器访问 http://localhost:3000/users, 我们可以看到下面的日志输出,

I, [2014-11-09T22:21:11.856447 #51508]  INFO -- : Completed 200 OK in 7499ms (Views: 7378.1ms | ActiveRecord: 112.5ms)
I, [2014-11-09T22:21:38.756297 #51508]  INFO -- : Completed 200 OK in 7598ms (Views: 7538.8ms | ActiveRecord: 58.8ms)
I, [2014-11-09T22:21:58.880376 #51508]  INFO -- : Completed 200 OK in 8133ms (Views: 8069.8ms | ActiveRecord: 62.5ms)
I, [2014-11-09T22:22:05.772164 #51508]  INFO -- : Completed 200 OK in 6774ms (Views: 6718.8ms | ActiveRecord: 54.0ms)
I, [2014-11-09T22:22:17.464458 #51508]  INFO -- : Completed 200 OK in 7802ms (Views: 7732.4ms | ActiveRecord: 68.0ms)
I, [2014-11-09T22:22:27.680324 #51508]  INFO -- : Completed 200 OK in 8425ms (Views: 8339.1ms | ActiveRecord: 84.7ms)

同时日志中有一些和资源加载相关的错误,

ActionController::RoutingError (No route matches [GET] "/stylesheets/application.css"):
  actionpack (4.1.7) lib/action_dispatch/middleware/debug_exceptions.rb:21:in `call'
  actionpack (4.1.7) lib/action_dispatch/middleware/show_exceptions.rb:30:in `call'

所以我们移除 layout 和 assets,

class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]
  +layout :false
  ...

在没有 layout 的情况下,响应时间如下,

I, [2014-11-09T22:29:30.293831 #52308]  INFO -- : Completed 200 OK in 7114ms (Views: 7020.4ms | ActiveRecord: 85.8ms)
I, [2014-11-09T22:29:43.623968 #52308]  INFO -- : Completed 200 OK in 8484ms (Views: 8293.2ms | ActiveRecord: 190.3ms)
I, [2014-11-09T22:30:34.257335 #52308]  INFO -- : Completed 200 OK in 7203ms (Views: 7102.5ms | ActiveRecord: 99.5ms)
I, [2014-11-09T22:30:45.708797 #52308]  INFO -- : Completed 200 OK in 9109ms (Views: 9019.5ms | ActiveRecord: 88.2ms)
I, [2014-11-09T22:30:57.693639 #52308]  INFO -- : Completed 200 OK in 9040ms (Views: 8955.4ms | ActiveRecord: 83.7ms)

此时响应时间还是 8ms 左右, 我们可以看到 ActiveRecord 花费的时间并不多,只有 80ms 左右,大部分(98%)的时间都用在了渲染 Haml视图上去了。我们先不着急 和 sinatra 作比较, 我们将 haml 和 erb 作个比较。在 routes.rb 里增加新的路由,

Rails.application.routes.draw do
  resources :users

  +get 'users_index_erb' => 'users#index_erb'
  get 'hello' => 'hello#index'
end

在 UsersController 里增加一个方法: index_erb,

class UsersController < ApplicationController
  ...
  def index_erb
    @users = User.all
  end
  ...
end

增加相应的视图文件 index_erb.html.erb, 可以利用 https://haml2erb.org/ 把 index.html.haml 的内容转换为 erb,

<h1>Listing users</h1>
<table>
  <tr>
    <th>Name</th>
    <th>Account</th>
    <th>Email</th>
    <th>Phone</th>
    <th>Zip</th>
    <th>Address</th>
    <th>Birthday</th>
    <th>Age</th>
    <th>Company</th>
    <th>Bio</th>
    <th>Admin</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>
  <% @users.each do |user| %>
    <tr>
      <td>
        <%= user.name %>
      </td>
      <td>
        <%= user.account %>
      </td>
      <td>
        <%= user.email %>
      </td>
      <td>
        <%= user.phone %>
      </td>
      <td>
        <%= user.zip %>
      </td>
      <td>
        <%= user.address %>
      </td>
      <td>
        <%= user.birthday %>
      </td>
      <td>
        <%= user.age %>
      </td>
      <td>
        <%= user.company %>
      </td>
      <td>
        <%= user.bio %>
      </td>
      <td>
        <%= user.admin %>
      </td>
      <td>
        <%= link_to 'Show', user %>
      </td>
      <td>
        <%= link_to 'Edit', edit_user_path(user) %>
      </td>
      <td>
        <%= link_to 'Destroy', user, :method => :delete, :data => { :confirm => 'Are you sure?' } %>
      </td>
    </tr>
  <% end %>
</table>
<br/>
<%= link_to 'New User', new_user_path %>
  • 使用浏览器访问 http://localhost:3000/users_index_erb

访问 5 次,每次的响应时间分别是: 10ms, 9ms, 9ms, 11ms, 10ms

日志输出为:

I, [2014-11-14T22:28:04.695994 #83566]  INFO -- : Completed 200 OK in 7404ms (Views: 7231.1ms | ActiveRecord: 171.7ms)
I, [2014-11-14T22:29:58.455348 #83566]  INFO -- : Completed 200 OK in 6941ms (Views: 6749.0ms | ActiveRecord: 190.7ms)
I, [2014-11-14T22:30:22.522506 #83566]  INFO -- : Completed 200 OK in 6798ms (Views: 6692.7ms | ActiveRecord: 104.2ms)
I, [2014-11-14T22:30:41.590326 #83566]  INFO -- : Completed 200 OK in 8399ms (Views: 8293.4ms | ActiveRecord: 104.1ms)
I, [2014-11-14T22:31:16.102876 #83566]  INFO -- : Completed 200 OK in 6871ms (Views: 6739.3ms | ActiveRecord: 131.3ms)

我们可以认为与 haml 相比较,使用 erb 并没有提高性能

Sinatra + Haml + ActiveRecord

  • 启用 Haml 和 ActiveRecord

Gemfile

gem 'sinatra'
gem 'sinatra-activerecord'
gem 'sqlite3'
gem 'haml'
  • 连接数据库

app.rb

+set :database, adapter: 'sqlite3', database: 'production.sqlite3'
+class User < ActiveRecord::Base
end

将 rails_is_slow 项目下的 production.sqlite3 数据库拷贝到项目下,

 $ cp rails_is_slow/db/production.sqlite3 rails_is_slow_sinatra/
  • 实现 /users

app.rb

+get '/users' do
+ @users = User.all.to_a
+ haml :index
+end

将 rails_is_slow 项目下的 users 视图文件拷贝到 rails_is_slow_sinatra 项目下,

$ cp rails_is_slow/app/views/users/index.html.haml rails_is_slow_sinatra/views/index.haml

注释掉 index.haml 内的一些 url helper, 比如 link_to, edit_user_path, new_user_path 等.

使用浏览器访问 http://localhost:4567/users, 访问 5 次, 每次的响应时间是 3s, 3s, 4s, 4s, 3s。

我们再将 rails_is_slow_sinatra/views/index.haml 拷贝回 rails_is_slow/app/views/users/index.html.haml

使用浏览器访问 http://localhost:3000/users, 访问 5 次, 每次的响应时间是 4ms, 4ms, 5ms, 4ms, 6ms, 这时我们发现如果去掉视图里的各种 url helper, rails 的性能 大概提高了一倍, 因此 rails 中的 url helper 的性能是一个值得关注的问题。

Rails 中的 url helper 对性能会有较大的负面影响

调整 Ruby GC

按照 is-rails-slow 这个 ppt 的步骤,我们对 Ruby 的 GC 做些试验。

Gemfile

+gem 'gc_tracer'

UsersController.rb

  def index
-    @users = User.all.to_a
+    GC::Tracer.start_logging(Rails.root.join("tmp/gc.txt").to_s) do
+      @users = User.all.to_a
+      render
+    end
  end

每次访问 http://localhost:3000/users 后,我们在 tmp/gc.txt 文件里能够得到一堆和 GC 相关的输出:

tick	type	count	heap_used	heap_length	heap_increment	heap_live_slot	heap_free_slot	heap_final_slot	heap_swept_slot	heap_eden_page_length	heap_tomb_page_length	total_allocated_object	total_freed_object	malloc_increase	malloc_limit	minor_gc_count	major_gc_count	remembered_shady_object	remembered_shady_object_limit	old_object	old_object_limit	oldmalloc_increase	oldmalloc_limit	major_by	gc_by	have_finalizer	immediate_sweep	ru_utime	ru_stime	ru_maxrss	ru_ixrss	ru_idrss	ru_isrss	ru_minflt	ru_majflt	ru_nswap	ru_inblock	ru_oublock	ru_msgsnd	ru_msgrcv	ru_nsignals	ru_nvcsw	ru_nivcsw
133232142907	gc_start	51	3532	4228	696	1381961	57670	0	758069	3532	0	7700718	6318757	31773504	31773459	41	9	24454	27704	652626	819366	31773504	24159190	oldmalloc	malloc	0	11922588	585390	289144832	0	0	0	73651	6	0	2	118	32	41	0	316	15767
133422656054	gc_end_m	51	3532	4228	696	1381961	57670	0	758069	3532	0	7700718	6318757	31773504	31773459	41	10	7958	27704	386214	819366	31773504	24159190	oldmalloc	malloc	0	11990883	587034	289153024	0	0	0	73653	6	0	2	118	32	41	0	316	16005
134303721618	gc_end_s	51	3532	4228	696	398669	1040973	11	1040962	2964	568	7700718	7302049	0	31773504	41	10	7958	15916	386214	772428	0	24159190	oldmalloc	malloc	0	1	12302598	600975	289153024	0	0	0	73656	6	0	2	118	32	41	0	316	16570
138024843100	gc_start	52	3532	4228	696	1112413	327218	0	1040973	2964	568	8414473	7302060	31773872	31773504	41	10	7958	15916	386214	772428	31773872	24159190	0	malloc	0	1	13581580	624087	289153024	0	0	0	74576	6	0	2	118	32	41	0	323	18719
138088825986	gc_end_m	52	3532	4228	696	1112413	327218	0	1040973	2964	568	8414473	7302060	31773872	31773504	42	10	14126	15916	410891	772428	31773872	24159190	0	malloc	0	1	13605305	624365	289153024	0	0	0	74576	6	0	2	118	32	41	0	323	18751
138538171872	gc_end_s	52	3532	4228	696	429503	1010128	0	778610	2964	568	8414473	7984970	0	31773824	42	10	14126	15916	410891	772428	2067344	28991028	0	malloc	0	1	13736593	625907	289153024	0	0	0	74576	6	0	2	118	32	41	0	323	18984

说实在话我没有看明白这些内容的意思。

现在我们停止 GC, 然后观察一下 rails_is_slow 的性能变化。我们把 GC.disable 这段代码加到某个配置文件中, 比如,

config/gc.rb

GC.disable

重启应用后,我们会发现 GC 停止了, 可以通过观察 tmp/gc.txt 的内容得出这个结论,

tick	type	count	heap_used	heap_length	heap_increment	heap_live_slot	heap_free_slot	heap_final_slot	heap_swept_slot	heap_eden_page_length	heap_tomb_page_length	total_allocated_object	total_freed_object	malloc_increase	malloc_limit	minor_gc_count	major_gc_count	remembered_shady_object	remembered_shady_object_limit	old_object	old_object_limit	oldmalloc_increase	oldmalloc_limit	major_by	gc_by	have_finalizer	immediate_sweep	ru_utime	ru_stime	ru_maxrss	ru_ixrss	ru_idrss	ru_isrss	ru_minflt	ru_majflt	ru_nswap	ru_inblock	ru_oublock	ru_msgsnd	ru_msgrcv	ru_nsignals	ru_nvcsw	ru_nivcsw

我们可以看到上面的内容里已经没有 GC 相关的数据了。

关闭 GC 时的响应时间:

I, [2014-11-15T13:36:25.785793 #66181]  INFO -- : Completed 200 OK in 2079ms (Views: 1823.2ms | ActiveRecord: 73.8ms)
I, [2014-11-15T13:36:31.061513 #66181]  INFO -- : Completed 200 OK in 2735ms (Views: 2392.9ms | ActiveRecord: 88.8ms)
I, [2014-11-15T13:36:47.150416 #66181]  INFO -- : Completed 200 OK in 2100ms (Views: 1827.2ms | ActiveRecord: 72.0ms)
I, [2014-11-15T13:37:05.795685 #66181]  INFO -- : Completed 200 OK in 2235ms (Views: 1952.2ms | ActiveRecord: 74.0ms)
I, [2014-11-15T13:37:21.624737 #66181]  INFO -- : Completed 200 OK in 2106ms (Views: 1814.3ms | ActiveRecord: 103.8ms)
I, [2014-11-15T13:37:37.734350 #66181]  INFO -- : Completed 200 OK in 2357ms (Views: 2067.1ms | ActiveRecord: 101.9ms)

打开 GC 时的响应时间:

I, [2014-11-15T13:38:45.708158 #66845]  INFO -- : Completed 200 OK in 2762ms (Views: 2381.7ms | ActiveRecord: 134.9ms)
I, [2014-11-15T13:38:56.903308 #66845]  INFO -- : Completed 200 OK in 2640ms (Views: 2290.2ms | ActiveRecord: 100.2ms)
I, [2014-11-15T13:39:21.561863 #66845]  INFO -- : Completed 200 OK in 2419ms (Views: 2135.5ms | ActiveRecord: 87.9ms)
I, [2014-11-15T13:39:37.824292 #66845]  INFO -- : Completed 200 OK in 2170ms (Views: 1879.4ms | ActiveRecord: 66.2ms)
I, [2014-11-15T13:39:53.330387 #66845]  INFO -- : Completed 200 OK in 2312ms (Views: 2067.5ms | ActiveRecord: 63.8ms)
I, [2014-11-15T13:40:15.175491 #66845]  INFO -- : Completed 200 OK in 2197ms (Views: 1909.7ms | ActiveRecord: 77.6ms)

两者对比下,可以看到 GC 的关闭或者打开对 rails 应用的性能影响不大

试试 Rails 4.2

@tenderlove 说 “Rails 4.2 is going to be the fastest Rails ever.”, 所以我们现在试试 Rails 4.2。

Gemfile

-gem 'rails', '4.1.7'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', github: 'rails/rails'

打开 GC, 访问 http://localhost:3000/users, 响应时间:

I, [2014-11-15T15:14:57.014377 #96328]  INFO -- : Completed 200 OK in 2926ms (Views: 2609.7ms | ActiveRecord: 99.3ms)
I, [2014-11-15T15:16:33.169118 #96328]  INFO -- : Completed 200 OK in 3026ms (Views: 2658.8ms | ActiveRecord: 162.1ms)
I, [2014-11-15T15:18:59.715360 #96328]  INFO -- : Completed 200 OK in 2934ms (Views: 2685.4ms | ActiveRecord: 73.7ms)
I, [2014-11-15T15:22:14.851652 #96328]  INFO -- : Completed 200 OK in 2987ms (Views: 2745.6ms | ActiveRecord: 66.0ms)
I, [2014-11-15T15:22:25.610507 #96328]  INFO -- : Completed 200 OK in 3831ms (Views: 3518.6ms | ActiveRecord: 110.0ms)

关闭 GC, GC.disable, 访问 http://localhost:3000/users, 响应时间:

I, [2014-11-15T15:24:15.153951 #97266]  INFO -- : Completed 200 OK in 2878ms (Views: 2604.2ms | ActiveRecord: 74.3ms)
I, [2014-11-15T15:24:24.363854 #97266]  INFO -- : Completed 200 OK in 4043ms (Views: 3607.7ms | ActiveRecord: 103.2ms)
I, [2014-11-15T15:24:34.652407 #97266]  INFO -- : Completed 200 OK in 3658ms (Views: 3268.5ms | ActiveRecord: 135.3ms)
I, [2014-11-15T15:24:48.085198 #97266]  INFO -- : Completed 200 OK in 4231ms (Views: 3863.1ms | ActiveRecord: 108.3ms)
I, [2014-11-15T15:24:57.836219 #97266]  INFO -- : Completed 200 OK in 3847ms (Views: 3443.1ms | ActiveRecord: 106.7ms)

从目前的试验来看, rails4.1 和 rails4.2 之间的性能差不多

那么 rails 4.2 的 ActiveRecord 到底发生了哪些变化呢?

使用 TracePoint 查看哪些方法被调用了

从 ruby2.0 起 TracePoint 已经被引入到了 ruby 核心。

app/controllers/users_controller.rb

  def index
    calls = []
    trace = TracePoint.new(:call) do |tp|
      calls << [tp.defined_class, tp.method_id, tp.lineno]
    end
    trace.enable
    @users = User.all.to_a
    trace.disable
    pp calls.group_by(&:itself).map {|k, v| {k => v.length}}.sort_by {|h| -h.values.first }
  end

访问 http://localhost:3000/users, 有如下输出:

rails 4.2,

[{[ActiveRecord::AttributeMethods::ClassMethods,
   :define_attribute_methods,
   79]=>20000},
 {[ActiveSupport::Callbacks::CallbackChain, :empty?, 488]=>20000},
 {[ActiveSupport::Callbacks, :_run_callbacks, 86]=>20000},
 {[ActiveRecord::ModelSchema::ClassMethods, :inheritance_column, 186]=>20000},
 {[Object, :present?, 23]=>10001},
 {[#<Class:ActiveRecord::Base>, :_initialize_callbacks, 110]=>10000},
 {[ActiveRecord::Base, :_run_initialize_callbacks, 733]=>10000},
 {[ActiveRecord::Inheritance::ClassMethods,
   :discriminate_class_for_record,
   167]=>10000},
 {[#<Class:ActiveRecord::Base>, :_find_callbacks, 110]=>10000},
 {[ActiveRecord::Base, :_find_callbacks, 734]=>10000},
 {[ActiveRecord::Inheritance::ClassMethods,
   :using_single_table_inheritance?,
   175]=>10000},
 {[ActiveRecord::Base, :_initialize_callbacks, 734]=>10000},
 {[ActiveRecord::Persistence::ClassMethods, :instantiate, 66]=>10000},
 {[ActiveRecord::Base, :_run_find_callbacks, 733]=>10000},
 {[NilClass, :blank?, 54]=>10000},
 {[ActiveRecord::AttributeSet, :initialize, 5]=>10000},
 {[ActiveRecord::Core, :init_with, 297]=>10000},
 {[ActiveRecord::Persistence::ClassMethods,

ppt 上标注的是

[{[ActiveRecord::AttributeMethods::ClassMethods,
   :define_attribute_methods,
   79]=>140000},

而我们现在的试验结果是

[{[ActiveRecord::AttributeMethods::ClassMethods,
   :define_attribute_methods,
   79]=>20000},

当时 ppt 的作者认为这是一个问题,会消耗大量的 CPU 时间, 因为 140000 是 20000的 7倍,按目前的测试来看,rails4.2已经修复了这个问题, 即 ActiveRecord 的一些方法 调用回到了正常水平。

试验 Stackprof

stackprof 是 @tmm1 写的一个 gem, 可以用于 ruby2.1+, 这个 gem 可以用于查看方法调用所消耗的 CPU 时间。

app/controllers/users_controller.rb

def index
  StackProf.run(mode: :cpu, out: 'tmp/stackprof-cpu-ar.dump') do
    @users = User.all.to_a
  end
end

可惜在我的试验中, tmp/stackprof-cpu-ar.dump 输出的是一堆乱码。

hamlx

作者给出了 hamlx, 貌似是一个优化版本的 haml, 作者说使用 hamlx 比使用 haml 会有 10% ~ 20% 的性能提高。 由于作者还没有发布 hamlx, 我们就不做这个试验了。

试验用到的项目代码和 PPT