यदि आप रूबी रिफाइनमेंट के लिए गूगल खोजते हैं बिना किसी बैक स्टोरी को जाने तो आप इस विचार के साथ आ सकते हैं कि रिफाइनमेंट धीमे हैं।
जैसा कि वे मूल रूप से प्रस्तावित थे, रिफाइनमेंट धीमे होते। वे इंटरप्रेटर के लिए मेथड लुकअप जैसी चीजों को अनुकूलित करना असंभव बना देते।
लेकिन रिफाइनमेंट का वास्तविक कार्यान्वयन मूल प्रस्ताव की तुलना में थोड़ा अधिक सीमित है। इसलिए मुझे लगा कि आज रूबी में मौजूद रिफाइनमेंट पर बेंचमार्क की एक श्रृंखला चलाना दिलचस्प होगा।
TL;DR
रिफाइनमेंट धीमे नहीं हैं। या कम से कम वे "सामान्य" मेथड से किसी भी धीमे नहीं लगते।
डमी लोड
हम मेथड कॉल को बेंचमार्क करने जा रहे हैं। तो हमें कुछ मेथड की आवश्यकता होगी।
यहां, मैं एक मूर्खतापूर्ण छोटे मेथड के दो संस्करण बना रहा हूँ। एक "सामान्य" है और दूसरा रिफाइनमेंट के अंदर है:
# As our "dummy load" we're going to create shrugs. # 1 shrug == "¯\_(ツ)_/¯" # 2 shrugs == "¯\_(ツ)_/¯¯\_(ツ)_/¯" # ...etc. SHRUG = "¯\_(ツ)_/¯" # We'll make a refinement that generates shrugs module Shruggable refine Fixnum do def shrugs SHRUG * self end end end # ...and we'll make a normal method that also generates shrugs def integer_to_shrugs(n) SHRUG * n end
हम रिफाइनमेंट का सीधे उपयोग नहीं कर सकते। इसे using स्टेटमेंट के माध्यम से सक्रिय किया जाना है। इसलिए मैं दो क्लास बनाऊंगा जो समान रूप से व्यवहार करती हैं। एक रिफाइनमेंट का उपयोग करता है, और दूसरा नहीं।
class TestUsing using Shruggable def noop end def shrug 10.shrugs end end class TestWithoutUsing def noop end def shrug integer_to_shrugs(10) end end
बेंचमार्क
मैं जानना चाहता था कि क्या रिफाइनमेंट का उपयोग करके ऑब्जेक्ट इंस्टेंशिएट करना धीमा है, या रिफाइनमेंट के माध्यम से जोड़े गए मेथड को कॉल करना।
सभी बेंचमार्क MRI 2.2.2 पर OSX एल कैपिटन पर चलाए गए थे।
ऑब्जेक्ट निर्माण
क्या "using" कीवर्ड क्लास को इनिशियलाइज़ करने में धीमा बनाता है? नहीं।
Benchmark.ips do |bm| bm.report("class initialization") { TestUsing.new } bm.report("class initialization WITH using") { TestWithoutUsing.new } bm.compare! end # Calculating ------------------------------------- # class initialization 142.929k i/100ms # class initialization WITH using # 145.323k i/100ms # ------------------------------------------------- # class initialization 5.564M (± 8.3%) i/s - 27.728M # class initialization WITH using # 5.619M (± 7.4%) i/s - 28.047M # Comparison: # class initialization WITH using: 5618601.3 i/s # class initialization: 5564116.5 i/s - 1.01x slower
मेथड कॉल
क्या रिफाइनमेंट "सामान्य" मेथड लुकअप गति को प्रभावित करते हैं? नहीं।
Benchmark.ips do |bm| bm.report("run method") { TestUsing.new.noop } bm.report("run method in class WITH using") { TestWithoutUsing.new.noop } bm.compare! end # Calculating ------------------------------------- # run method 141.905k i/100ms # run method in class WITH using # 144.435k i/100ms # ------------------------------------------------- # run method 5.010M (± 6.4%) i/s - 24.975M # run method in class WITH using # 5.086M (± 5.3%) i/s - 25.421M # Comparison: # run method in class WITH using: 5086262.3 i/s # run method: 5010273.6 i/s - 1.02x slower
क्या रिफाइनमेंट से एक मेथड का उपयोग करना समकक्ष "सामान्य" मेथड का उपयोग करने से धीमा है? नहीं।
Benchmark.ips do |bm| bm.report("shrug") { TestUsing.new.shrug } bm.report("shrug via refinement") { TestWithoutUsing.new.shrug } bm.compare! end # Calculating ------------------------------------- # shrug 96.089k i/100ms # shrug via refinement 95.559k i/100ms # ------------------------------------------------- # shrug 1.825M (± 9.3%) i/s - 9.128M # shrug via refinement 1.929M (± 6.2%) i/s - 9.651M # Comparison: # shrug via refinement: 1928841.5 i/s # shrug: 1825069.4 i/s - 1.06x slower
डेक को स्टैक करना
क्या मैं रिफाइनमेंट बेंचमार्क को अपने कंट्रोल से धीमा बनाने के लिए कुछ कर सकता हूँ? ¯\_(ツ)_/¯
# Does repeated evaluation of the `using` keyword affect performance. Only slightly. # This is an unfair test, but I really wanted to force refinements to be slower # in SOME use case :) Benchmark.ips do |bm| bm.report("inline shrug") { integer_to_shrugs(10) } bm.report("inline shrug via refinement") do using Shruggable 10.shrugs end bm.compare! end # Calculating ------------------------------------- # inline shrug 100.460k i/100ms # inline shrug via refinement # 72.131k i/100ms # ------------------------------------------------- # inline shrug 2.507M (± 5.2%) i/s - 12.557M # inline shrug via refinement # 1.498M (± 4.3%) i/s - 7.502M # Comparison: # inline shrug: 2506663.9 i/s # inline shrug via refinement: 1497747.6 i/s - 1.67x slower
पूरा कोड
यदि आप बेंचमार्क को स्वयं चलाना चाहते हैं, तो यहां कोड है।
require 'benchmark/ips' # As our "dummy load" we're going to create shrugs. # 1 shrug == "¯\_(ツ)_/¯" # 2 shrugs == "¯\_(ツ)_/¯¯\_(ツ)_/¯" # ...etc. SHRUG = "¯\_(ツ)_/¯" # We'll make a refinement that generates shrugs module Shruggable refine Fixnum do def shrugs SHRUG * self end end end # ...and we'll make a normal method that also generates shrugs def integer_to_shrugs(n) SHRUG * n end # Now we'll define two classes. The first uses refinments. The second doesn't. class TestUsing using Shruggable def noop end def shrug 10.shrugs end end class TestWithoutUsing def noop end def shrug integer_to_shrugs(10) end end # Does the "using" keyword make a class slower to initialize? Nope. Benchmark.ips do |bm| bm.report("class initialization") { TestUsing.new } bm.report("class initialization WITH using") { TestWithoutUsing.new } bm.compare! end # Calculating ------------------------------------- # class initialization 142.929k i/100ms # class initialization WITH using # 145.323k i/100ms # ------------------------------------------------- # class initialization 5.564M (± 8.3%) i/s - 27.728M # class initialization WITH using # 5.619M (± 7.4%) i/s - 28.047M # Comparison: # class initialization WITH using: 5618601.3 i/s # class initialization: 5564116.5 i/s - 1.01x slower # Do refinements affect "normal" method lookup speed? Nope. Benchmark.ips do |bm| bm.report("run method") { TestUsing.new.noop } bm.report("run method in class WITH using") { TestWithoutUsing.new.noop } bm.compare! end # Calculating ------------------------------------- # run method 141.905k i/100ms # run method in class WITH using # 144.435k i/100ms # ------------------------------------------------- # run method 5.010M (± 6.4%) i/s - 24.975M # run method in class WITH using # 5.086M (± 5.3%) i/s - 25.421M # Comparison: # run method in class WITH using: 5086262.3 i/s # run method: 5010273.6 i/s - 1.02x slower # Is using a method from a refinement slower than using an equivalent "normal" method? Nope. Benchmark.ips do |bm| bm.report("shrug") { TestUsing.new.shrug } bm.report("shrug via refinement") { TestWithoutUsing.new.shrug } bm.compare! end # Calculating ------------------------------------- # shrug 96.089k i/100ms # shrug via refinement 95.559k i/100ms # ------------------------------------------------- # shrug 1.825M (± 9.3%) i/s - 9.128M # shrug via refinement 1.929M (± 6.2%) i/s - 9.651M # Comparison: # shrug via refinement: 1928841.5 i/s # shrug: 1825069.4 i/s - 1.06x slower # Does repeated evaluation of the `using` keyword affect performance. Only slightly. # This is an unfair test, but I really wanted to force refinements to be slower # in SOME use case :) Benchmark.ips do |bm| bm.report("inline shrug") { integer_to_shrugs(10) } bm.report("inline shrug via refinement") do using Shruggable 10.shrugs end bm.compare! end # Calculating ------------------------------------- # inline shrug 100.460k i/100ms # inline shrug via refinement # 72.131k i/100ms # ------------------------------------------------- # inline shrug 2.507M (± 5.2%) i/s - 12.557M # inline shrug via refinement # 1.498M (± 4.3%) i/s - 7.502M # Comparison: # inline shrug: 2506663.9 i/s # inline shrug via refinement: 1497747.6 i/s - 1.67x slower