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 và 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.erb
và task.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_haml
và views/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 và 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:
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ó 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!