Ruby
 Computer >> コンピューター >  >> プログラミング >> Ruby

どちらが最速ですか? ERB vs. HAML vs. Slim

この記事では、最も人気のある3つのRubyテンプレートエンジンであるERB(デフォルトのエンジン)、HAML、SLIMのパフォーマンスをテストおよび分析します。

ベンチマークとは、ビジネスプロセスとパフォーマンスメトリックを業界のベストプラクティスや他社のベストプラクティスと比較することです。一方、負荷テストは、システムに需要を与え、その応答を測定するプロセスです。

私たちの目標は、Rubyコードの実行に使用された時間を測定および報告するためのメソッドを提供するRubyベンチマークモジュールを少し調べることです。いくつかのインラインテンプレートを作成し、それらをテストに対して実行し、3つの異なるエンジンのメトリックを抽出します。

その後、実際のテンプレートビューを作成して負荷テストを行い、それらをサーバーに公開してから、heyツールを使用して負荷テストを実行します。特定のWebアプリケーションに負荷を送信し、各エンドポイント、つまり各テンプレートエンジンのパフォーマンスに関連する完全なデータを取得するための優れた機能を提供します。

セットアップ

もちろん、最初に行うことは、Rubyがすでにインストールされていることを確認することです。この記事の執筆時点では、最新バージョンの2.7.0を使用しています。 Railsgemもインストールしてください。

RubyとRailsの宝石は、私たちが始めるために必要なものです。このチュートリアルでは、Visual Studio Codeがコーディング用のIDEですが、必要に応じて別のコードを選択してください。

次に、Railsプロジェクトのフォルダーを選択し、次のコマンドを実行します。

rails new haml-slim-erb

これにより、必要なすべての依存関係がダウンロードされ、スキャフォールドRailsプロジェクトが作成されます。さあ、探索してください。

コードに進む前に、SLIMとHAMLの依存関係をGemfileに追加する必要があります。 :

group :development, :test do
  # Call 'byebug' anywhere in the code to stop execution and get a debugger console
  gem 'byebug', platform: :mri
  gem 'haml'
  gem 'slim'
end

SQLiteバージョンに関連するプロジェクトにはデフォルトで発生するバグもあります。 Gemfileで見つけます 次のように変更します:

gem 'sqlite3', '~> 1.3.0'

次に、 bundle installを実行します 依存関係をダウンロードするコマンド。

ベンチマークモジュールの探索

記事のこの部分では、テストを直接使用します。 フォルダ。それを開くと、いくつかの空のフォルダが表示されます。 ベンチマークという新しいベンチマークを作成しましょう および他の3つのファイル: example_1_test.rb example_2_test.rb 、およびexample_3_test.rb。

Rubyはtestで終わる必要があります テストファイルと見なされます。

次に、次のコンテンツを最初のファイルに追加します。

require 'benchmark'

number = (0..50).to_a.sort{ rand() - 0.5 }[0..10000]

puts Benchmark.measure {
  20_000.times do
    number[rand()] * (0..50).to_a.sort{ rand() - 0.5 }[0..10000][rand()]
  end
}

最初の行は、必要なベンチマークモジュールをインポートすることに注意してください。次に、サイズが10.000の0から50までの数値のランダム配列を生成します。これらの大きな数は、処理に少し時間がかかるだけです。

メソッドmeasure Rubyコードのどこにでも配置して、処理にかかる時間を測定できるため、非常に便利です。今回はputsによって返され印刷されます 。

内部では、ランダムに生成された同じ配列の実行を20k回ループして、それぞれの値を1つ乗算しています。

このテストファイルを具体的に実行するには、次のコマンドを発行します。

rake test TEST=test/benchmark/example_1_test.rb

結果は次のようになります:

0.702647   0.012353   0.715000 (  0.721910)

このレポートは、ユーザーのCPU時間、システムのCPU時間、ユーザーとシステムのCPU時間の合計、および経過したリアルタイムをそれぞれ出力します。時間の単位は秒です。

実際には、他のベンチマーク手法をさらに活用します。

インラインテンプレートテスト

Rubyのベンチマークモジュールがどのように機能するかについてもう少し理解できたので、3つのテンプレートに対するいくつかのテストの実行について詳しく説明します。

このために、単一のテンプレートを作成し、それを3つのエンジン構文に変換して、最後にベンチマークメソッドで実行します。

2番目のテストファイルに以下を追加します。

require 'erb'
require 'haml'
require 'slim'
require 'benchmark'
require 'ostruct'

notes = OpenStruct.new title: 'Write an essay', description: 'My essay is about...', randomList: (0..50).to_a.sort{ rand() - 0.5 }[0..10000]

erb_example = <<-ERB_EXAMPLE
<span><%= notes.title %></span>
<span><%= notes.description %></span>
<table>
  <tr>
    <% notes.randomList.each do |note| %>
      <td><%= note %></td>
    <% end %>
  </tr>
</table>
ERB_EXAMPLE

slim_example = <<-SLIM_EXAMPLE
span= notes.title
span= notes.description
table
  tr
    - notes.randomList.each do |note|
      td= note
SLIM_EXAMPLE

haml_example = <<-HAML_EXAMPLE
%span= notes.title
%span= notes.description
%table
  %tr
    - notes.randomList.each do |note|
      %td= note
HAML_EXAMPLE

context = OpenStruct.new notes: notes
__result = ''

Benchmark.bmbm(20) do |bcmk|
  bcmk.report("erb_test") { (1..2000).each { ERB.new(erb_example, 0, '-', '__result').result binding } }
  bcmk.report("slim_test") { (1..2000).each{ __result = Slim::Template.new { slim_example }.render(context) } }
  bcmk.report("haml_test") { (1..2000).each { __result = Haml::Engine.new(haml_example).render(binding) } }
end

まず、必要なモジュールをインポートします。テンプレートエンジンの他に、 ostructもインポートしています モジュール。 OpenStruct Hash に似た、メタプログラミングのデータ構造です。 、これにより、任意の属性とそれに付随する値を定義できます。

値を格納するためにクラス構造全体を作成する必要がないため、これは便利です。インラインで定義できます。

私たちの構造体は基本的にNote タイトル、説明、および乱数のリストを含むオブジェクト。これにより、処理時間が長くなります。

各テンプレートエンジンがどのように機能するかに焦点を当てません。これについては、彼らの公式ドキュメントを参照できます。ただし、それらの構文は非常に簡単に理解できます。

魔法はコードの最後に配置されます。現在、 bmbmを使用しています 最初の方法よりも少し多くのことを行う方法。ガベージコレクションやメモリリークなどの外部要因により、ベンチマーク結果が歪む場合があります。この方法では、テストを2回実行することで、この影響を最小限に抑えようとします。本当の時間。これについて詳しくは、こちらをご覧ください。

最後に、 bmbmのそれぞれ の内部コード行は、各テンプレートの作成中に2000回ループを実行します。焦点は、テンプレートの割り当てとレンダリングにあります。

このテストファイルを実行した後の結果は次のとおりです。

Rehearsal --------------------------------------------------------
erb_test               0.311534   0.002963   0.314497 (  0.314655)
slim_test              2.544711   0.004520   2.549231 (  2.550307)
haml_test              1.449813   0.003169   1.452982 (  1.454118)
----------------------------------------------- total: 4.316710sec

                           user     system      total        real
erb_test               0.298730   0.000679   0.299409 (  0.299631)
slim_test              2.550665   0.004148   2.554813 (  2.556023)
haml_test              1.432653   0.001984   1.434637 (  1.435417)

2つの結果ブロックは、リハーサルから実際のものを吸収するために分離されています。

シナリオを少し変更する

最後のテスト結果では、ERBが最良のオプションであり、SLIMが最悪であると想定できます。繰り返しますが、状況によって異なります。

そのテストでは、ループするたびに、新しいをインスタンス化する必要があります。 テンプレートエンジンオブジェクト。これは最適なフローではありません。

次のコードスニペットに示すように、少し変更して、このインスタンス化を外側に移動しましょう。

erb_engine = ERB.new(erb_example, 0, '-', '__result')
slim_engine = Slim::Template.new { slim_example }
haml_engine = Haml::Engine.new(haml_example)

Benchmark.bmbm(10) do |bcmk|
  bcmk.report("erb_test") { (1..2000).each { erb_engine.result binding } }
  bcmk.report("slim_test") { (1..2000).each{ __result = slim_engine.render(context) } }
  bcmk.report("haml_test") { (1..2000).each { __result = haml_engine.render(binding) } }
end

コードは以前とまったく同じです。ここで、テストを再度実行すると、結果として次のようなものが表示されます。

Rehearsal ----------------------------------------------
erb_test     0.127599   0.002407   0.130006 (  0.130137)
slim_test    0.046972   0.000841   0.047813 (  0.047858)
haml_test    0.208308   0.002239   0.210547 (  0.210769)
------------------------------------- total: 0.388366sec

                 user     system      total        real
erb_test     0.118002   0.000556   0.118558 (  0.118618)
slim_test    0.040129   0.000090   0.040219 (  0.040320)
haml_test    0.205331   0.001163   0.206494 (  0.206680)

どちらが現在最高で最悪であるかに注意してください。これは、完璧なエンジンという点で特効薬がないことを示すためだけのものです。

コーディングのスタイルに適した機能をテストおよび分析する必要があります。さらに、Ruby Profilerなどの他の補助ツールがあり、コードがこのように動作する方法と理由をよりよく理解するために使用できます。

実際のシナリオの負荷テスト

私たちの現実に近いものに移りましょう。いくつかのメモをリストした実際のテンプレートのベンチマークを行います(テンプレートエンジンごとに3つ、1つ)。

ベンチマーク以降 モジュールはRailsコードで行われるため、テンプレートエンジンの内部プロセスに関連するいくつかの重要な手段が失われます。

このタイプのベンチマークテストでは、プロセスの最初から要求の到着から、応答データがビューに到達するまでのビジネスロジック処理まで、各エンジンが全体としてどのように機能するかを確認できます。この最後のステップには、具体的には、負荷テストで測定できる解析およびレンダリングプロセスや、ベンチマークなどの一連のステップが含まれます。 できません。

まず、例ごとにRailsコントローラーを作成しましょう。

rails g controller notes_erb index
rails g controller notes_haml index
rails g controller notes_slim index

このコマンドは、慣れ親しんだ一般的なRailsファイルの束を自動生成します。

次に、 notes_erb_controller.rbを開きます。 ファイルを作成し、その内容を次のように変更します:

class NotesErbController < ApplicationController
  def index
    @notes = JSON.parse(Constants::NOTES, object_class: OpenStruct)
  end
end

ここで、テンプレートにデータをフィードします。エンジンごとに1つのコントローラークラスがあることに注意してください。

基本的に、インライン定数からJSONを取得しています。応答は新しいOpenStructに解析されます オブジェクトとエンジンに返されます。

注意事項 定数は、 constants.rbという新しいファイルに配置する必要があります 。先に進んで作成し、次のコンテンツを追加します。

class Constants
    NOTES = '[
        {
            "title": "Walk the dog",
            "description": "Bla bla",
            "tasks": [{
                "title": "Task #1"
            },
            {
                "title": "Task #2"
            },
            {
                "title": "Task #3"
            }]
        },
        ...
        {
            "title": "Walk the dog",
            "description": "Bla bla",
            "tasks": [{
                "title": "Task #1"
            },
            {
                "title": "Task #2"
            },
            {
                "title": "Task #3"
            }]
        }
    ]
    '
end

より多くの音符要素の省略記号を変更してください。さらに、他の各コントローラーでロジックを複製することを忘れないでください。

ERBビューの作成

次に、例のビューを作成します。これを行うには、 views / notes_erbに移動します フォルダを作成し、他の2つのファイルを作成します: note.html.erb およびtask.html.erb 。ビュー、パーシャル、レイアウトを含む例を作成するのに役立ちます。

このようにして、私たちの例がRailsエンジンを最も探索したことを保証します。必ず両方のviews/ notes_hamlと同等のファイルを作成してください およびviews/ notes_slim

index.html.erbから始めましょう コード:

<style>
h2 {
    text-align: center;
}

table, td, th {
  border: 1px solid #ddd;
  text-align: left;
}

table {
  border-collapse: collapse;
  width: 80%;
  margin: auto;
}

th, td {
  padding: 15px;
}
</style>
<h2>List of Notes</h2>
<table>
    <thead>
        <tr>
            <th>Title</th>
            <th>Description</th>
            <th>Tasks</th>
        </tr>
    </thead>
    <tbody>
        <%= render partial: 'notes_erb/note', collection: @notes %>
    </tbody>
</table>

ここには特別なことは何もありません。部分的な_note.html.erbをインポートしていることに注意してください ファイル、およびコレクションを渡す パラメータ: @notes 以前にコントローラーで作成されました。

ちなみに、メモの内容は次のとおりです。

<tr>
  <td>
    <span><%= note.title %></span>
  </td>
  <td>
    <span><%= note.description %></span>
  </td>
  <td>
    <ul>
      <%= render partial: 'notes_erb/task', collection: note.tasks %>
    </ul>
  </td>
</tr>

タスクにアクセスするもう1つの部分があります 今回は配列します。

_task.html.erbのコンテンツ 次のとおりです:

<li><%= task.title %></li>

HAMLの見解

あるエンジンから別のエンジンへの構文が非常に似ていることに気付くでしょう。変化するのは、それぞれの冗長性だけです。たとえば、SLIMはその中で最もクリーンなものです。

3つのファイルのコードを見てください:

# Content of index.html.haml
:css
  h2 {
      text-align: center;
  }

  table, td, th {
    border: 1px solid #ddd;
    text-align: left;
  }

  table {
    border-collapse: collapse;
    width: 80%;
    margin: auto;
  }

  th, td {
    padding: 15px;
  }

%h2 List of Notes
%table
  %thead
    %tr
      %th Title
      %th Description
      %th Tasks

  %tbody
    = render partial: 'notes_haml/note', collection: @notes

# Content of _note.html.haml
%tr
  %td
    %span= note.title


  %td
    %span= note.description


  %td
    %ul
      = render partial: 'notes_haml/task', collection: note.tasks

# Content of _task.html.haml
%li= task.title

非常に似ていますね

SLIMの見解

最後に、SLIMの見解があります。これが他の2つとの最大の違いです。全体の構造がより明確になります:

# index.html.slim
css:
  h2 {
      text-align: center;
  }

  table, td, th {
    border: 1px solid #ddd;
    text-align: left;
  }

  table {
    border-collapse: collapse;
    width: 80%;
    margin: auto;
  }

  th, td {
    padding: 15px;
  }

h2 List of Notes
table
  thead
    tr
      th Title
      th Description
      th Tasks

  tbody
    = render partial: 'notes_haml/note', collection: @notes

# _note.html.slim
tr
  td
    span= note.title


  td
    span= note.description


  td
    ul
      = render partial: 'notes_haml/task', collection: note.tasks

# _task.html.slim
li= task.title

また、レイアウトをそれぞれのエンジン構文に変換する必要があります。 ビュー/レイアウトの下に2つの新しいファイルを作成する必要があります フォルダ: application.html.haml およびapplication.html.slim

その仕事は宿題としてあなたに任せます。ただし、難しい場合は、記事の最後にあるGitHubプロジェクトのリンクで私のバージョンを参照できます。

テストの実行

最後に、例をテストします。まず、 railsを実行してアプリケーションを起動します 指図。 http:// localhost:3000/アドレスから始まります。

ビューは次のようになります:

どちらが最速ですか? ERB vs. HAML vs. Slim

各テンプレートエンジンの例は、 config / routers.rbで自動生成されたそれぞれのURLで利用できます。 ファイル。

これらの例をベンチマークテストするために、heyベンチマークツールを使用します。これは非常に単純で、ベンチマーク分析に役立つ情報を提供します。コマンドは次のとおりです。

$ hey https://localhost:3000/notes_erb/index

Summary:
  Total:        9.3978 secs
  Slowest:      9.1718 secs
  Fastest:      0.0361 secs
  Average:      1.2714 secs
  Requests/sec: 21.2816

$ hey https://localhost:3000/notes_haml/index
Summary:
  Total:        10.8661 secs
  Slowest:      10.2354 secs
  Fastest:      0.1871 secs
  Average:      1.4735 secs
  Requests/sec: 18.4058

$ hey https://localhost:3000/notes_slim/index

Summary:
  Total:        11.3384 secs
  Slowest:      10.7570 secs
  Fastest:      0.0437 secs
  Average:      1.5406 secs
  Requests/sec: 17.6392

ご覧のとおり、すべてのエンジンは実行時間の点で非常に近いです。実行するリクエストのデフォルト数は200ですが、この値は -nを使用して変更できます。 オプション。

1200リクエストで実行された同じテストを見てみましょう:

$ hey -n 1200 https://localhost:3000/notes_erb/index

Summary:
  Total:        52.2586 secs
  Slowest:      19.2837 secs
  Fastest:      0.0389 secs
  Average:      0.6960 secs
  Requests/sec: 22.9627

$ hey -n 1200 https://localhost:3000/notes_haml/index
Summary:
  Total:        61.7637 secs
  Slowest:      18.5290 secs
  Fastest:      0.0442 secs
  Average:      0.8557 secs
  Requests/sec: 19.4289

$ hey -n 1200 https://localhost:3000/notes_slim/index

Summary:
  Total:        63.1625 secs
  Slowest:      19.9744 secs
  Fastest:      0.0874 secs
  Average:      0.7959 secs
  Requests/sec: 18.9986

同時リクエストの数を増やすと、合計処理時間と平均処理時間の差が大きくなります。明らかに、何千もの並列リクエストを伴うこのシナリオは非常に具体的であり、あまり一般的ではありません。ただし、負荷テストはそれがすべてであり、エンドポイントを限界まで引き上げます。

heyツールは、応答時間ヒストグラムなどの他の情報も出力します。

どちらが最速ですか? ERB vs. HAML vs. Slim

これは、各リクエストが完了するまでにかかった平均時間を概算で示しています。この例では、ほとんどのリクエスト(1048)が1.893秒で完了したことは明らかです。これが、ストレステストを同時に実行することが望ましい理由です。

レイテンシの分散、DNSダイヤルアップとルックアップの詳細、リクエストの書き込み、待機時間と読み取り時間、エラーなどに関する詳細情報もあります。

その他のカスタムオプション/結果については、ドキュメントを確認してください。

概要

この例のソースコードはここにあります。

この種のテストは、プロジェクトのニーズにより適したテンプレートエンジンを定義するのに役立ちます。これまで見てきたように、一部の小さなコードスニペットは実行の全体的なパフォーマンスを突然変更する可能性があるため、実装が不十分なコードには注意してください。

もう1つの興味深い点は、ERBは、デフォルトのRailsエンジンとして、その仕事にも優れているということです。一部のテストでは他のエンジンが高速であることに加えて、ERBは常にその価値を証明するのに十分な距離にあります。

最後に、キャッシュ、プロキシ、データベースの使用、その他の保存メカニズム、キューなどの非同期ツールなど、テストのその他の重要な要素を宿題として検討することをお勧めします。これらのアイテムは、ビューの動作やレンダリングにかかる​​処理時間において常に重要な役割を果たします。頑張ってください!


  1. お住まいの地域で最も速いVPNはどれですか?この無料ツールはあなたに教えてくれます

    VPNがファイル共有などの違法行為にのみ必要であると思われる場合は、もう一度考えてみてください。真実は、VPNを使用して自分自身を保護したり、粗雑なネットワークパフォーマンスを改善したりする必要がある場合が多いということです。 たとえば、VPNを使用すると、一時的に海外にいるときに米国のNetflixにアクセスできます。 VPNに対する最近のNetflixの取り締まりは、多くのコードカッターに打撃を与えましたが、他にも用途があります。検索プライバシー、国際VoIP、さらには公共Wi-Fiの保護です。 ただし、すべてのVPNが同じように作成されるわけではありません。私にとってうまくいくもの

  2. どのRubyIDEを使用する必要がありますか?

    エディター内で開発者として多くの時間を費やすことになるため、快適で生産性の高いエディターを使用することが重要です。 この記事の内容: どのRubyIDE/エディターがあなたに適しているかを選択するお手伝いをしたいと思います ! この決定について強調する必要はありません。1つ選んで、数週間試してみて、どのように感じるかを確認してください。エディターは後でいつでも変更できます。 探すべきことがいくつかあります : 作業を簡単にするために利用できるRuby関連のプラグイン エディターがオープンソースであるかどうか 生産性を向上させる組み込み機能(コードスニペット、オートコンプリート、邪魔に