루비에서 작은 따옴표 대신 큰 따옴표를 사용하면 루비 1.8 및 1.9에서 의미있는 방식으로 성능이 저하되는지 알 수 있습니다.
내가 입력하면
question = 'my question'
보다 빠르다
question = "my question"
루비가 큰 따옴표를 만났을 때 무언가를 평가 해야하는지 알아 내려고하고 아마도 그 일을하는 데 약간의주기를 소비한다고 생각합니다.
루비에서 작은 따옴표 대신 큰 따옴표를 사용하면 루비 1.8 및 1.9에서 의미있는 방식으로 성능이 저하되는지 알 수 있습니다.
내가 입력하면
question = 'my question'
보다 빠르다
question = "my question"
루비가 큰 따옴표를 만났을 때 무언가를 평가 해야하는지 알아 내려고하고 아마도 그 일을하는 데 약간의주기를 소비한다고 생각합니다.
답변:
$ ruby -v
ruby 1.9.3p0 (2011-10-30 revision 33570) [x86_64-darwin11.0.0]
$ cat benchmark_quotes.rb
# As of Ruby 1.9 Benchmark must be required
require 'benchmark'
n = 1000000
Benchmark.bm(15) do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
end
$ ruby benchmark_quotes.rb
user system total real
assign single 0.110000 0.000000 0.110000 ( 0.116867)
assign double 0.120000 0.000000 0.120000 ( 0.116761)
concat single 0.280000 0.000000 0.280000 ( 0.276964)
concat double 0.270000 0.000000 0.270000 ( 0.278146)
참고 :이 최신 Ruby 버전에서 작동하도록하고 헤더를 정리하고 더 빠른 시스템에서 벤치 마크를 실행하도록이를 업데이트했습니다.
이 답변은 몇 가지 핵심 사항을 생략합니다. 보간에 관한 이러한 다른 답변 과 작은 따옴표와 큰 따옴표를 사용할 때 성능에 큰 차이가없는 이유를 참조하십시오 .
'
그리고 "
그들은 같은 일을 구문 분석으로는.
요약 : 속도 차이 없음; 이 훌륭한 협업 Ruby 스타일 가이드 는 일관성을 권장합니다. 'string'
보간이 필요하지 않으면 (가이드의 옵션 A) 좋아하지 않는 한 사용 하지만 일반적으로와 함께 더 많은 코드가 표시됩니다 "string"
.
세부:
이론적으로 코드를 구문 분석 할 때 차이가 생길 수 있지만 일반적인 구문 분석 시간 (실행 시간과 비교할 수없는)에 신경 쓰지 않아야 할뿐만 아니라이 경우 큰 차이를 찾을 수 없습니다.
중요한 것은 도착 때이다 실행 이 될 것입니다 정확히 같은 .
이것을 벤치마킹하면 Ruby의 작동 방식에 대한 이해가 부족하다는 것을 보여줍니다. 두 경우 모두 문자열이로 구문 분석됩니다 tSTRING_CONTENT
( 의 소스parse.y
참조 ). 만들 때 즉, CPU는 동일한 작업을 거치게됩니다 'string'
나 "string"
. 똑같은 비트는 똑같은 방식으로 뒤집습니다. 벤치마킹은 중요하지 않고 다른 요인 (GC 발 차기 등)으로 인한 차이 만 보여줍니다. 이 경우에는 차이가 없습니다. 이와 같은 마이크로 벤치 마크는 이해하기 어렵습니다. 이것 fruity
에 대한 적절한 도구는 내 보석 을 참조하십시오 .
폼의 보간이 있다면 "...#{...}..."
, 이것은 각 표현식에 대한 tSTRING_DBEG
여러 개의 파싱 과 마지막에 파싱됩니다 . 그러나 보간이있는 경우에만 가능합니다 .OP가 아닙니다.tSTRING_DVAR
#{...}
tSTRING_DEND
나는 당신이 어디에서나 큰 따옴표를 사용하도록 제안 #{some_var}
했지만 (나중에 실제로 추가하기가 더 쉬워 집니다) 보간 \n
등이 필요하지 않으면 작은 따옴표를 사용합니다. 문자열에 구문이 있어야 표현식이 포함되어 있는지 확인할 수 있습니다.
#{n}
숫자 변환 중임을 강조했습니다 ). 구문 분석의 차이점이 표시되지 않습니까?
연결 대 보간을 측정하는 사람은 아무도 없었습니다.
$ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-darwin9.6.2]
$ cat benchmark_quotes.rb
require 'benchmark'
n = 1000000
Benchmark.bm do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("assign interp") { n.times do; c = "a string #{'b string'}"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
end
$ ruby -w benchmark_quotes.rb
user system total real
assign single 2.600000 1.060000 3.660000 ( 3.720909)
assign double 2.590000 1.050000 3.640000 ( 3.675082)
assign interp 2.620000 1.050000 3.670000 ( 3.704218)
concat single 3.760000 1.080000 4.840000 ( 4.888394)
concat double 3.700000 1.070000 4.770000 ( 4.818794)
구체적으로 assign interp = 2.62
vs 에 유의하십시오 concat single = 3.76
. 케이크에 착빙 할 때, 'a' + var + 'b'
특히 보간이 특히 공간 보다 더 읽기 쉽습니다.
#{some_var}
스타일 문자열 보간을 사용하지 않는 한 차이는 없습니다 . 그러나 실제로 그렇게하면 성능이 저하됩니다.
Zetetic의 예 에서 수정 :
require 'benchmark'
n = 1000000
Benchmark.bm do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("assign interp") { n.times do; c = "a #{n} string"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
x.report("concat interp") { n.times do; "a #{n} string " + "b #{n} string"; end}
end
산출
user system total real
assign single 0.370000 0.000000 0.370000 ( 0.374599)
assign double 0.360000 0.000000 0.360000 ( 0.366636)
assign interp 1.540000 0.010000 1.550000 ( 1.577638)
concat single 1.100000 0.010000 1.110000 ( 1.119720)
concat double 1.090000 0.000000 1.090000 ( 1.116240)
concat interp 3.460000 0.020000 3.480000 ( 3.535724)
어휘 분석기는 #{}
보간 마커 를 확인할 필요가 없기 때문에 작은 따옴표는 큰 따옴표보다 약간 빠를 수 있습니다 . 구현 등에 따라 달라집니다. 이는 런타임 비용이 아니라 파싱 시간 비용입니다.
즉, 실제 질문은 큰 따옴표로 묶인 문자열을 사용하는 것이 "의미있는 방식으로 성능을 저하시키는 지"여부이며, 그 대답은 결정적인 "아니오"입니다. 성능의 차이는 매우 작기 때문에 실제 성능 문제와 비교할 때 완전히 중요하지 않습니다. 시간을 낭비하지 마십시오.
실제 보간은 물론 다른 이야기입니다. 'foo'
보다 정확히 1 초 빠릅니다 "#{sleep 1; nil}foo"
.
큰 따옴표는 작은 따옴표보다 두 배나 많은 키를칩니다. 나는 항상 서두르고 있습니다. 작은 따옴표를 사용합니다. :) 그리고 네, 나는 "성능 향상"이라고 생각합니다. :)
1.8.7과 1.9.2의 비교를 추가 할 것이라고 생각했습니다. 나는 그들을 몇 번 달렸다. 분산은 약 + -0.01이었다.
require 'benchmark'
n = 1000000
Benchmark.bm do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("assign interp") { n.times do; c = "a #{n} string"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
x.report("concat interp") { n.times do; "a #{n} string " + "b #{n} string"; end}
end
루비 1.8.7 (2010-08-16 패치 레벨 302) [x86_64-linux]
assign single 0.180000 0.000000 0.180000 ( 0.187233)
assign double 0.180000 0.000000 0.180000 ( 0.187566)
assign interp 0.880000 0.000000 0.880000 ( 0.877584)
concat single 0.550000 0.020000 0.570000 ( 0.567285)
concat double 0.570000 0.000000 0.570000 ( 0.570644)
concat interp 1.800000 0.010000 1.810000 ( 1.816955)
루비 1.9.2p0 (2010-08-18 개정 29036) [x86_64-linux]
user system total real
assign single 0.140000 0.000000 0.140000 ( 0.144076)
assign double 0.130000 0.000000 0.130000 ( 0.142316)
assign interp 0.650000 0.000000 0.650000 ( 0.656088)
concat single 0.370000 0.000000 0.370000 ( 0.370663)
concat double 0.370000 0.000000 0.370000 ( 0.370076)
concat interp 1.420000 0.000000 1.420000 ( 1.412210)
또한 작은 따옴표로 묶인 문자열이 Ruby에 대해 더 빨리 구문 분석 될 수 있다고 생각했습니다. 사실이 아닌 것 같습니다.
어쨌든, 위의 벤치 마크가 잘못된 것을 측정한다고 생각합니다. 어느 쪽 버전이 동일한 내부 문자열 표현으로 구문 분석되어 구문 분석이 더 빠른지에 대한 답을 얻으려면 문자열 변수로 성능을 측정하지 말고 Ruby의 문자열 구문 분석 속도를 측정해야합니다.
generate.rb:
10000.times do
('a'..'z').to_a.each {|v| print "#{v}='This is a test string.'\n" }
end
#Generate sample ruby code with lots of strings to parse
$ ruby generate.rb > single_q.rb
#Get the double quote version
$ tr \' \" < single_q.rb > double_q.rb
#Compare execution times
$ time ruby single_q.rb
real 0m0.978s
user 0m0.920s
sys 0m0.048s
$ time ruby double_q.rb
real 0m0.994s
user 0m0.940s
sys 0m0.044s
반복되는 달리기는 큰 차이를 보이지 않는 것 같습니다. 두 버전의 문자열을 구문 분석하는 데 여전히 거의 같은 시간이 걸립니다.
구현에 따라 가능하지만 통역사의 스캔 부분은 각 문자를 한 번만 봐야합니다. # {} 블록을 처리하기 위해 추가 상태 (또는 가능한 상태 세트)와 전환이 필요합니다.
테이블 기반 스캐너에서 전환을 결정하기위한 단일 조회이며 어쨌든 각 문자에 대해 발생합니다.
파서는 스캐너 출력을 얻었을 때 이미 블록에서 코드를 평가해야한다는 것을 알고 있습니다. 따라서 오버 헤드는 실제로 스캐너 / 파서의 # {} 블록을 처리하기위한 메모리 오버 헤드 일뿐입니다.
내가 빠뜨리지 않는 한 (또는 컴파일러 구성 세부 정보를 잘못 기억하지 않는 한) 확실히 가능합니다 :)
~ > ruby -v
jruby 1.6.7 (ruby-1.8.7-p357) (2012-02-22 3e82bc8) (Java HotSpot(TM) 64-Bit Server VM 1.6.0_37) [darwin-x86_64-java]
~ > cat qu.rb
require 'benchmark'
n = 1000000
Benchmark.bm do |x|
x.report("assign single") { n.times do; c = 'a string'; end}
x.report("assign double") { n.times do; c = "a string"; end}
x.report("concat single") { n.times do; 'a string ' + 'b string'; end}
x.report("concat double") { n.times do; "a string " + "b string"; end}
end
~ > ruby qu.rb
user system total real
assign single 0.186000 0.000000 0.186000 ( 0.151000)
assign double 0.062000 0.000000 0.062000 ( 0.062000)
concat single 0.156000 0.000000 0.156000 ( 0.156000)
concat double 0.124000 0.000000 0.124000 ( 0.124000)
모두 놓친 것이 있습니다.
여기 의사
이 시도
require 'benchmark'
mark = <<EOS
a string
EOS
n = 1000000
Benchmark.bm do |x|
x.report("assign here doc") {n.times do; mark; end}
end
그것은 나에게 주었다
`asign here doc 0.141000 0.000000 0.141000 ( 0.140625)`
과
'concat single quotes 1.813000 0.000000 1.813000 ( 1.843750)'
'concat double quotes 1.812000 0.000000 1.812000 ( 1.828125)'
concat하고 모든 것을 쓰는 것보다 확실히 낫습니다.
루비가 문서 조작 언어의 라인을 따라 더 많은 것을 배우기를 원합니다.
결국 Rails, Sinatra 및 테스트 실행에서 실제로 그렇게하지 않습니까?
Tim Snowhite의 답변을 수정했습니다.
require 'benchmark'
n = 1000000
attr_accessor = :a_str_single, :b_str_single, :a_str_double, :b_str_double
@a_str_single = 'a string'
@b_str_single = 'b string'
@a_str_double = "a string"
@b_str_double = "b string"
@did_print = false
def reset!
@a_str_single = 'a string'
@b_str_single = 'b string'
@a_str_double = "a string"
@b_str_double = "b string"
end
Benchmark.bm do |x|
x.report('assign single ') { n.times do; c = 'a string'; end}
x.report('assign via << single') { c =''; n.times do; c << 'a string'; end}
x.report('assign double ') { n.times do; c = "a string"; end}
x.report('assing interp ') { n.times do; c = "a string #{'b string'}"; end}
x.report('concat single ') { n.times do; 'a string ' + 'b string'; end}
x.report('concat double ') { n.times do; "a string " + "b string"; end}
x.report('concat single interp') { n.times do; "#{@a_str_single}#{@b_str_single}"; end}
x.report('concat single << ') { n.times do; @a_str_single << @b_str_single; end}
reset!
# unless @did_print
# @did_print = true
# puts @a_str_single.length
# puts " a_str_single: #{@a_str_single} , b_str_single: #{@b_str_single} !!"
# end
x.report('concat double interp') { n.times do; "#{@a_str_double}#{@b_str_double}"; end}
x.report('concat double << ') { n.times do; @a_str_double << @b_str_double; end}
end
결과 :
jruby 1.7.4 (1.9.3p392) 2013-05-16 2390d3b on Java HotSpot(TM) 64-Bit Server VM 1.7.0_10-b18 [darwin-x86_64]
user system total real
assign single 0.220000 0.010000 0.230000 ( 0.108000)
assign via << single 0.280000 0.010000 0.290000 ( 0.138000)
assign double 0.050000 0.000000 0.050000 ( 0.047000)
assing interp 0.100000 0.010000 0.110000 ( 0.056000)
concat single 0.230000 0.010000 0.240000 ( 0.159000)
concat double 0.150000 0.010000 0.160000 ( 0.101000)
concat single interp 0.170000 0.000000 0.170000 ( 0.121000)
concat single << 0.100000 0.000000 0.100000 ( 0.076000)
concat double interp 0.160000 0.000000 0.160000 ( 0.108000)
concat double << 0.100000 0.000000 0.100000 ( 0.074000)
ruby 1.9.3p429 (2013-05-15 revision 40747) [x86_64-darwin12.4.0]
user system total real
assign single 0.100000 0.000000 0.100000 ( 0.103326)
assign via << single 0.160000 0.000000 0.160000 ( 0.163442)
assign double 0.100000 0.000000 0.100000 ( 0.102212)
assing interp 0.110000 0.000000 0.110000 ( 0.104671)
concat single 0.240000 0.000000 0.240000 ( 0.242592)
concat double 0.250000 0.000000 0.250000 ( 0.244666)
concat single interp 0.180000 0.000000 0.180000 ( 0.182263)
concat single << 0.120000 0.000000 0.120000 ( 0.126582)
concat double interp 0.180000 0.000000 0.180000 ( 0.181035)
concat double << 0.130000 0.010000 0.140000 ( 0.128731)
나는 다음을 시도했다.
def measure(t)
single_measures = []
double_measures = []
double_quoted_string = ""
single_quoted_string = ''
single_quoted = 0
double_quoted = 0
t.times do |i|
t1 = Time.now
single_quoted_string << 'a'
t1 = Time.now - t1
single_measures << t1
t2 = Time.now
double_quoted_string << "a"
t2 = Time.now - t2
double_measures << t2
if t1 > t2
single_quoted += 1
else
double_quoted += 1
end
end
puts "Single quoted did took longer in #{((single_quoted.to_f/t.to_f) * 100).round(2)} percent of the cases"
puts "Double quoted did took longer in #{((double_quoted.to_f/t.to_f) * 100).round(2)} percent of the cases"
single_measures_avg = single_measures.inject{ |sum, el| sum + el }.to_f / t
double_measures_avg = double_measures.inject{ |sum, el| sum + el }.to_f / t
puts "Single did took an average of #{single_measures_avg} seconds"
puts "Double did took an average of #{double_measures_avg} seconds"
puts "\n"
end
both = 10.times do |i|
measure(1000000)
end
그리고 이것들은 출력입니다 :
1.
Single quoted did took longer in 32.33 percent of the cases
Double quoted did took longer in 67.67 percent of the cases
Single did took an average of 5.032084099982639e-07 seconds
Double did took an average of 5.171539549983464e-07 seconds
2.
Single quoted did took longer in 26.9 percent of the cases
Double quoted did took longer in 73.1 percent of the cases
Single did took an average of 4.998066229983696e-07 seconds
Double did took an average of 5.223457359986066e-07 seconds
삼.
Single quoted did took longer in 26.44 percent of the cases
Double quoted did took longer in 73.56 percent of the cases
Single did took an average of 4.97640888998877e-07 seconds
Double did took an average of 5.132918459987151e-07 seconds
4.
Single quoted did took longer in 26.57 percent of the cases
Double quoted did took longer in 73.43 percent of the cases
Single did took an average of 5.017136069985988e-07 seconds
Double did took an average of 5.004514459988143e-07 seconds
5.
Single quoted did took longer in 26.03 percent of the cases
Double quoted did took longer in 73.97 percent of the cases
Single did took an average of 5.059069689983285e-07 seconds
Double did took an average of 5.028807639983705e-07 seconds
6.
Single quoted did took longer in 25.78 percent of the cases
Double quoted did took longer in 74.22 percent of the cases
Single did took an average of 5.107472039991399e-07 seconds
Double did took an average of 5.216212339990241e-07 seconds
7.
Single quoted did took longer in 26.48 percent of the cases
Double quoted did took longer in 73.52 percent of the cases
Single did took an average of 5.082368429989468e-07 seconds
Double did took an average of 5.076817109989933e-07 seconds
8.
Single quoted did took longer in 25.97 percent of the cases
Double quoted did took longer in 74.03 percent of the cases
Single did took an average of 5.077162969990005e-07 seconds
Double did took an average of 5.108381859991112e-07 seconds
9.
Single quoted did took longer in 26.28 percent of the cases
Double quoted did took longer in 73.72 percent of the cases
Single did took an average of 5.148080479983138e-07 seconds
Double did took an average of 5.165793929982176e-07 seconds
10.
Single quoted did took longer in 25.03 percent of the cases
Double quoted did took longer in 74.97 percent of the cases
Single did took an average of 5.227828659989748e-07 seconds
Double did took an average of 5.218296609988378e-07 seconds
내가 실수를하지 않으면 작은 따옴표가 대부분의 경우 약간 빠르더라도 둘 다 거의 같은 시간이 걸리는 것 같습니다.