Computer >> Máy Tính >  >> Lập trình >> Ruby

Nào là nhanh nhất? ERB so với HAML và Slim

Trong bài viết này, chúng tôi sẽ kiểm tra và phân tích hiệu suất của ba công cụ tạo khuôn mẫu Ruby phổ biến nhất:ERB (công cụ mặc định), HAML và SLIM.

Đo điểm chuẩn là phương pháp so sánh các quy trình kinh doanh và số liệu hiệu suất với các phương pháp hay nhất trong ngành và các phương pháp hay nhất từ ​​các công ty khác. Trong khi đó, thử nghiệm tải là quá trình đặt nhu cầu lên hệ thống và đo lường phản ứng của nó.

Mục tiêu của chúng tôi là khám phá một chút về mô-đun Ruby Benchmark, mô-đun này cung cấp các phương pháp để đo lường và báo cáo thời gian được sử dụng để thực thi mã Ruby. Chúng tôi sẽ tạo một số mẫu nội tuyến, chạy chúng với các bài kiểm tra và trích xuất các chỉ số qua ba công cụ khác nhau.

Sau đó, chúng ta sẽ đi sâu vào kiểm tra tải bằng cách tạo một số chế độ xem mẫu trong thế giới thực, đặt chúng trực tiếp trên máy chủ và sau đó thực hiện một số kiểm tra tải thông qua công cụ hey. Nó cung cấp cho chúng tôi một số tính năng tuyệt vời để gửi tải đến một ứng dụng web cụ thể và nhận dữ liệu kỹ lưỡng liên quan đến cách mỗi điểm cuối và do đó, mỗi công cụ mẫu đang hoạt động.

Thiết lập

Tất nhiên, điều đầu tiên cần làm là đảm bảo rằng bạn đã cài đặt Ruby. Chúng tôi đang sử dụng phiên bản mới nhất, 2.7.0, kể từ khi viết bài này. Đảm bảo bạn cũng cài đặt Rails gem.

Ruby và đá quý Rails là những thứ chúng ta cần để bắt đầu. Đối với hướng dẫn này, Visual Studio Code là IDE để viết mã, nhưng hãy chọn IDE khác nếu bạn thích.

Bây giờ, chọn một thư mục cho dự án Rails của chúng tôi và chạy lệnh sau:

rails new haml-slim-erb

Thao tác này sẽ tải xuống tất cả các phụ thuộc bắt buộc và tạo dự án Rails được dàn dựng của chúng tôi. Hãy tiếp tục và khám phá nó.

Trước khi tiếp tục mã, chúng tôi cần thêm các phụ thuộc SLIM và HAML vào Gemfile của chúng tôi :

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

Cũng có một lỗi xảy ra theo mặc định với dự án có liên quan đến phiên bản SQLite. Tìm nó trong Gemfile và thay đổi nó thành như sau:

gem 'sqlite3', '~> 1.3.0'

Bây giờ, hãy chạy bundle install lệnh để tải xuống các phần phụ thuộc.

Khám phá Mô-đun điểm chuẩn

Đối với phần này của bài viết, chúng tôi sẽ làm việc trực tiếp với bài kiểm tra thư mục. Mở nó và bạn sẽ thấy một số thư mục trống. Hãy tạo một cái mới có tên là điểm chuẩn và ba tệp khác: example_1_test.rb , example_2_test.rb example_3_test.rb. .

Ruby cần kết thúc bằng kiểm tra được coi là một tệp thử nghiệm.

Tiếp theo, thêm nội dung sau vào tệp đầu tiên:

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
}

Lưu ý rằng dòng đầu tiên nhập mô-đun Điểm chuẩn bắt buộc. Sau đó, chúng tôi tạo ra một dãy số ngẫu nhiên từ 0 đến 50 với kích thước 10.000. Những con số lớn này chỉ cần một chút thời gian xử lý.

Phương pháp measure rất hữu ích vì nó có thể được đặt ở bất kỳ đâu trong mã Ruby của bạn để đo thời gian xử lý. Thời gian này được trả lại và in bởi puts .

Bên trong, chúng tôi đang lặp lại 20k lần việc thực thi cùng một mảng được tạo ngẫu nhiên để nhân một giá trị của mỗi.

Để chạy cụ thể tệp thử nghiệm này, hãy sử dụng lệnh sau:

rake test TEST=test/benchmark/example_1_test.rb

Kết quả có thể giống như sau:

0.702647   0.012353   0.715000 (  0.721910)

Báo cáo này lần lượt in ra thời gian CPU của người dùng, thời gian CPU của hệ thống, tổng thời gian của người dùng và CPU hệ thống cũng như thời gian thực đã trôi qua. Đơn vị thời gian là giây.

Chúng tôi sẽ tiếp tục sử dụng các phương pháp Điểm chuẩn khác trong thực tế.

Kiểm tra Mẫu Nội tuyến

Bây giờ bạn đã hiểu thêm một chút về cách hoạt động của mô-đun Ruby’s Benchmark, chúng ta sẽ đi sâu vào việc thực hiện một số thử nghiệm trên ba mẫu.

Đối với điều này, chúng tôi sẽ tạo một mẫu duy nhất, dịch nó thành ba cú pháp công cụ và cuối cùng, chạy nó theo phương pháp Điểm chuẩn.

Thêm phần sau vào tệp thử nghiệm thứ hai:

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

Đầu tiên, nhập các mô-đun cần thiết. Ngoài các công cụ mẫu, chúng tôi cũng đang nhập ostruct mô-đun. Một OpenStruct là một cấu trúc dữ liệu từ lập trình siêu mẫu, tương tự như một Hash , cho phép định nghĩa các thuộc tính tùy ý với các giá trị đi kèm của chúng.

Nó rất hữu ích vì chúng ta không cần phải tạo toàn bộ cấu trúc lớp để lưu trữ các giá trị. Chúng tôi có thể xác định nó nội tuyến.

Cấu trúc của chúng tôi về cơ bản là một Note đối tượng có tiêu đề, mô tả và danh sách các số ngẫu nhiên, làm tăng thời gian xử lý.

Chúng tôi sẽ không tập trung vào cách hoạt động của từng công cụ mẫu; bạn có thể tham khảo tài liệu chính thức của họ cho điều này. Tuy nhiên, cú pháp của chúng khá dễ đồng hóa.

Phép thuật được đặt ở cuối mã. Bây giờ, chúng tôi đang sử dụng bmbm phương thức này nhiều hơn một chút so với phương thức đầu tiên. Đôi khi, kết quả điểm chuẩn có thể bị sai lệch do các yếu tố bên ngoài, chẳng hạn như thu thập rác, rò rỉ bộ nhớ, v.v. Phương pháp này cố gắng giảm thiểu ảnh hưởng này bằng cách chạy thử nghiệm hai lần:lần đầu tiên là một lần diễn tập để có được môi trường thời gian chạy ổn định và lần thứ hai thời gian cho thực tế. Bạn có thể đọc thêm về điều này tại đây.

Cuối cùng, mỗi một trong số bmbm Các dòng mã bên trong thực hiện một vòng lặp 2000 lần trong quá trình tạo mỗi mẫu. Trọng tâm là gán mẫu và hiển thị nó.

Sau khi thực hiện tệp thử nghiệm này, đây là kết quả:

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)

Hai khối kết quả được tách biệt để bạn đồng hóa những gì là thực từ buổi diễn tập.

Thay đổi kịch bản một chút

Đối với kết quả thử nghiệm cuối cùng, bạn có thể cho rằng ERB là lựa chọn tốt nhất, trong khi SLIM là kém nhất. Một lần nữa, nó phụ thuộc vào tình huống.

Trong thử nghiệm đó, mỗi khi lặp lại, chúng tôi phải khởi tạo một mới đối tượng công cụ mẫu. Đây không phải là quy trình tối ưu.

Hãy thay đổi một chút và di chuyển phần thuyết minh này ra bên ngoài, như được hiển thị trong đoạn mã sau:

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

Mã thực hiện chính xác giống như trước đây. Bây giờ, hãy chạy lại các bài kiểm tra và bạn sẽ thấy kết quả như thế này:

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)

Để ý xem cái nào là tốt nhất và tệ nhất bây giờ. Điều này chỉ để chứng tỏ rằng không có viên đạn bạc nào về một động cơ hoàn hảo.

Bạn phải kiểm tra và phân tích những tính năng nào hoạt động tốt hơn cho phong cách viết mã của bạn. Ngoài ra, còn có các công cụ phụ trợ khác, chẳng hạn như Ruby Profiler, mà bạn có thể sử dụng để hiểu rõ hơn về cách thức và lý do tại sao mã hoạt động theo cách này.

Tải thử nghiệm một tình huống trong thế giới thực

Hãy chuyển sang điều gì đó gần với thực tế của chúng ta hơn. Chúng tôi sẽ đánh giá tiêu chuẩn một mẫu thực có liệt kê một số ghi chú (ba, một cho mỗi công cụ mẫu).

Kể từ benchmark mô-đun diễn ra trong mã Rails, chúng tôi mất một số biện pháp quan trọng liên quan đến các quy trình nội bộ của công cụ mẫu.

Loại kiểm tra điểm chuẩn này cho phép chúng tôi xem mỗi công cụ hoạt động như thế nào nói chung, ngay từ khi bắt đầu quy trình thông qua việc xuất hiện một yêu cầu, xử lý logic nghiệp vụ cho đến khi dữ liệu phản hồi được chuyển đến các chế độ xem. Cụ thể, bước cuối cùng này sẽ có một loạt các bước, chẳng hạn như quá trình phân tích cú pháp và kết xuất mà thử nghiệm tải có thể đo lường và benchmark không thể.

Đầu tiên, hãy tạo bộ điều khiển Rails cho từng ví dụ:

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

Lệnh này sẽ tự động tạo một loạt các tệp Rails phổ biến mà bạn có thể quen dùng.

Tiếp theo, hãy mở Notes_erb_controller.rb tệp đã tạo và thay đổi nội dung của nó thành:

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

Đây là nơi chúng tôi cung cấp dữ liệu cho các mẫu. Lưu ý rằng chúng tôi có một lớp bộ điều khiển cho mỗi công cụ.

Về cơ bản, chúng tôi đang lấy một số JSON từ một hằng số nội tuyến. Phản hồi sẽ được phân tích cú pháp thành một OpenStruct mới đối tượng và quay trở lại động cơ.

NOTES hằng số phải được đặt vào một tệp mới có tên là constants.rb . Hãy tiếp tục và tạo nó, sau đó thêm nội dung sau:

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

Hãy đảm bảo thay đổi dấu chấm lửng để có thêm các yếu tố ghi chú. Ngoài ra, đừng quên sao chép logic trong từng bộ điều khiển khác.

Tạo Chế độ xem ERB

Bây giờ, đã đến lúc tạo các chế độ xem ví dụ của chúng ta. Để làm như vậy, hãy truy cập views/notes_erb và tạo hai tệp khác:note.html.erbtask.html.erb . Chúng sẽ giúp chúng tôi xây dựng một ví dụ chứa các chế độ xem, các phần và bố cục.

Bằng cách này, chúng tôi đảm bảo rằng ví dụ của chúng tôi đã khám phá các công cụ Rails nhiều nhất. Đảm bảo tạo các tệp tương đương với cả views/notes_hamlviews/notes_slim .

Hãy bắt đầu với index.html.erb mã:

<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>

Không có gì quá đặc biệt ở đây. Lưu ý rằng chúng tôi đang nhập một phần, _note.html.erb và chuyển collection tham số:@notes của chúng tôi trước đó đã được tạo trong bộ điều khiển.

Nhân tiện, đây là nội dung của ghi chú:

<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>

Đây là một phần khác, truy cập tasks mảng lần này.

Nội dung cho _task.html.erb như sau:

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

Lượt xem của HAML

Bạn sẽ nhận thấy rằng cú pháp từ động cơ này sang động cơ khác rất giống nhau. Những gì thay đổi chỉ là độ dài của từng cái. Ví dụ:SLIM là cái sạch nhất trong số đó.

Hãy xem mã cho ba tệp:

# 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

Rất giống, phải không?

Số lượt xem của SLIM

Cuối cùng, chúng tôi có quan điểm của SLIM. Đây là sự khác biệt lớn nhất so với hai cái còn lại. Toàn bộ cấu trúc trở nên rõ ràng hơn:

# 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

Bạn cũng sẽ phải dịch các bố cục sang từng cú pháp công cụ tương ứng. Hai tệp mới phải được tạo trong chế độ xem / bố cục thư mục: application.html.haml application.html.slim .

Tôi sẽ giao công việc đó cho bạn làm bài tập về nhà. Tuy nhiên, nếu cảm thấy khó khăn, bạn có thể tham khảo phiên bản của tôi trong liên kết dự án GitHub ở cuối bài viết.

Chạy Kiểm tra

Cuối cùng, chúng tôi có thể kiểm tra ví dụ. Đầu tiên, khởi động ứng dụng bằng cách chạy rails s yêu cầu. Nó sẽ bắt đầu tại địa chỉ https:// localhost:3000 /.

Đây là chế độ xem sẽ như thế nào:

Nào là nhanh nhất? ERB so với HAML và Slim

Mỗi ví dụ về công cụ mẫu sẽ có sẵn tại URL tương ứng được tạo tự động trong config / route.rb tệp.

Để kiểm tra điểm chuẩn các ví dụ này, chúng tôi sẽ sử dụng công cụ điểm chuẩn hey. Nó rất đơn giản và cung cấp một số thông tin hữu ích để phân tích điểm chuẩn. Đây là các lệnh:

$ 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

Như bạn có thể thấy, tất cả các engine đều rất gần nhau về thời gian thực thi. Số lượng yêu cầu mặc định để chạy là 200, nhưng bạn có thể thay đổi giá trị này thông qua -n tùy chọn.

Hãy cùng xem xét các thử nghiệm tương tự được thực hiện với 1200 yêu cầu:

$ 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

Khi bạn tăng số lượng yêu cầu đồng thời, bạn sẽ thấy sự khác biệt về tổng số và thời gian xử lý trung bình tăng lên. Rõ ràng, kịch bản này, với hàng nghìn yêu cầu song song, là rất cụ thể và không phổ biến lắm. Tuy nhiên, kiểm tra tải là tất cả về điều đó, đưa các điểm cuối đến giới hạn.

Công cụ hey cũng in thông tin khác, chẳng hạn như biểu đồ thời gian phản hồi:

Nào là nhanh nhất? ERB so với HAML và Slim

Nó cho biết thời gian trung bình mỗi yêu cầu cần để hoàn thành theo các điều khoản gần đúng. Trong ví dụ của chúng tôi, rõ ràng là hầu hết các yêu cầu (1048) đã được hoàn thành trong 1,893 giây. Đây là lý do tại sao bạn nên thực hiện các bài kiểm tra căng thẳng cùng một lúc.

Ngoài ra còn có thêm thông tin về phân phối độ trễ, chi tiết về quay số và tra cứu DNS, ghi yêu cầu, thời gian chờ và đọc, lỗi, v.v.

Kiểm tra tài liệu để biết thêm các tùy chọn / kết quả tùy chỉnh.

Tóm tắt

Bạn có thể tìm mã nguồn cho ví dụ này tại đây.

Loại thử nghiệm này rất tốt để giúp bạn xác định công cụ mẫu nào có thể phù hợp hơn với nhu cầu dự án của bạn. Như chúng ta đã thấy, hãy cẩn thận về mã được triển khai kém vì một số đoạn mã nhỏ có thể đột ngột thay đổi hiệu suất tổng thể trong quá trình thực thi của bạn.

Một điểm thú vị khác là ERB, với tư cách là công cụ Rails mặc định, cũng rất tốt trong công việc của nó. Bên cạnh các động cơ khác nhanh hơn trong một số thử nghiệm của chúng tôi, ERB luôn đủ gần để chứng minh giá trị của nó.

Cuối cùng, tôi khuyên bạn nên coi một số yếu tố quan trọng khác trong các bài kiểm tra như một nhiệm vụ bài tập về nhà, bao gồm bộ nhớ đệm, proxy, việc sử dụng cơ sở dữ liệu và các cơ chế lưu trữ khác, cũng như hàng đợi và bất kỳ công cụ nào có tính chất không đồng bộ như vậy. Các mục này luôn đóng một vai trò quan trọng trong cách các chế độ xem của bạn hoạt động hoặc thời gian xử lý để hiển thị chúng. Chúc bạn thành công!