আমরা রুবিস্টরা হ্যাশকে খুব পছন্দ করি। কিন্তু হ্যাশের কিছু পরিচিত ত্রুটি আছে। Ричар্ড님이 Hashie Considered Harmful-এ দেখিয়েছেন, এটি কখনও কখনও খুব ফ্লেক্সিবল হয় - একটি টাইপো করলে আপনি অনিচ্ছাকৃত কি অ্যাসাইন এবং রেফারেন্স করতে পারেন।
a = { type: "F150" } a[:typo] # nil কিছু সাধারণ হ্যাশ বিকল্প
আপনি যদি হ্যাশ ব্যবহার করে স্ট্রাকচার্ড ডেটা স্টোর করেন, তবে আপনি ডিসাইড করতে পারেন যে আপনার ফ্লেক্সিবিলিটি দরকার নেই। কেবল এটি আপনাকে মুশকিলে ফেলবে।
কয়েকটি বিকল্প রয়েছে। ধরুন আপনাকে XY কোঅর্ডিনেটের একটি জোড়া স্টোর করতে হবে। একটি আপভাবে হলো একটি ক্লাস ডিফাইন করা যার একমাত্র কাজ হলো XY কোঅর্ডিনেটের জোড়া ধারণ করা।
class PointClass # আমি ক্লাসের নাম 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 যেহেতু এই কেসে আমাদের শুধু ডেটা এনক্যাপসুলেট করতে হবে, আরও সংক্ষিপ্ত পছন্দ হতে পারে Struct ব্যবহার করা। এখানে সেটি কেমন দেখাবে:
PointStruct = Struct.new(:x, :y) point_struct = PointStruct.new(1, 2) point_struct.x # 1 তৃতীয় অপশন হতে পারে OpenStruct ব্যবহার করা। OpenStruct কিছুটা Struct-এর মতো দেখায়, কিন্তু হ্যাশের মতো আপনি আrbit্রারি ভ্যালু সেট করতে পারেন। উদাহরণ:
point_os = OpenStruct.new(x: 1, y: 2) point_os.x # 1 পারফরম্যান্স ইমপ্লিকেশন
[আপডেট ১০/৭/২০১৫: মনে হচ্ছে আমার বেঞ্চмар্কিং স্ক্রিপ্ট হ্যাশের প্রতি অন্যায্য ছিল। প্যাট্রিক হেল্মনি দেখিয়েছেন যে আমি হ্যাশ ইনিশিয়ালাইজ করার একটি অকার্যকর মেথড ব্যবহার করেছিলাম। তাই হ্যাশের রেজাল্টগুলো উপেক্ষা করুন। যদিও OpenStruct খুব স্লো এই মূল পয়েন্টটি এখনো বৈধ। তার আমার বেঞ্চмар্ক স্ক্রিপ্টে পরিবর্তনগুলো এখানে দেখুন]
এই চারটি অপশন দেখে আমার মনে প্রশ্নে পড়লো পারফরম্যান্স ইমপ্লিকেশন কী। এটা বোঝা সহজ যে আপনি যদি একটু ডেটা নিয়ে কাজ করেন তবে এই যেকোনো অপশন দ্রুত পূরণ করবে। কিন্তু যদি হাজার হাজার বা মিলিয়ন মিলিয়ন আইটেম প্রসেস করতে হয়, তবে হ্যাশ vs OpenStruct vs Struct vs ক্লাসের পারফরম্যান্স প্রভাব গুরুত্বপূর্ণ হতে পারে।
Honeybadger-এ, আমাদের API-এ প্রতি সেকেন্ড হাজার হাজার এক্সেপশন রিপোর্ট হয়, তাই এই টাইপের পারফরম্যান্স ইমপ্লিকেশন বোঝা আমাদের জন্য সবসময় গুরুত্বপূর্ণ।
তাই আমি একটি সিম্পল বেঞ্চмар্ক স্ক্রিপ্ট লিখলাম। আমি `benchmark-ips` জেম ব্যবহার করি কারণ এটি অটোমেটিক্যালি ভালো স্যাম্পল সাইজ বের করে এবং স্ট্যান্ডার্ড ডেভিয়েশন রিপোর্ট করে।
ইনিশিয়ালাইজেশন
PointClass, PointStruct, Hash, এবং OpenStruct-এর ইনিশিয়ালাইজেশন টাইম বেঞ্চмар্ক করলে আমি দেখলাম PointClass এবং PointStruct ক্লিয়ার উইনার। এগুলো OpenStruct-এর চেয়ে প্রায় ১০গুন দ্রুত, এবং হ্যাশের চেয়ে প্রায় ২গুন দ্রুত।

PointClass এবং PointStruct OpenStruct-এর চেয়ে প্রায় ১০গুন দ্রুত
এই রেজাল্টগুলো অর্থপূর্ণ। Struct সবচেয়ে সিম্পল, তাই সবচেয়ে দ্রুত। OpenStruct সবচেয়ে জটিল (এটি হ্যাশের একটি র্যাপার) তাই সবচেয়ে স্লো। কিন্তু স্পীডের পার্থক্যের পরিমাণ কিছুটা আশ্চর্যजनক।
এই এক্সপেরিমেন্ট চালানোর পর, আমি স্পীড যেখানে কনসার্ন, সেখানে OpenStruct ব্যবহার করতে খুব হিচকিচাব। এবং পারফরম্যান্স-ক্রিটিক্যাল কোডে যেকোনো হ্যাশ দেখলে আমি সশঙ্ক থাকব।
রিড / রাইট
ইনিশিয়ালাইজেশনের বিপরীতে, সব চার অপশন রফলি একই পারফরম্যান্স দেয় যখন ভ্যালু সেট এবং এক্সেস করার কথা।

রিড এবং রাইট বেঞ্চмар্ক দেখায় Struct, ক্লাস, হ্যাশ এবং OpenStruct-এর মধ্যে বড় পার্থক্য নেই
বেঞ্চмар্কিং স্ক্রিপ্ট
আপনি যদি নিজের সিস্টেমে বেঞ্চмар্ক চালাতে চান, তবে নিচের স্ক্রিপ্টটি ব্যবহার করতে পারেন। আমি এটি MRI 2.1-এ OSX-এ চালালাম। অন্যান্য রুবি ইন্টারপ্রেটারের পারফরম্যান্স নিয়ে আগ্রহ থাকলে, মাইকেল কোহেন একটি অসাধারণ gist বানিয়েছেন MRI 2.2, JRuby এবং অন্যান্য জন্য রেজাল্টসহ।
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