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

রুবি কীভাবে আপনার কোড পার্স করে: দ্রুত, আরও দক্ষ স্ক্রিপ্টের চাবিকাঠি

একটি নতুন রুবি ম্যাজিক আর্টিকেলে স্বাগতম! এইবার আমরা দেখব কীভাবে রুবি আমাদের কোড ইন্টারপ্রেট করে, এবং আমরা এই জ্ঞান কীভাবে আমাদের সুবিধায় ব্যবহার করতে পারি। এই পোস্ট আপনাকে বুঝতে সাহায্য করবে কীভাবে কোড ইন্টারপ্রেট হয়, এবং কীভাবে এটা দ্রুত কোডে নেতৃত্ব দিতে পারে।

সিম্বলগুলোর মধ্যে একটি সূক্ষ্ম পার্থক্য

রুবি ম্যাজিকের আগের একটি আর্টিকেলে রুবিতে ক্যারেক্টার এস্কেপিং নিয়ে ছিল সেখানে লাইন ব্রেক এস্কেপিং নিয়ে একটি উদাহরণ ছিল।

নিচের উদাহরণে আপনি দেখতে পাচ্ছেন কীভাবে দুটি স্ট্রিং একসাথে কম্বাইন হয় এক স্ট্রিং হিসেবে একাধিক লাইনে, হয় প্লাস + সিম্বল দিয়ে বা ব্যাকস্ল্যাশ \ দিয়ে।

"foo" + "bar" => "foobar"
# versus
"foo" \ "bar" => "foobar"

এই দুটি উদাহরণ এক जैसा দেখতে পারে, কিন্তু সেগুলো আচরণ করে অনেক ভিন্নভাবে। এই দুটি কীভাবে পড়া হয় এবং ইন্টারপ্রেট হয় সেটার পার্থক্য জানতে, আপনাকে সাধারণত রুবি ইন্টারপ্রেটারের নিটি-গ্রিটি জানতে হয়। অথবা, আমরা শুধু রুবিকে জিজ্ঞেস করতে পারি পার্থক্য কী।

InstructionSequence

RubyVM::InstructionSequence ক্লাস ব্যবহার করে আমরা রুবি থেকে জিজ্ঞেস করতে পারি সেটি আমাদের দেয়া কিছু কোড কীভাবে ইন্টারপ্রেট করে। এই ক্লাস আমাদের একটি টুলসেট দেয় যাতে আমরা রুবির ইন্টারন্যালে একটি ঝলক পাই।

নিচের উদাহরণে যা রিটার্ন হয় সেটি রুবি কোড যেন YARV ইন্টারপ্রেটার বুঝতে পারে।

YARV ইন্টারপ্রেটার

YARV (Yet Another Ruby VM) হলো রুবি ভার্সন ১.৯-এ আনা রুবি ইন্টারপ্রেটার, যেটি মূল ইন্টারপ্রেটারকে রিপ্লেস করেছিল: MRI (Matz's Ruby Interpreter)।

ইন্টারপ্রেটার ব্যবহার করা ল্যাঙ্গুয়েজ সরাসরি কোড এক্সিকিউট করে কোনো ইন্টারমিডিয়েট কম্পাইলেশন স্টেপ ছাড়া। এর মানে রুবি প্রথমে একটি প্রোগ্রাম কম্পাইল করে না একটি অপ্টিমাইজড মেশিন ল্যাঙ্গুয়েজ প্রোগ্রামে, যা কম্পাইলড ল্যাঙ্গুয়েজ যেমন C, Rust এবং Go করে।

রুবিতে, একটি প্রোগ্রাম প্রথমে রুবি VM-এর জন্য একটি ইনস্ট্রাকশন সেটে ট্রান্সলেট হয়, এবং তারপর তাৎক্ষণিক এক্সিকিউট হয়। এই ইনস্ট্রাকশনগুলো আপনার রুবি কোড এবং রুবি VM-এ এক্সিকিউট হচ্ছে কোডের মাঝপথে একটি ইন্টারমিডিয়েট স্টেপ। এই ইনস্ট্রাকশনগুলো রুবি VM-কে রুবি কোড বুঝতে সহজ করে সিনট্যাক্স-স্পেসিফিক ইন্টারপ্রিটেশন ছাড়া। সেটি হ্যান্ডেল হয় এই ইনস্ট্রাকশন তৈরি করার সময়। ইনস্ট্রাকশন সিকোয়েন্স অপ্টিমাইজড অপারেশন যা ইন্টারপ্রেটেড কোডকে রিপ্রেজেন্ট করে।

এক রুবি প্রোগ্রামের নরমাল এক্সিকিউশনের সময় আমরা এই ইনস্ট্রাকশনগুলো দেখি না, কিন্তু InstructionSequence দিয়ে আমরা দেখতে পারি YARV এক্সিকিউট করার আগে কী ধরনের ইনস্ট্রাকশন তৈরি করে।

YARV ইনস্ট্রাকশনগুলো যেগুলো রুবি ইন্টারপ্রেটার বানায় সেগুলো সব বুঝতে হয় না। অধিকাংশ কমান্ড সেলফ-এক্সপ্লানেটরি।

"foo" + "bar"
RubyVM::InstructionSequence.compile('"foo" + "bar"').to_a
# ...
[:putstring, "foo"],
[:putstring, "bar"]
...
# versus
"foo" \ "bar"
RubyVM::InstructionSequence.compile('"foo" "bar"').to_a
# ...
[:putstring, "foobar"]
...

রিয়েল আউটপুটে কিছুটা বেশি সেটআপ কমান্ড আছে যা আমরা পরে দেখব, কিন্তু এখানে আমরা "foo" + "bar" এবং "foo" "bar"-র আসল পার্থক্য দেখতে পাচ্ছি।

পুরনোটি দুটি স্ট্রিং তৈরি করে এবং সেগুলো কম্বাইন করে। নতুনটি একটি স্ট্রিং তৈরি করে। এর মানে "foo" "bar"-এ আমরা শুধু একটি স্ট্রিং তৈরি করি, "foo" + "bar"-এ তিনটি স্ট্রিং তৈরির বদলে।

 1 2 3
↓ ↓ ↓
"foo" + "bar" # => "foobar"

অবশ্যই, এটা শুধু সবচেয়ে বেসিক উদাহরণ যা আমরা ব্যবহার করতে পারি, কিন্তু এটা দেখাচ্ছে একটি ছোট ডিটেইল রুবি ল্যাঙ্গুয়েজে কীভাবে পোটেনশিয়ালি বড় 임প্যাক্ট থাকতে পারে:

  • আরো alokেশন: প্রতিটি String অবজেক্ট আলাদা আলাদা alok হয়।
  • আরো মেমরি ব্যবহার: প্রতিটি alok করা String অবজেক্ট মেমরি নেয়।
  • লম্বা গারবেজ কালেকশন: প্রতিটি অবজেক্ট, এমনকি শর্ট-লিভড, গারবেজ কলেক্টর দ্বারা ক্লিন হওয়াতে সময় নেয়। আরও alokেশন মানে লম্বা গারবেজ কালেকশন টাইম।

ডিসঅ্যাসেম্বলিং

অন্য একটি ইউজ কেস হলো লজিক ইস্যু ডিবাগ করা। নিচের একটি সহজ মিস্টেক যা বড় ফলাফল দিতে পারে। পার্থক্য বের করতে পারেন?

1 + 2 * 3
# versus
(1 + 2) * 3

আমরা রুবি ব্যবহার করতে পারি এই কিছুটা কমপ্লেক্স উদাহরণে পার্থক্য বের করতে।

এই কোড উদাহরণ ডিসঅ্যাসেম্বল করলে আমরা রুবিকে একটি বেশি রিডেবল টেবিল প্রিন্ট করতে বলতে পারি যা কমান্ডগুলো সেটি পারফর্ম করছে।

1 + 2 * 3 # => 7
puts RubyVM::InstructionSequence.compile("1 + 2 * 3").disasm
# == disasm: @>==========
# 0000 trace 1 ( 1)
# 0002 putobject_OP_INT2FIX_O_1_C_
# 0003 putobject 2
# 0005 putobject 3
# 0007 opt_mult 
# 0009 opt_plus 
# 0011 leave
# versus
(1 + 2) * 3 # => 9
puts RubyVM::InstructionSequence.compile("(1 + 2) * 3").disasm
# == disasm: @>==========
# 0000 trace 1 ( 1)
# 0002 putobject_OP_INT2FIX_O_1_C_
# 0003 putobject 2
# 0005 opt_plus 
# 0007 putobject 3
# 0009 opt_mult 
# 0011 leave

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

1 + 2-এর চারপাশে পেরেন্থেসিস দিয়ে আমরা নিশ্চিত করি এডিশনটি প্রথম পারফর্ম হয়, গাণিতিক অপারেশন অর্ডারে সেটি উপরে মুভ করিয়ে।

নোট করুন আপনি আসলে ডিসঅ্যাসেম্বলি আউটপুটে পেরেন্থেসিস দেখতে পাবেন না, শুধু বাকি কোডের ওপর তাদের ইফেক্ট।

ডিসঅ্যাসেম্বলি

ডিসঅ্যাসেম্বলি আউটপুট অনেক জিনিস প্রিন্ট করে যা তাৎক্ষণিক বোঝা যেতে পারে না।

যে টেবিল ফরম্যাটে প্রিন্ট হয়, প্রতিটি লাইন একটি অপারেশন নম্বর দিয়ে শুরু হয়। তারপর অপারেশন মেনশন করে এবং চূড়ান্তে অপারেশনের আর্গুমেন্ট।

অপারেশনগুলোর একটি ছোট স্যাম্পল যা আমরা 이제 পর্যন্ত দেখেছি:

  • trace - একটি ট্রেস শুরু করুন। আরও ইনফরমেশনের জন্য TracePoint ডক্স দেখুন।
  • putobject - স্টাকে একটি অবজেক্ট পুশ করুন।
  • putobject_OP_INT2FIX_O_1_C_ - ইন্টিজার 1 স্টাকে পুশ করুন। অপ্টিমাইজড অপারেশন। (0 এবং 1 অপ্টিমাইজড।)
  • putstring - স্টাকে একটি স্ট্রিং পুশ করুন।
  • opt_plus - অ্যাডিশন অপারেশন (ইন্টারন্যালি অপ্টিমাইজড)।
  • opt_mult - মাল্টিপ্লাই অপারেশন (ইন্টারন্যালি অপ্টিমাইজড)।
  • leave - বর্তমান কোড কনটেক্সট ছেড়ে দিন।

এখন যেহেতু আমরা জানি কীভাবে রুবি ইন্টারপ্রেটার আমাদের ডেভেলপার-ফ্রেন্ডলি এবং রিডেবল রুবি কোডকে YARV ইনস্ট্রাকশনে কনভার্ট করে, আমরা এটা ব্যবহার করতে পারি আমাদের অ্যাপ্লিকেশন অপ্টিমাইজ করতে।

পুরো মেথড এবং এমনকি পুরো ফাইল পাস করা যায় RubyVM::InstructionSequence-এ।

puts RubyVM::InstructionSequence.disasm(method(:foo))
puts RubyVM::InstructionSequence.compile_file("/tmp/hello.rb").disasm

কেন কিছু কোড টুকরো কাজ করে এবং অন্য না সেটা বের করুন। কেন নির্দিষ্ট সিম্বল কোডকে অন্যদের চেয়ে ভিন্ন আচরণ করিয়ে সেটা শিখুন। ডיטיেলসে ডেভিল আছে, এবং জানা ভালো আপনার রুবি কোড আপনার অ্যাপে কীভাবে আচরণ করছে এবং আপনি সেটি কোনোভাবে অপ্টিমাইজ করতে পারেন কিনা।

অপ্টিমাইজেশন

আপনার কোড ইন্টারপ্রেটার লেভেলে দেখে অপ্টিমাইজ করার পাশাপাশি, আপনি InstructionSequence ব্যবহার করে আপনার কোড আরও বেশি অপ্টিমাইজ করতে পারেন।

InstructionSequence দিয়ে, নির্দিষ্ট ইনস্ট্রাকশন রুবির বিল্ট-ইন পারফরম্যান্স অপ্টিমাইজেশনের সাথে অপ্টিমাইজ করা সম্ভব। পূর্ণ উপলব্ধ অপ্টিমাইজেশন তালিকা RubyVM::InstructionSequence.compile_option = মেথড ডকুমেন্টেশনে পাওয়া যাবে।

এই অপ্টিমাইজেশনগুলোতে একটি হলো টেইল কল অপ্টিমাইজেশন।

RubyVM::InstructionSequence.compile মেথড অপশন গ্রহণ করে এই অপ্টিমাইজেশন ইনেবল করার জন্য যেভাবে:

some_code = <<-EOS
def fact(n, acc=1)
  return acc if n <= 1
  fact(n-1, n*acc)
end
EOS
puts RubyVM::InstructionSequence.compile(some_code, nil, nil, nil, tailcall_optimization: true, trace_instruction: false).disasm
RubyVM::InstructionSequence.compile(some_code, nil, nil, nil, tailcall_optimization: true, trace_instruction: false).eval

আপনি আপনার সব কোডের জন্য এই অপ্টিমাইজেশন অন করতে পারেন RubyVM::InstructionSequence.compile_option = দিয়ে। শুধু নিশ্চিত করুন আপনার অন্য কোনো কোড লোড করার আগে এটা লোড হয়।

RubyVM::InstructionSequence.compile_option = { tailcall_optimization: true, trace_instruction: false }

রুবিতে টেইল কল অপ্টিমাইজেশন কীভাবে কাজ করে সেটা সম্পর্কে আরও ইনফরমেশনের জন্য এই আর্টিকেলগুলো চেক আউট করুন: রুবিতে টেইল কল অপ্টিমাইজেশন এবং রুবিতে টেইল কল অপ্টিমাইজেশন: ব্যাকগ্রাউন্ড।

উপসংহার

RubyVM::InstructionSequence দিয়ে আপনার কোড রুবি কীভাবে ইন্টারপ্রেট করে সেটা আরও জানুন এবং দেখুন আপনার কোড আসলে কী করছে যাতে আপনি সেটি আরও পারফরম্যান্ট করতে পারেন।

InstructionSequence-র এই ইন্ট্রোডাকশন হতে পারে রুবি কীভাবে হাড আন্ডারে কাজ করে সেটা শিখার একটি মজার উপায়। কে জানে? আপনি হয়তো রুবির কিছু কোডে কাজ করার আগ্রহী হন।

রুবিতে কোড কম্পাইলেশনের আমাদের সংক্ষিপ্ত ইন্ট্রোডাকশন এখানেই সমাপ্তি। আমরা জানতে চাই আপনি এই আর্টিকেল কেমন লাগলো, আপনার কোনো প্রশ্ন আছে কিনা, এবং আপনি কী নিয়ে পড়তে চান, তাই @AppSignal-এ আমাদের জানাতে ভুলবেন না।

  1. রুবি মেথড ব্যাখ্যা: কাস্টম ফাংশন সহজে তৈরি করুন

  2. রুবি কোড উন্নত করুন: ক্লিনার OOP ডিজাইনের জন্য Cohesion & Coupling আয়ত্ত করুন

  3. Ruby-এ TracePoint দিয়ে মেথড ট্রেস করা: একটি ব্যবহারিক গাইড

  4. রুবি ডিবাগিং মাস্টার করুন: কমন এররর দ্রুত সমাধান