Ruby의 표준 라이브러리에는 코드 성능을 측정하는 데 도움이 되는 벤치마킹 도구가 있습니다. 두 구현을 비교할 때 어느 것이 가장 빠른지 알아내는 것이 가장 유용합니다.
이 예에서는 문자열 키(예:{"foo" => "bar"}
)를 사용하여 해시를 변환해야 합니다. 기호가 있는 것(예:{:foo => "bar"}
). 예제 전체에서 영어 알파벳의 각 문자에 대해 키와 값이 있는 해시를 사용합니다.
이 해시를 입력하지 않고 빠르게 생성하기 위해 문자 범위를 테스트 해시로 변환합니다. input
에 넣겠습니다. 나중에 사용할 변수입니다.
input = ("a".."z").map {|letter| [letter, letter]}.to_h
# => {"a"=>"a", "b"=>"b", "c"=>"c", "d"=>"d", "e"=>"e", "f"=>"f", "g"=>"g", "h"=>"h", "i"=>"i", "j"=>"j", "k"=>"k", "l"=>"l", "m"=>"m", "n"=>"n", "o"=>"o", "p"=>"p", "q"=>"q", "r"=>"r", "s"=>"s", "t"=>"t", "u"=>"u", "v"=>"v", "w"=>"w", "x"=>"x", "y"=>"y", "z"=>"z"}
이제 input
이 있습니다. 구현을 테스트할 변수를 사용하여 성능을 확인하기 위해 하나를 작성합니다. 입력 해시의 모든 키를 문자열 대신 기호로 변환하는 멋진 한 줄짜리는 다음과 같습니다.
input.map { |key, value| [key.to_sym, value] }.to_h
# => {:a=>"a", :b=>"b", :c=>"c", :d=>"d", :e=>"e", :f=>"f", :g=>"g", :h=>"h", :i=>"i", :j=>"j", :k=>"k", :l=>"l", :m=>"m", :n=>"n", :o=>"o", :p=>"p", :q=>"q", :r=>"r", :s=>"s", :t=>"t", :u=>"u", :v=>"v", :w=>"w", :x=>"x", :y=>"y", :z=>"z"}
이 구현은 map
을 사용합니다. 각 키-값 쌍에 대해 블록을 실행하기 위해 해시를 반복하는 메서드입니다. 블록에서 키를 기호로 변환하고 새로 생성된 기호 키와 손대지 않은 값을 포함하는 2요소 배열을 반환합니다.
map
의 결과 command는 26개의 키-값 배열이 있는 배열입니다. 해시가 필요하므로 #to_h
를 사용합니다. 새 배열을 다시 해시로 변환합니다.
벤치마크.측정
이제 작동하는 구현이 있으므로 Ruby의 Benchmark 모듈을 사용하여 성능을 확인할 수 있습니다.
require 'benchmark'
input = ('a'..'z').map { |letter| [letter, letter] }.to_h
puts Benchmark.measure {
50_000.times do
input.map { |key, value| [key.to_sym, value] }.to_h
end
}
Benchmark.measure
실행하는 데 걸린 시간을 추적하면서 실행되는 블록을 사용합니다. puts
를 사용하여 콘솔에 인쇄되는 보고서 문자열을 반환합니다. .
이것은 빠른 코드 조각이므로 가시적인 결과를 얻기 위해 50,000번 실행합니다.
$ ruby bench.rb
0.810000 0.000000 0.810000 ( 0.816964)
보고서 문자열은 사용자 CPU 시간을 나타내는 4개의 숫자를 보여줍니다. (코드를 실행하는 데 소요된 시간), 시스템 CPU 시간 (커널에서 보낸 시간), 사용자 및 시스템 CPU 시간을 모두 합산하고 블록이 실행되는 데 걸린 실제 시간(또는 벽시계 시간)을 괄호 안에 표시합니다.
실제 시간은 800밀리초가 조금 넘는 시간에 50,000번 이상의 코드 블록을 실행할 수 있음을 보여줍니다. 인상적인 수치지만 코드의 다른 구현과 비교하지 않는 한 이것이 무엇을 의미하는지 모릅니다.
Benchmark.bm
Benchmark.measure
외에 , Ruby는 Benchmark.bm
을 제공합니다. , 여러 코드 샘플을 실행하고 결과를 인쇄할 수 있습니다. 각 샘플에 대해 Benchmark#report
라고 합니다. 이름과 실행할 블록.
require 'benchmark'
input = ("a".."z").map { |letter| [letter, letter] }.to_h
n = 50_000
Benchmark.bm do |benchmark|
benchmark.report("Hash[]") do
n.times do
input.map { |key, value| [key.to_sym, value] }.to_h
end
end
benchmark.report("{}.tap") do
n.times do
{}.tap do |new_hash|
input.each do |key, value|
new_hash[key.to_sym] = value
end
end
end
end
end
이 벤치마크에서는 Benchmark.bm
을 사용합니다. 각각 50,000번씩 실행하여 두 가지 구현을 테스트합니다. 첫 번째 측정 블록은 이전 예제와 동일합니다.
두 번째 측정 블록에서는 새로운 해시를 미리 생성하는 더 긴 구현을 사용합니다. 문자열 키 해시를 반복하고 모든 항목의 새 해시에 요소를 추가합니다. 이렇게 하면 해시를 배열로 변환하고 완료되면 다시 해시로 변환할 필요가 없습니다.
벤치마크를 다시 실행하면 이 구현이 25% 이상 더 빠르다는 것을 알 수 있지만 이전에 시도한 한 줄짜리 코드보다 코드가 더 길고 영리하지 않습니다.
$ ruby bench.rb
user system total real
Hash[] 0.850000 0.000000 0.850000 ( 0.851106)
{}.tap 0.610000 0.020000 0.630000 ( 0.637070)
더 많은 벤치마킹
코드베이스에서 중요한 코드를 작업할 때 벤치마크를 실행하여 다른 구현을 비교하면 실행 속도에 대한 더 많은 통찰력을 얻을 수 있습니다. 여러 구현을 비교하여 성능에 미치는 영향을 이해하면 안티 패턴을 방지하고 더 빠른 Ruby를 작성할 수 있습니다.
팁 :많은 일반적인 관용구가 미리 벤치마킹되어 있으며 그 결과가 fast ruby로 게시됩니다. 예제를 읽으면 향후 벤치마킹을 줄일 수 있습니다.
이 예제에 대해 테스트할 수 있는 더 많은 옵션이 있으며 Ruby의 벤치마킹 라이브러리에는 시도할 수 있는 훨씬 더 정교한 기능이 있지만 이것은 Ruby에서 벤치마킹이 작동하는 방식에 대한 좋은 소개를 제공합니다. 벤치마킹에 대해 더 알고 싶거나 질문이나 제안 사항이 있으면 @AppSignal로 알려주십시오.