Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

어느 것이 가장 빠릅니까? ERB 대 HAML 대 슬림

이 기사에서는 가장 인기 있는 세 가지 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.rbexample_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.erbtask.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.hamlapplication.html.slim .

그 일은 너에게 숙제로 맡길게. 하지만 어렵다면 기사 마지막 부분에 있는 GitHub 프로젝트 링크에서 내 버전을 참조할 수 있습니다.

테스트 실행

마지막으로 예제를 테스트합니다. 먼저 rails s를 실행하여 애플리케이션을 시작합니다. 명령. https://localhost:3000/ 주소에서 시작합니다.

보기는 다음과 같습니다.

어느 것이 가장 빠릅니까? ERB 대 HAML 대 슬림

각 템플릿 엔진 예제는 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 도구는 응답 시간 히스토그램과 같은 다른 정보도 인쇄합니다.

어느 것이 가장 빠릅니까? ERB 대 HAML 대 슬림

각 요청을 완료하는 데 걸린 평균 시간을 대략적으로 보여줍니다. 이 예에서 대부분의 요청(1048)이 1.893초 내에 완료되었음을 알 수 있습니다. 이것이 스트레스 테스트를 동시에 수행하는 것이 바람직한 이유입니다.

대기 시간 분포, DNS 전화 접속 및 조회에 대한 세부 정보, 쓰기 요청, 대기 및 읽기 시간, 오류 등에 대한 자세한 정보도 있습니다.

더 많은 맞춤 옵션/결과는 문서를 확인하세요.

요약

이 예제의 소스 코드는 여기에서 찾을 수 있습니다.

이러한 종류의 테스트는 프로젝트 요구 사항에 더 적합한 템플릿 엔진을 정의하는 데 유용합니다. 지금까지 살펴본 것처럼 일부 작은 코드 조각이 전체 실행 성능을 갑자기 변경할 수 있으므로 제대로 구현되지 않은 코드에 주의하십시오.

또 다른 흥미로운 점은 기본 Rails 엔진인 ERB가 제 역할을 한다는 것입니다. 일부 테스트에서 더 빠른 다른 엔진 외에도 ERB는 항상 그 가치를 증명할 만큼 충분히 가깝습니다.

마지막으로 캐싱, 프록시, 데이터베이스 사용 및 기타 저장 메커니즘, 대기열 및 이러한 비동기 도구를 포함하여 테스트의 다른 중요한 요소를 숙제로 고려하는 것이 좋습니다. 이러한 항목은 뷰가 동작하는 방식이나 뷰를 렌더링하는 데 걸리는 처리 시간에서 항상 중요한 역할을 합니다. 행운을 빕니다!