हम रूबीस्ट अपने हैश से प्यार करते हैं। लेकिन हैश में कुछ प्रसिद्ध खामियां हैं। जैसा कि रिचर्ड ने हाशी कंसिडर्ड हार्मफुल में बताया, वे कभी-कभी बहुत लचीले हो सकते हैं - एक त्वरित टाइपो और आप उन कुंजियों को असाइन और संदर्भित कर सकते हैं जिनका आपने कभी इरादा नहीं किया था।
a = { type: "F150" }
a[:typo] # nil
कुछ सामान्य हैश विकल्प
यदि आप वास्तविक संरचित डेटा को संग्रहीत करने के लिए हैश का उपयोग कर रहे हैं, तो आप यह तय कर सकते हैं कि आपको वास्तव में लचीलेपन की आवश्यकता नहीं है। कि यह केवल आपको परेशानी में डालने वाला है।
कुछ विकल्प हैं। कल्पना कीजिए कि आपको XY निर्देशांक की एक जोड़ी को स्टोर करने की आवश्यकता है। एक दृष्टिकोण एक वर्ग को परिभाषित करना हो सकता है, जिसका एकमात्र काम XY निर्देशांक की एक जोड़ी को पकड़ना है।
class PointClass # I don't recommend ending class names with Class :)
attr_accessor :x, :y
def initialize(args)
@x = args.fetch(:x)
@y = args.fetch(:y)
end
end
point_class = PointClass.new(x: 1, y: 2)
point_class.x # 1
चूंकि इस मामले में हमें केवल डेटा को एनकैप्सुलेट करने की आवश्यकता है, इसलिए स्ट्रक्चर का उपयोग करने के लिए एक अधिक संक्षिप्त विकल्प हो सकता है। यहाँ जो दिखता है वह है:
PointStruct = Struct.new(:x, :y)
point_struct = PointStruct.new(1, 2)
point_struct.x # 1
ओपनस्ट्रक्चर का उपयोग करने के लिए एक तीसरा विकल्प हो सकता है। ओपनस्ट्रक्चर एक संरचना की तरह दिखता है, लेकिन आपको हैश की तरह मनमाना मान सेट करने देता है। यहां एक उदाहरण दिया गया है:
point_os = OpenStruct.new(x: 1, y: 2)
point_os.x # 1
प्रदर्शन प्रभाव
[अद्यतन 7/10/2015:ऐसा प्रतीत होता है कि मेरी बेंचमार्किंग स्क्रिप्ट हैश के लिए अनुचित थी। जैसा कि पैट्रिक हेल्म ने बताया, मैं उन्हें प्रारंभ करने की एक अक्षम विधि का उपयोग कर रहा था। तो कृपया हैश के लिए परिणामों की अवहेलना करें। हालांकि ओपनस्ट्रक्चर के सुपर स्लो होने के बारे में मेरा मुख्य बिंदु अभी भी मान्य है। आप मेरी बेंचमार्क स्क्रिप्ट में उनके बदलाव यहां देख सकते हैं]
इन चार विकल्पों को देखते हुए, मुझे आश्चर्य हुआ कि प्रदर्शन के निहितार्थ क्या थे। यह बहुत स्पष्ट है कि यदि आप केवल थोड़े से डेटा के साथ काम कर रहे हैं तो इनमें से कोई भी विकल्प पर्याप्त तेज़ है। लेकिन अगर आपके पास संसाधित करने के लिए हजारों या लाखों आइटम हैं, तो हैश बनाम ओपनस्ट्रक्चर बनाम स्ट्रक्चर बनाम क्लास का प्रदर्शन प्रभाव मायने रखता है।
हनीबैगर में, हमारे एपीआई को हर सेकंड हजारों अपवादों की सूचना दी जा रही है, इसलिए इस तरह के प्रदर्शन प्रभाव को समझना हमेशा हमारे दिमाग में होता है।
इसलिए, मैंने एक साधारण बेंचमार्क स्क्रिप्ट लिखी। मैं इस तरह के प्रयोगों के लिए बेंचमार्क-आईपीएस रत्न का उपयोग करना पसंद करता हूं क्योंकि यह स्वचालित रूप से एक अच्छे नमूना आकार का पता लगाता है, और मानक विचलन की रिपोर्ट करता है।
आरंभीकरण
जब मैंने प्वाइंटक्लास, प्वाइंटस्ट्रक्चर, हैश और ओपनस्ट्रक्चर के लिए प्रारंभिक समय बेंचमार्क किया तो मैंने पाया कि प्वाइंट क्लास और प्वाइंटस्ट्रक्चर स्पष्ट विजेता थे। वे OpenStruct से लगभग 10x तेज़ और हैश से लगभग 2x तेज़ थे.
PointClass और PointStruct, OpenStruct से लगभग 10x तेज़ थे
ये परिणाम समझ में आते हैं। संरचनाएं सबसे सरल हैं, इसलिए वे सबसे तेज़ हैं। ओपनस्ट्रक्चर सबसे जटिल है (यह हैश के लिए एक रैपर है) इसलिए यह सबसे धीमा है। हालांकि गति में अंतर का परिमाण आश्चर्यजनक है।
इस प्रयोग को चलाने के बाद, मुझे किसी भी कोड में ओपनस्ट्रक्चर का उपयोग करने में वास्तव में संकोच होगा जहां गति एक चिंता का विषय है। और मैं प्रदर्शन-महत्वपूर्ण कोड में दिखाई देने वाले किसी भी हैश पर सतर्क नजर रखूंगा।
पढ़ें/लिखें
इनिशियलाइज़ेशन के विपरीत, मानों को सेट करने और एक्सेस करने की बात आती है, तो सभी चार विकल्प मोटे तौर पर समान होते हैं।
बेंचमार्क पढ़ना और लिखना स्ट्रक्चर, क्लास, हैश और ओपनस्ट्रक्चर के बीच कोई बड़ा अंतर नहीं दिखाता है
बेंचमार्किंग स्क्रिप्ट
यदि आप अपने सिस्टम पर बेंचमार्क चलाना चाहते हैं, तो आप नीचे दी गई स्क्रिप्ट का उपयोग कर सकते हैं। मैंने इसे OSX पर MRI 2.1 पर चलाया। यदि आप अन्य रूबी दुभाषियों पर प्रदर्शन के बारे में उत्सुक हैं, तो माइकल कोहेन ने एमआरआई 2.2, जेआरबी और अन्य के परिणामों के साथ एक शानदार सार बनाया है।
require 'benchmark/ips'
require 'ostruct'
data = { x: 100, y: 200 }
PointStruct = Struct.new(:x, :y)
class PointClass
attr_accessor :x, :y
def initialize(args)
@x = args.fetch(:x)
@y = args.fetch(:y)
end
end
puts "\n\nINITIALIZATION =========="
Benchmark.ips do |x|
x.report("PointStruct") { PointStruct.new(100, 200) }
x.report("PointClass") { PointClass.new(data) }
x.report("Hash") { Hash.new.merge(data) }
x.report("OpenStruct") { OpenStruct.new(data) }
end
puts "\n\nREAD =========="
point_struct = PointStruct.new(100, 200)
point_class = PointClass.new(data)
point_hash = Hash.new.merge(data)
point_open_struct = OpenStruct.new(data)
Benchmark.ips do |x|
x.report("PointStruct") { point_struct.x }
x.report("PointClass") { point_class.x }
x.report("Hash") { point_hash.fetch(:x) }
x.report("OpenStruct") { point_open_struct.x }
end
puts "\n\nWRITE =========="
Benchmark.ips do |x|
x.report("PointStruct") { point_struct.x = 1 }
x.report("PointClass") { point_class.x = 1 }
x.report("Hash") { point_hash[:x] = 1 }
x.report("OpenStruct") { point_open_struct.x = 1 }
end