이 기사에서는 가장 인기 있는 세 가지 Ruby 템플릿 엔진인 ERB(기본값), HAML 및 SLIM의 성능을 테스트하고 분석합니다.
벤치마킹은 비즈니스 프로세스 및 성능 메트릭을 업계 최고 및 다른 회사의 모범 사례와 비교하는 관행입니다. 한편, 부하 테스팅은 시스템에 요구를 가하고 응답을 측정하는 과정입니다.
우리의 목표는 Ruby 코드를 실행하는 데 사용된 시간을 측정하고 보고하는 방법을 제공하는 Ruby Benchmark 모듈을 약간 탐색하는 것입니다. 몇 가지 인라인 템플릿을 만들고 테스트에 대해 실행하고 세 가지 다른 엔진에서 측정항목을 추출합니다.
그런 다음 실제 템플릿 보기를 만들어 서버에 라이브로 배치한 다음 Hey 도구를 통해 몇 가지 부하 테스트를 수행하여 부하 테스트에 뛰어들 것입니다. 특정 웹 애플리케이션에 로드를 보내고 각 엔드포인트 및 이에 따른 각 템플릿 엔진의 성능과 관련된 철저한 데이터를 얻을 수 있는 몇 가지 훌륭한 기능을 제공합니다.
설정
물론 가장 먼저 해야 할 일은 Ruby가 이미 설치되어 있는지 확인하는 것입니다. 우리는 이 글을 쓰는 시점에서 가장 최신 버전인 2.7.0을 사용하고 있습니다. Rails gem도 설치해야 합니다.
Ruby와 Rails gem은 시작하는 데 필요한 것입니다. 이 자습서의 경우 Visual Studio Code는 코딩용 IDE이지만 원하는 경우 다른 것을 자유롭게 선택하십시오.
이제 Rails 프로젝트의 폴더를 선택하고 다음 명령을 실행합니다.
rails new haml-slim-erb
그러면 필요한 모든 종속성이 다운로드되고 스캐폴딩된 Rails 프로젝트가 생성됩니다. 계속해서 탐색하십시오.
코드를 진행하기 전에 Gemfile에 SLIM 및 HAML 종속성을 추가해야 합니다. :
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
를 실행합니다. 종속성을 다운로드하는 명령입니다.
벤치마크 모듈 탐색
기사의 이 부분에서는 테스트 폴더. 그것을 열면 몇 개의 빈 폴더가 보일 것입니다. benchmark라는 새 항목을 만들어 보겠습니다. 및 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
}
첫 번째 줄은 필요한 Benchmark 모듈을 가져옵니다. 그런 다음 10.000 크기의 0에서 50까지의 숫자로 구성된 임의의 배열을 생성합니다. 이 큰 숫자는 처리하는 데 약간의 시간이 걸립니다.
measure
방법 처리하는 데 걸리는 시간을 측정하기 위해 Ruby 코드의 아무 곳에나 배치할 수 있기 때문에 매우 유용합니다. 이 시간은 puts
에 의해 반환되고 인쇄됩니다. .
내부에서 동일한 무작위 생성 배열을 20,000번 반복하여 각각의 값을 하나씩 곱합니다.
이 테스트 파일을 구체적으로 실행하려면 다음 명령을 실행하십시오.
rake test TEST=test/benchmark/example_1_test.rb
결과는 다음과 같습니다.
0.702647 0.012353 0.715000 ( 0.721910)
이 보고서는 각각 사용자 CPU 시간, 시스템 CPU 시간, 사용자와 시스템 CPU 시간의 합, 경과된 실시간 시간을 인쇄합니다. 시간 단위는 초입니다.
다른 벤치마크 방법을 실제로 더 많이 사용할 것입니다.
인라인 템플릿 테스트
이제 Ruby의 Benchmark 모듈이 작동하는 방식에 대해 조금 더 이해했으므로 세 가지 템플릿에 대한 몇 가지 테스트 실행에 대해 알아보겠습니다.
이를 위해 단일 템플릿을 생성하고 3가지 엔진 구문으로 변환한 다음 마지막으로 Benchmark 방식으로 실행합니다.
두 번째 테스트 파일에 다음을 추가합니다.
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
을 사용하고 있습니다. 첫 번째 방법보다 약간 더 많은 작업을 수행하는 방법입니다. 때때로 가비지 수집, 메모리 누수 등과 같은 외부 요인으로 인해 벤치마크 결과가 왜곡될 수 있습니다. 이 방법은 테스트를 두 번 실행하여 이 영향을 최소화하려고 시도합니다. 첫 번째는 런타임 환경을 안정시키기 위한 리허설로, 두 번째는 진짜 시간. 여기에서 자세한 내용을 읽을 수 있습니다.
마지막으로 각 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)
리허설에서 실제 내용을 이해할 수 있도록 두 개의 결과 블록이 분리되어 있습니다.
시나리오를 약간 변경
마지막 테스트 결과의 경우 ERB가 최상의 옵션이고 SLIM이 최악의 옵션이라고 가정할 수 있습니다. 다시 말하지만 상황에 따라 다릅니다.
이 테스트에서는 루프를 돌릴 때마다 new를 인스턴스화해야 합니다. 템플릿 엔진 개체. 이것은 최적의 흐름이 아닙니다.
다음 코드 스니펫과 같이 약간 변경하고 이 인스턴스화를 외부로 이동해 보겠습니다.
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개).
benchmark
이후 모듈이 Rails 코드에서 발생하면 템플릿 엔진 내부 프로세스와 관련된 몇 가지 중요한 측정값을 잃게 됩니다.
이러한 유형의 벤치마크 테스트를 통해 프로세스의 맨 처음부터 요청 도착, 응답 데이터가 뷰에 도달할 때까지 비즈니스 로직 처리에 이르기까지 각 엔진이 전체적으로 어떻게 수행되는지 확인할 수 있습니다. 특히 이 마지막 단계에는 부하 테스트가 측정할 수 있는 구문 분석 및 렌더링 프로세스, benchmark
와 같은 여러 단계가 있습니다. 할 수 없습니다.
먼저 각 예제에 대해 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
여기에서 템플릿에 데이터를 제공합니다. 각 엔진에 대해 하나의 컨트롤러 클래스가 있습니다.
기본적으로 인라인 상수에서 일부 JSON을 가져옵니다. 응답은 새로운 OpenStruct
로 구문 분석됩니다. 개체 및 엔진으로 반환됩니다.
NOTES
상수는 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
로 이동하십시오. 폴더를 만들고 두 개의 다른 파일을 만듭니다. 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
를 가져오고 있습니다. 파일 및 collection
전달 매개변수:@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>
다음은 tasks
에 액세스하는 또 다른 부분입니다. 이번에는 배열입니다.
_task.html.erb
의 콘텐츠 다음은 다음과 같습니다.
<li><%= task.title %></li>
HAML의 보기
한 엔진에서 다른 엔진으로의 구문이 매우 유사함을 알 수 있습니다. 변화하는 것은 각각의 장황함뿐입니다. 예를 들어, SLIM은 그 중 가장 깨끗한 것입니다.
세 파일의 코드를 살펴보십시오.
# 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의 견해가 있습니다. 여기에 다른 두 가지와 가장 큰 차이점이 있습니다. 전체 구조가 더 명확해집니다.
# 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
또한 레이아웃을 각각의 엔진 구문으로 번역해야 합니다. 보기/레이아웃 아래에 두 개의 새 파일을 만들어야 합니다. 폴더:application.html.haml 및 application.html.slim .
그 일은 너에게 숙제로 맡길게. 하지만 어렵다면 기사 마지막 부분에 있는 GitHub 프로젝트 링크에서 내 버전을 참조할 수 있습니다.
테스트 실행
마지막으로 예제를 테스트합니다. 먼저 rails s
를 실행하여 애플리케이션을 시작합니다. 명령. https://localhost:3000/ 주소에서 시작합니다.
보기는 다음과 같습니다.
각 템플릿 엔진 예제는 config/routes.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 도구는 응답 시간 히스토그램과 같은 다른 정보도 인쇄합니다.
각 요청을 완료하는 데 걸린 평균 시간을 대략적으로 보여줍니다. 이 예에서 대부분의 요청(1048)이 1.893초 내에 완료되었음을 알 수 있습니다. 이것이 스트레스 테스트를 동시에 수행하는 것이 바람직한 이유입니다.
대기 시간 분포, DNS 전화 접속 및 조회에 대한 세부 정보, 쓰기 요청, 대기 및 읽기 시간, 오류 등에 대한 자세한 정보도 있습니다.
더 많은 맞춤 옵션/결과는 문서를 확인하세요.
요약
이 예제의 소스 코드는 여기에서 찾을 수 있습니다.
이러한 종류의 테스트는 프로젝트 요구 사항에 더 적합한 템플릿 엔진을 정의하는 데 유용합니다. 지금까지 살펴본 것처럼 일부 작은 코드 조각이 전체 실행 성능을 갑자기 변경할 수 있으므로 제대로 구현되지 않은 코드에 주의하십시오.
또 다른 흥미로운 점은 기본 Rails 엔진인 ERB가 제 역할을 한다는 것입니다. 일부 테스트에서 더 빠른 다른 엔진 외에도 ERB는 항상 그 가치를 증명할 만큼 충분히 가깝습니다.
마지막으로 캐싱, 프록시, 데이터베이스 사용 및 기타 저장 메커니즘, 대기열 및 이러한 비동기 도구를 포함하여 테스트의 다른 중요한 요소를 숙제로 고려하는 것이 좋습니다. 이러한 항목은 뷰가 동작하는 방식이나 뷰를 렌더링하는 데 걸리는 처리 시간에서 항상 중요한 역할을 합니다. 행운을 빕니다!