কম্পিউটার

রুবি কিভাবে আপনার কোড ব্যাখ্যা করে তা দেখুন

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

চিহ্নগুলির মধ্যে একটি সূক্ষ্ম পার্থক্য

পূর্ববর্তী রুবি ম্যাজিক নিবন্ধে রুবিতে এস্কেপিং অক্ষর সম্পর্কে একটি উদাহরণ ছিল লাইন ব্রেক পালানোর বিষয়ে।

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

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

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

নির্দেশনা ক্রম

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

নীচের উদাহরণে যা দেওয়া হয়েছে তা হল রুবি কোড কারণ এটি YARV দোভাষী দ্বারা বোঝা যায়৷

YARV দোভাষী

YARV (এখনও আরেকটি রুবি VM) হল রুবি দোভাষী যা রুবি সংস্করণ 1.9-এ প্রবর্তিত হয়েছে, মূল দোভাষীকে প্রতিস্থাপন করেছে:MRI (Matz's Ruby Interpreter)।

যে ভাষাগুলি দোভাষী ব্যবহার করে সেগুলি একটি মধ্যবর্তী সংকলন পদক্ষেপ ছাড়াই সরাসরি কোড চালায়। এর মানে হল যে রুবি প্রথমে একটি অপ্টিমাইজড মেশিন ল্যাঙ্গুয়েজ প্রোগ্রামে একটি প্রোগ্রাম কম্পাইল করে না, যা সি, রাস্ট এবং গো ডু এর মতো ভাষাগুলিকে কম্পাইল করে৷

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

এই নির্দেশাবলী রুবি ভিএম-এর জন্য সিনট্যাক্স নির্দিষ্ট ব্যাখ্যার সাথে মোকাবিলা না করেই রুবি কোড বোঝা সহজ করে তোলে। এই নির্দেশাবলী তৈরি করার সময় এটি পরিচালনা করা হয়। নির্দেশের ক্রমগুলি হল অপ্টিমাইজ করা ক্রিয়াকলাপ যা ব্যাখ্যা করা কোডকে উপস্থাপন করে৷

একটি রুবি প্রোগ্রামের স্বাভাবিক সঞ্চালনের সময় আমরা এই নির্দেশাবলী দেখতে পাই না, তবে সেগুলি দেখে আমরা পর্যালোচনা করতে পারি যে রুবি আমাদের কোডটি সঠিকভাবে ব্যাখ্যা করেছে কিনা। 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"

অবশ্যই, এটি শুধুমাত্র সবচেয়ে মৌলিক উদাহরণ যা আমরা ব্যবহার করতে পারি, তবে এটি একটি ভাল ব্যবহারের ক্ষেত্রে দেখায় কিভাবে রুবি ভাষায় একটি ছোট বিবরণ সম্ভাব্যভাবে অনেক প্রভাব ফেলতে পারে:

  • আরো বরাদ্দ:প্রতিটি স্ট্রিং অবজেক্ট আলাদাভাবে বরাদ্দ করা হয়।
  • আরো মেমরি ব্যবহার:প্রতিটি বরাদ্দকৃত স্ট্রিং অবজেক্ট মেমরি গ্রহণ করে।
  • দীর্ঘ সময়ের আবর্জনা সংগ্রহ:প্রতিটি বস্তু, এমনকি স্বল্পস্থায়ী হলেও, আবর্জনা সংগ্রহকারী দ্বারা পরিষ্কার করতে সময় লাগে। বেশি বরাদ্দ মানে আবর্জনা সংগ্রহের সময় বেশি।

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

আরেকটি ব্যবহারের ক্ষেত্রে একটি যুক্তি সমস্যা ডিবাগ করা হয়। নিম্নলিখিতটি করা একটি সহজ ভুল, যা বড় পরিণতি হতে পারে। আপনি পার্থক্য খুঁজে পেতে পারেন?

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

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

এই কোডের উদাহরণটি বিচ্ছিন্ন করার মাধ্যমে আমরা রুবিকে এটি সম্পাদন করা কমান্ডগুলির একটি আরও পাঠযোগ্য টেবিল প্রিন্ট করতে পেতে পারি৷

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

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

1 + 2 এর চারপাশে বন্ধনী সহ আমরা নিশ্চিত করি যে সংযোজনটি প্রথমে সঞ্চালিত হয়েছে, এটিকে গণিতের ক্রিয়াকলাপের ক্রম উপরে নিয়ে যাওয়া।

মনে রাখবেন যে আপনি আসলে বন্ধনীগুলিকে আলাদা করা আউটপুটে দেখতে পাচ্ছেন না, শুধুমাত্র বাকি কোডের উপর তাদের প্রভাব৷

বিচ্ছিন্ন করা

Disassembly আউটপুট এমন অনেক কিছু প্রিন্ট করে যা তাৎক্ষণিকভাবে বোধগম্য নাও হতে পারে।

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

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

  • trace - একটি ট্রেস শুরু করুন. আরও তথ্যের জন্য ট্রেসপয়েন্টে ডক্স দেখুন।
  • 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. মৌলিক ওওপি নীতিগুলির সাথে আপনার রুবি কোডকে কীভাবে নাটকীয়ভাবে উন্নত করবেন

  3. আপনার রুবি পদ্ধতি গুপ্তচর কিভাবে

  4. কীভাবে আপনার রুবি প্রোগ্রামগুলি ডিবাগ এবং ঠিক করবেন