আমরা রুবিস্টরা আমাদের হ্যাশ পছন্দ করি। কিন্তু হ্যাশের কয়েকটি সুপরিচিত ত্রুটি রয়েছে। রিচার্ড যেমন Hashie Considered Harmful-এ উল্লেখ করেছেন, সেগুলি অনেক সময় খুব নমনীয় হতে পারে - একটি দ্রুত টাইপো এবং আপনি কীগুলি বরাদ্দ এবং রেফারেন্স করতে পারেন যা আপনি কখনই চাননি৷
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
একটি তৃতীয় বিকল্প হতে পারে OpenStruct ব্যবহার করা। OpenStruct একটি struct মত দেখায়, কিন্তু আপনি একটি হ্যাশ মত নির্বিচারে মান সেট করতে দেয়. এখানে একটি উদাহরণ:
point_os = OpenStruct.new(x: 1, y: 2)
point_os.x # 1
পারফরম্যান্স ইমপ্লিকেশন
[আপডেট 7/10/2015:এটা প্রতীয়মান হয় যে আমার বেঞ্চমার্কিং স্ক্রিপ্ট হ্যাশের জন্য অন্যায় ছিল। প্যাট্রিক হেলম যেমন উল্লেখ করেছেন, আমি তাদের শুরু করার একটি অদক্ষ পদ্ধতি ব্যবহার করছিলাম। তাই হ্যাশের জন্য ফলাফল উপেক্ষা করুন. যদিও ওপেনস্ট্রাকট সম্পর্কে আমার প্রধান বিন্দু সুপার ধীর হওয়া এখনও বৈধ। আপনি এখানে আমার বেঞ্চমার্ক স্ক্রিপ্টে তার পরিবর্তনগুলি দেখতে পারেন]
এই চারটি বিকল্পের দিকে তাকিয়ে, আমি ভাবতে শুরু করি যে কর্মক্ষমতার প্রভাব কী। এটা বেশ সুস্পষ্ট যে এই বিকল্পগুলির মধ্যে যেকোনও দ্রুত যথেষ্ট যদি আপনি শুধুমাত্র সামান্য ডেটা নিয়ে কাজ করেন। কিন্তু যদি আপনার কাছে প্রসেস করার জন্য হাজার হাজার বা লক্ষাধিক আইটেম থাকে, তাহলে হ্যাশ বনাম OpenStruct বনাম struct বনাম ক্লাসের পারফরম্যান্সের প্রভাব গুরুত্বপূর্ণ হতে পারে।
Honeybadger-এ, প্রতি সেকেন্ডে আমাদের এপিআই-তে হাজার হাজার ব্যতিক্রম রিপোর্ট করা হচ্ছে, তাই এই ধরনের কর্মক্ষমতা বোঝার বিষয়টি সবসময় আমাদের মাথায় থাকে।
তাই, আমি একটি সাধারণ বেঞ্চমার্ক স্ক্রিপ্ট লিখেছিলাম। আমি এই ধরনের পরীক্ষা-নিরীক্ষার জন্য বেঞ্চমার্ক-আইপিএস রত্ন ব্যবহার করতে চাই কারণ এটি স্বয়ংক্রিয়ভাবে একটি ভাল নমুনার আকার বের করে এবং স্ট্যান্ডার্ড বিচ্যুতির প্রতিবেদন করে।
শুরুকরণ
যখন আমি পয়েন্টক্লাস, পয়েন্টস্ট্রাক্ট, হ্যাশ এবং ওপেনস্ট্রাক্টের জন্য প্রারম্ভিক সময় বেঞ্চমার্ক করেছি তখন আমি দেখতে পেলাম যে পয়েন্টক্লাস এবং পয়েন্টস্ট্রাক্ট স্পষ্ট বিজয়ী। সেগুলি OpenStruct থেকে প্রায় 10x দ্রুত এবং হ্যাশের চেয়ে প্রায় 2x দ্রুত।
PointClass এবং PointStruct OpenStruct থেকে প্রায় 10x দ্রুত ছিল
এই ফলাফল অর্থপূর্ণ. কাঠামোগুলি সবচেয়ে সহজ, তাই তারা দ্রুততম। OpenStruct সবচেয়ে জটিল (এটি হ্যাশের জন্য একটি মোড়ক) তাই এটি সবচেয়ে ধীর। তবে গতির পার্থক্যের মাত্রা আশ্চর্যজনক।
এই পরীক্ষা চালানোর পরে, আমি যেকোন কোডে OpenStruct ব্যবহার করতে সত্যিই দ্বিধা বোধ করব যেখানে গতি একটি উদ্বেগের বিষয়। এবং পারফরম্যান্স-সমালোচনামূলক কোডে আমি যে কোনও হ্যাশ দেখতে পাই সেদিকে আমি সতর্ক দৃষ্টি রাখব।
পড়ুন / লিখুন
আরম্ভ করার বিপরীতে, মান সেট করা এবং অ্যাক্সেস করার ক্ষেত্রে চারটি বিকল্পই মোটামুটি একই।
পড়া এবং লেখার বেঞ্চমার্কগুলি স্ট্রাকট, ক্লাস, হ্যাশ এবং ওপেনস্ট্রাক্টের মধ্যে কোনও বড় পার্থক্য দেখায় না
বেঞ্চমার্কিং স্ক্রিপ্ট
আপনি যদি আপনার নিজস্ব সিস্টেমে বেঞ্চমার্ক চালাতে চান তবে আপনি নীচের স্ক্রিপ্টটি ব্যবহার করতে পারেন। আমি ওএসএক্সে এমআরআই 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