কম্পিউটার টিউটোরিয়াল

রুবি পারফরম্যান্স মাস্টার করুন: কোড ইফেক্টিভলি বেঞ্চмар্কিং

রুবির স্ট্যান্ডার্ড লাইব্রেরিতে একটি বেঞ্চмар্কিং টুল আছে যা আপনার কোডের পারফরম্যান্স মাপতে সাহায্য করে। এটি সবচেয়ে উপকারী যখন দুটি ইমপ্লিমেন্টেশন তুলনা করতে হয়, কোনটি দ্রুত সেটা বের করার জন্য।

এই উদাহরণে, আমাদের টাস্ক হলো একটি স্ট্রিং কী সহ হ্যাশ (যেমন {"foo" => "bar"}) কে সিম্বল কী সহ হ্যাশে (যেমন {:foo => "bar"}) কনভার্ট করা। উদাহরণগুলোতে, আমরা ইংরেজি অ্যালফাবেটের প্রতিটি অক্ষরের জন্য একটি কী এবং ভ্যালু সহ একটি হ্যাশ ব্যবহার করব।

টাইপ আউট না করে দ্রুত এই হ্যাশ জেনারেট করতে, আমরা অক্ষরের একটি রেঞ্জ কনভার্ট করব আমাদের টেস্ট হ্যাশে। আমরা সেটি input ভেরিয়েবলে রাখব পরে ব্যবহার করার জন্য।

input = ("a".."z").map {|letter| [letter, letter]}.to_h
# => {"a"=>"a", "b"=>"b", "c"=>"c", "d"=>"d", "e"=>"e", "f"=>"f", "g"=>"g", "h"=>"h", "i"=>"i", "j"=>"j", "k"=>"k", "l"=>"l", "m"=>"m", "n"=>"n", "o"=>"o", "p"=>"p", "q"=>"q", "r"=>"r", "s"=>"s", "t"=>"t", "u"=>"u", "v"=>"v", "w"=>"w", "x"=>"x", "y"=>"y", "z"=>"z"}

এখন যেহেতু আমাদের টেস্ট করার জন্য input ভেরিয়েবল আছে, আমরা একটি ইমপ্লিমেন্টেশন লিখব দেখতে কেমন পারফর্ম করে। আমাদের ইনপুট হ্যাশের সব কী সিম্বলে কনভার্ট করার একটি সুন্দর one-liner দেখতে এভাবে:

input.map { |key, value| [key.to_sym, value] }.to_h
# => {:a=>"a", :b=>"b", :c=>"c", :d=>"d", :e=>"e", :f=>"f", :g=>"g", :h=>"h", :i=>"i", :j=>"j", :k=>"k", :l=>"l", :m=>"m", :n=>"n", :o=>"o", :p=>"p", :q=>"q", :r=>"r", :s=>"s", :t=>"t", :u=>"u", :v=>"v", :w=>"w", :x=>"x", :y=>"y", :z=>"z"}

এই ইমপ্লিমেন্টেশন map মেথড ব্যবহার করে হ্যাশের ওপর লুপ করে প্রতিটি কী-ভ্যালু পেয়ারের জন্য একটি ব্লক রান করে। ব্লকে, এটা কীকে সিম্বলে কনভার্ট করে এবং একটি টু-এলিমেন্ট অ্যারে রিটার্ন করে নতুন তৈরি সিম্বল কী এবং অনঅ্যাল্টার্ড ভ্যালু সহ।

map কমান্ডের রেজাল্ট হলো ২৬টি কী-ভ্যালু অ্যারে সহ একটি অ্যারে। যেহেতু আমাদের হ্যাশ দরকার, আমরা #to_h ব্যবহার করি আমাদের নতুন অ্যারেকে আবার হ্যাশে কনভার্ট করতে।

Benchmark.measure

এখন যেহেতু আমাদের একটি ওয়ার্কিং ইমপ্লিমেন্টেশন আছে, আমরা রুবির Benchmark মডিউল ব্যবহার করে দেখতে পারি কেমন পারফর্ম করে।

require 'benchmark'
input = ('a'..'z').map { |letter| [letter, letter] }.to_h
puts Benchmark.measure {
  50_000.times do
    input.map { |key, value| [key.to_sym, value] }.to_h
  end
}

Benchmark.measure একটি ব্লক নেয়, যা এক্সিকিউট হয় তার এক্সিকিউশন টাইম ট্র্যাক করে। এটা একটি রিপোর্ট স্ট্রিং রিটার্ন করে, যা puts দিয়ে কনসোলে প্রিন্ট হয়।

যেহেতু এটি একটি কুইক কোড টুকরো, আমরা সেটি ৫০,০০০ বার রান করি যাতে কিছু ভিজিবল রেজাল্ট পাই।

$ ruby bench.rb
0.810000 0.000000 0.810000 ( 0.816964)

রিপোর্ট স্ট্রিং চারটি সংখ্যা দেখায়, যেগুলো রিপ্রেজেন্ট করে ইউজার সিপিইউ টাইম (আপনার কোড এক্সিকিউট করতে লেগে থাকা সময়), সিস্টেম সিপিইউ টাইম (কার্নেলে বিত্তিত সময়), ইউজার এবং সিস্টেম সিপিইউ টাইম যোগ করে, এবং একচুয়াল টাইম (বা ওয়াল ক্লক টাইম) যা ব্লক এক্সিকিউট হতে লেগেছে ব্র্যাকেটে।

ওয়াল টাইম দেখাচ্ছে আমরা উপরের কোড ব্লক ৫০,০০০ বার একটু বেশি ৮০০ মিলিসেকেন্ডে রান করতে পারি। যদিও এটা একটি ইমপ্রেসিভ সংখ্যা, আমরা না জানি সেটা কী বোঝায় যদি না আমরা অন্য একটি ইমপ্লিমেন্টেশনের সাথে তুলনা করি।

Benchmark.bm

Benchmark.measure-র পাশাপাশি, রুবি Benchmark.bm প্রদান করে, যা একাধিক কোড স্যাম্পল রান করতে পারে এবং তাদের রেজাল্ট প্রিন্ট করতে পারে। প্রতিটি স্যাম্পলের জন্য, আমরা Benchmark#report কল করব একটি নাম এবং এক্সিকিউট হওয়া ব্লক দিয়ে।

require 'benchmark'
input = ("a".."z").map { |letter| [letter, letter] }.to_h
n = 50_000
Benchmark.bm do |benchmark|
  benchmark.report("Hash[]") do
    n.times do
      input.map { |key, value| [key.to_sym, value] }.to_h
    end
  end
  benchmark.report("{}.tap") do
    n.times do
      {}.tap do |new_hash|
        input.each do |key, value|
          new_hash[key.to_sym] = value
        end
      end
    end
  end
end

এই বেঞ্চмар্কে, আমরা Benchmark.bm ব্যবহার করব দুটি ইমপ্লিমেন্টেশন টেস্ট করার জন্য প্রতিটি ৫০,০০০ বার রান করে। প্রথম মেজারমেন্ট ব্লক আগের উদাহরণের মতো।

দ্বিতীয় মেজারমেন্ট ব্লকে, আমরা একটি লম্বা ইমপ্লিমেন্টেশন ব্যবহার করব, যা একটি নতুন হ্যাশ আগে তৈরি করে। এটা স্ট্রিং-কী হ্যাশের ওপর লুপ করে, এবং নতুন হ্যাশে প্রতিটি আইটেমের জন্য একটি এলিমেন্ট যোগ করে। এইভাবে, এটাকে হ্যাশকে অ্যারে কনভার্ট করতে হয় না, এবং শেষে আবার হ্যাশে কনভার্ট করতে হয় না।

বেঞ্চмар্ক আবার চালালে দেখাবে এই ইমপ্লিমেন্টেশন ২৫% এর চেয়ে বেশি দ্রুত, যদিও কোডটি লম্বা (এবং একটু কম চমৎকার) আগের one-liner চেয়ে।

$ ruby bench.rb
user system total real
Hash[] 0.850000 0.000000 0.850000 ( 0.851106)
{}.tap 0.610000 0.020000 0.630000 ( 0.637070)

আরও বেঞ্চмар্কিং

যখন আপনার কোডবেসে একটি গুরুত্বপূর্ণ কোড টুকরোতে কাজ করছেন, বেঞ্চмар্ক রান করে বিভিন্ন ইমপ্লিমেন্টেশন তুলনা করা তাদের এক্সিকিউশন স্পীড নিয়ে আরও ইনসাইট দিতে পারে। বিভিন্ন ইমপ্লিমেন্টেশন তুলনা করে বুঝতে পারবেন কীভাবে সেগুলো পারফরম্যান্স প্রভাবিত করে, তখন আপনি অ্যান্টি-প্যাটার্ন এড়িয়ে দ্রুত রুবি লিখতে পারবেন।

টিপ: অনেক কমন ইডিওম প্রি-বেঞ্চмар্কড, এবং তাদের রেজাল্ট fast-ruby হিসেবে পাবলিশড। উদাহরণগুলো পড়তে আপনার কিছু বেঞ্চмар্কিং বাঁচাতে পারে ভবিষ্যতে।

এই উদাহরণের জন্য আরও অপশন টেস্ট করা যায়, এবং রুবির বেঞ্চмар্কিং লাইব্রেরিতে আরও অনেক সফিস্টিকেটেড ফিচার আছে আপনি ট্রাই করতে পারেন, কিন্তু এটা রুবিতে বেঞ্চмар্কিং কীভাবে কাজ করে সেটার একটি ভালো ইন্ট্রোডাকশন দেয়। আপনি যদি বেঞ্চмар্কিং নিয়ে আরও জানতে চান, বা কোনো প্রশ্ন বা সাজেশন আছে, দয়া করে @AppSignal-এ আমাদের জানান।

  1. RuboCop দিয়ে রুবি কোড কোয়ালিটি উন্নত: লিন্টিং ও অটো-ফরম্যাটিং

  2. টেস্ট-কমিট-রিভার্ট: লিগ্যাসি রুবি কোড নিরাপদে টেস্ট করার প্র্যাকটিক্যাল ওয়ার্কফ্লো

  3. রুবি ২.৬-এ ৯টি গুরুত্বপূর্ণ উন্নতির অন্বেষণ

  4. রুবিতে স্ট্যাটিক অ্যানালাইসিস মাস্টার করুন: Parser Gem দিয়ে মেথড ও প্যারামিটার এক্সট্রাক্ট করুন