যখন কোন টিসিও নেই, কখন স্ট্যাকটি ফুঁকানোর বিষয়ে চিন্তা করবেন?


14

প্রতি একক সময় জেভিএমকে লক্ষ্য করে একটি নতুন প্রোগ্রামিং ভাষা নিয়ে আলোচনা হয়, অনিবার্যভাবে লোকেরা এমন কথা বলে থাকে:

"জেভিএম টেল-কল অপ্টিমাইজেশান সমর্থন করে না, তাই আমি প্রচুর বিস্ফোরক স্ট্যাকের পূর্বাভাস দিয়েছি"

থিমটিতে হাজার হাজার বৈচিত্র রয়েছে।

এখন আমি জানি যে উদাহরণস্বরূপ ক্লোজুরের মতো কিছু ভাষাতে একটি বিশেষ পুনরুক্তি রয়েছে যা আপনি ব্যবহার করতে পারেন।

আমি যা বুঝতে পারি না তা হ'ল: টেল-কল অপ্টিমাইজেশনের অভাব কতটা গুরুতর? আমি কখন এটা নিয়ে চিন্তা করব?

আমার বিভ্রান্তির মূল উত্স সম্ভবত এ থেকে এসেছে যে জাভা এখন পর্যন্ত অন্যতম সফল ভাষা এবং জেভিএম ভাষায় বেশ কয়েকটি ভাষা মোটামুটি ভাল করে চলেছে বলে মনে হয়। কিভাবে যে সম্ভব হলে TCO অভাবের সত্যিই কোন উদ্বেগ?


4
যদি আপনার টিসিও ছাড়াই স্ট্যাকটি ফুটিয়ে তুলতে যথেষ্ট গভীর পুনরাবৃত্তি হয় তবে আপনার টিসিও নিয়েও সমস্যা হবে
ratchet freak

18
@ratchet_freak এটি বাজে কথা। স্কিমের এমনকি লুপগুলি নেই, তবে কারণ টিপিসো সমর্থন টিপস সমর্থন দেয়, একটি বড় সংখ্যক ডেটের উপর পুনরাবৃত্ত পুনরাবৃত্তি একটি অপরিহার্য লুপের চেয়ে বেশি ব্যয়বহুল নয় (যে বোনাসটি যে স্কিমটি নির্মাণ করে একটি মান দেয়)।
ব্রুস

6
@ratchetfreak টিসিও হ'ল একটি পদ্ধতি যা নির্দিষ্ট পদ্ধতিতে লিখিত পুনরাবৃত্ত ক্রিয়াকলাপগুলি তৈরি করার জন্য (যেমন পুচ্ছ-পুনরাবৃত্তভাবে) তারা চাইলেও স্ট্যাকটি ফুঁতে দিতে পুরোপুরি অক্ষম হবে। আপনার বিবৃতিটি কেবল পুনরাবৃত্তির জন্য অর্থবোধ করে যা লেজ-পুনরাবৃত্তভাবে লেখা হয়নি, সেক্ষেত্রে আপনি সঠিক এবং টিসিও আপনাকে সহায়তা করবে না।
এভিকাটোস

2
সর্বশেষে আমি দেখেছিলাম, ৮০x86 (টি (নেটিভ) টেল-কল অপটিমাইজেশনও করে না। তবে এটি ভাষা বিকাশকারীদের যে ভাষা ব্যবহার করে সেগুলি পোর্টিং করা থেকে বিরত রাখেনি। সংকলক শনাক্ত করে যে এটি কখন একটি জেসার বনাম একটি লাফ ব্যবহার করতে পারে এবং সবাই খুশি। আপনি একটি JVM এ একই জিনিস করতে পারেন।
kdgregory

3
@ কেডিগ্রিগরি: তবে x86 রয়েছে GOTO, জেভিএম তা দেয় না। এবং x86 ইন্টারপ প্ল্যাটফর্ম হিসাবে ব্যবহৃত হয় না। জেভিএমের নেই GOTOএবং জাভা প্ল্যাটফর্ম বেছে নেওয়ার প্রধান কারণগুলির মধ্যে একটি হ'ল আন্তঃআবিড়তা। আপনি যদি জেভিএম-তে টিসিও বাস্তবায়ন করতে চান তবে আপনাকে স্ট্যাকের জন্য কিছু করতে হবে। এটা নিজেকে পরিচালনা (অর্থাত এ সব জেভিএম কল স্ট্যাক ব্যবহার করবেন না), ব্যবহার trampolines, হিসাবে ব্যবহারের ব্যতিক্রম GOTO, যে ভালো কিছু। এই সমস্ত ক্ষেত্রে, আপনি জেভিএম কল স্ট্যাকের সাথে বেমানান হয়ে যান। জাভার সাথে স্ট্যাক-সামঞ্জস্যপূর্ণ হওয়া অসম্ভব, টিসিও এবং উচ্চ কার্যকারিতা রয়েছে। আপনাকে এই তিনজনের মধ্যে একজনকে ত্যাগ করতে হবে।
Jörg ডব্লু মিটাগ

উত্তর:


16

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

int factorial(int i){ return factorial(i, 1);}
int factorial(int i, int accum){
  if(i == 0) return accum;
  return factorial(i-1, accum * i);
}

এখন আমরা বেশ চালাক বোধ করছি, আমরা লুপ ছাড়াই আমাদের ফ্যাক্টরিয়ালটি লিখতে পেরেছি! কিন্তু যখন আমরা পরীক্ষা করি, আমরা লক্ষ্য করি যে কোনও যুক্তিসঙ্গত আকারের সংখ্যার সাথে, আমরা টিসিও নেই বলে স্ট্যাকওভারফ্লো ত্রুটিগুলি পাচ্ছি।

বাস্তব জাভাতে এটি কোনও সমস্যা নয়। আমাদের যদি কোনও লেজ পুনরাবৃত্তির অ্যালগরিদম থাকে তবে আমরা এটিকে একটি লুপে রূপান্তর করতে পারি এবং ঠিকঠাক হয়ে উঠতে পারি। যাইহোক, কোন লুপ নেই এমন ভাষাগুলি সম্পর্কে কী? তারপরে আপনি ঠিক হোজেড এই কারণেই ক্লোজারের এই recurফর্মটি রয়েছে, এগুলি ছাড়া এটি সম্পূর্ণ ট্যুরিংও হয় না (অসীম লুপগুলি করার কোনও উপায় নেই)।

জেভিএম, ফ্রেইজ, কাওয়া (স্কিম), ক্লোজুর লক্ষ্য করে এমন ক্রিয়ামূলক ভাষাগুলির শ্রেণি সর্বদা লেজ কলগুলির অভাবকে মোকাবেলা করার চেষ্টা করে, কারণ এই ভাষাগুলিতে টিসি লুপগুলি করার মূখ্য উপায়! যদি স্কীমে অনুবাদ করা হয়, তবে উপরে বর্ণনাকারীটি একটি ভাল ফ্যাক্টরিয়াল হবে। 5000 বার লুপ করা আপনার প্রোগ্রামটি ক্র্যাশ করে দিলে এটি অত্যন্ত অসুবিধে হতে পারে। এটি প্রায়শই কাজ করা যেতে পারে, recurবিশেষ ফর্ম সহ, টীকাগুলি নিজের কলগুলি অনুকূল করতে, ট্রাম্পোলাইনিংয়ে যা কিছু হোক না কেন। তবে তারা সবাই প্রোগ্রামারের উপর পারফরম্যান্স হিট বা অপ্রয়োজনীয় কাজকে বাধ্য করে।

এখন জাভাও নিখরচায় যায় না, যেহেতু টিসিও-তে আরও কিছু রয়েছে কেবল পুনরাবৃত্তি, পারস্পরিক পুনরাবৃত্তি ফাংশনগুলির কী? এগুলি লুপগুলিতে সোজাভাবে অনুবাদ করা যায় না, তবে এখনও জেভিএম দ্বারা বিনা প্রতিশ্রুতিবদ্ধ। এটি জাভা ব্যবহার করে পারস্পরিক পুনরাবৃত্তি ব্যবহার করে অ্যালগরিদমগুলি লেখার চেষ্টা করা দর্শনীয়ভাবে অপ্রীতিকর করে তোলে যদি আপনি শালীন পারফরম্যান্স / ব্যাপ্তি চান তবে এটি লুপগুলিতে ফিট করার জন্য আপনাকে গা magic় যাদু করতে হবে।

সুতরাং, সংক্ষেপে, এটি অনেক ক্ষেত্রে একটি বিশাল চুক্তি নয়। বেশিরভাগ লেজ কলগুলি কেবল একটি স্ট্যাকফ্রেমে গভীরতরভাবে এগিয়ে যায়, এর মতো জিনিসগুলি সহ

return foo(bar, baz); // foo is just a simple method

বা পুনরাবৃত্তি হয়। তবে, টিসি যে শ্রেণীর সাথে এটি খাপ খায় না তাদের জন্য প্রতিটি জেভিএম ভাষা ব্যথা অনুভব করে।

তবে, আমাদের এখনও টিসিও না থাকার একটি শালীন কারণ রয়েছে। জেভিএম আমাদের স্ট্যাক ট্রেস দেয়। টিসিওর সাহায্যে আমরা পদ্ধতিগতভাবে স্ট্যাকফ্রেমগুলি মুছে ফেলি যা আমরা জানি "ডুমুড", তবে জেভিএম সম্ভবত স্ট্যাকট্রেসের জন্য পরে এগুলি চাইবে! বলুন আমরা এর মতো একটি এফএসএম বাস্তবায়ন করি, যেখানে প্রতিটি রাজ্য পরেরটি টেল-কল করে। আমরা পূর্ববর্তী সমস্ত রাজ্যের সমস্ত রেকর্ড মুছে ফেলব যাতে কোনও ট্রেসব্যাক আমাদের কী অবস্থা তা দেখায় তবে সেখানে কীভাবে পৌঁছেছে সে সম্পর্কে কিছুই নয়।

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


2
বৃহত্তম সমস্যাটি বাইট কোড যাচাইকারী, যা সম্পূর্ণরূপে স্ট্যাক পরিদর্শনের উপর ভিত্তি করে। এটি জেভিএম স্পেসিফিকেশনের একটি বড় বাগ। 25 বছর আগে, যখন জেভিএম ডিজাইন করা হয়েছিল, লোকেরা ইতিমধ্যে বলেছিল যে ভাষাটি অনিরাপদ হওয়ার চেয়ে প্রথমে জেভিএম বাইট কোড ভাষাটি নিরাপদ থাকা ভাল এবং পরে সত্যতার পরে বাইট কোড যাচাইকরণের উপর নির্ভর করা ভাল। তবে ম্যাথিয়াস ফেলেলিসেন (স্কিম সম্প্রদায়ের অন্যতম শীর্ষস্থানীয়) একটি কাগজ লিখেছিলেন যাতে বাইট কোড যাচাইকারী সংরক্ষণ করার সময় কীভাবে জেভিএম-এ লেজ কলগুলি যুক্ত করা যায়।
Jörg ডব্লু মিটাগ

2
মজার বিষয় হচ্ছে, আইবিএম দ্বারা জে 9 জেভিএম টিসিও সম্পাদন করে
Jörg ডব্লু মিটাগ

1
@ জোজেফগ মজার বিষয় হল, কেউ লুপগুলির জন্য স্ট্যাকট্রেস এন্ট্রিগুলি সম্পর্কে চিন্তা করে না, সুতরাং স্ট্যাকট্রেস যুক্তিতে কমপক্ষে লেজ পুনরাবৃত্তি ফাংশনগুলির জন্য জল ধরে না।
ইনগো

2
@ ম্যাসনভিয়েল এটি ঠিক আমার বক্তব্য: স্ট্যাকট্রেস আপনাকে জানায় না যে এটি কোন পুনরাবৃত্তিতে ঘটেছে। লুপ ভেরিয়েবল ইত্যাদি পরিদর্শন করে আপনি এটি কেবল পরোক্ষভাবে দেখতে পাচ্ছেন, তাই আপনি কেন একটি লেজ পুনরাবৃত্তির ক্রিয়াকলাপের বেশ কয়েকটি হান্ডার্ট স্ট্যাক ট্রেস এন্ট্রি চান? শুধুমাত্র শেষটি আকর্ষণীয়! এবং, লুপগুলির মতো, আপনি স্থানীয় ভার্যবেলস, আর্গুমেন্টের মান ইত্যাদি পরীক্ষা করে এটি নির্ধারণ করতে পারবেন
এঙ্গো

3
@ ইঙ্গো: যদি কোনও ফাংশন কেবল নিজের সাথে পুনরাবৃত্তি করে তবে স্ট্যাক ট্রেসটি বেশি দেখাতে পারে না। তবে, যদি কোনও গ্রুপের ফাংশনগুলি পারস্পরিকভাবে পুনরাবৃত্তি হয় তবে স্ট্যাক ট্রেসটি মাঝে মধ্যে একটি দুর্দান্ত চুক্তি প্রদর্শন করতে পারে।
সুপারক্যাট

7

টেল কল অপ্টিমাইজেশান মূলত লেজ পুনরাবৃত্তির কারণে গুরুত্বপূর্ণ। তবে, একটি যুক্তি রয়েছে কেন এটি আসলে ভাল যে জেভিএম লেজ কলগুলি অনুকূল করে না: টিসিও স্ট্যাকের একটি অংশ পুনরায় ব্যবহার করলে, ব্যতিক্রম থেকে স্ট্যাকের চিহ্নটি অসম্পূর্ণ হবে, এভাবে ডিবাগিংটি আরও শক্ত করা হবে।

জেভিএমের সীমাবদ্ধতার আশপাশে কাজ করার উপায় রয়েছে:

  1. সাধারণ লেজ পুনরাবৃত্তি সংকলক দ্বারা একটি লুপ অনুকূল করা যেতে পারে।
  2. যদি প্রোগ্রামটি ধারাবাহিকতা-পাসিং শৈলীতে থাকে, তবে এটি "ট্রাম্পোলাইনিং" ব্যবহার করা তুচ্ছ। এখানে, একটি ফাংশন শেষ ফলাফলটি দেয় না, তবে একটি ধারাবাহিকতা যা পরে বাইরেতে কার্যকর করা হয়। এই কৌশলটি কোনও সংকলক লেখককে নির্বিচারে জটিল নিয়ন্ত্রণ প্রবাহকে মডেল করতে দেয়।

এর জন্য আরও বড় উদাহরণের প্রয়োজন হতে পারে। ক্লোজারগুলি (যেমন জাভাস্ক্রিপ্ট বা অনুরূপ) সহ একটি ভাষা বিবেচনা করুন। আমরা কারণ হিসাবে লিখতে পারি

def fac(n, acc = 1) = if (n <= 1) acc else n * fac(n-1, acc*n)

print fac(x)

এখন আমরা এটির পরিবর্তে একটি কলব্যাক ফিরিয়ে আনতে পারি:

def fac(n, acc = 1) =
  if (n <= 1) acc
  else        (() => fac(n-1, acc*n))  // this isn't full CPS, but you get the idea…

var continuation = (() => fac(x))
while (continuation instanceof function) {
  continuation = continuation()
}
var result = continuation
print result

এটি এখন ধ্রুব স্ট্যাক স্পেসে কাজ করে যা একধরণের নিরীহ কারণ এটি যাইহোক লেজ-পুনরাবৃত্ত হয়। তবে এই কৌশলটি সমস্ত লেজ কলকে ধ্রুব স্ট্যাক স্পেসে সমতল করতে সক্ষম । এবং প্রোগ্রামটি যদি সিপিএসে থাকে, তবে এর অর্থ হ'ল কলস্ট্যাক সামগ্রিকভাবে স্থির থাকে (সিপিএসে, প্রতিটি কল একটি লেজ কল)।

এই কৌশলটির একটি বড় অসুবিধা হ'ল এটি ডিবাগ করা আরও শক্ত, বাস্তবায়ন করা কিছুটা কঠিন এবং কম পারফরম্যান্ট - আমি যে সমস্ত ক্লোজার এবং দিকনির্দেশ ব্যবহার করছি তা দেখুন।

এই কারণগুলির জন্য জাভা যেমন টেল কল পছন্দ করে না - ভিএম একটি লেজ কল অপটিকে বাস্তবায়িত করা আরও বেশি পছন্দনীয় - যাতে টেল কলগুলি সমর্থন না করার উপযুক্ত কারণ রয়েছে এটি ব্যবহার করতে হবে না।


1
"যেমন টিসিও স্ট্যাকের একটি অংশ পুনরায় ব্যবহার করে, একটি ব্যতিক্রম থেকে স্ট্যাক ট্রেস অসম্পূর্ণ হবে," - হ্যাঁ, তবে, তবে একটি লুপের মধ্য থেকে একটি স্ট্যাকট্রেস অসম্পূর্ণ - এটি লুপটি কতবার কার্যকর হয়েছিল তা রেকর্ড করে না। - হায়, জেভিএম যথাযথ টেল কল সমর্থন করেও, তবুও কেউ ডিবাগিংয়ের সময় বেছে নিতে পারেন say এবং তারপরে, উত্পাদনের জন্য, টিসিও এটি নিশ্চিত করতে সক্ষম করুন যে কোডটি 100,000 বা 100,000,000 টিলে কল চলে runs
ইঙ্গো

1
@ ইঙ্গো নং (১) যখন লুপগুলি পুনরাবৃত্তি হিসাবে প্রয়োগ করা হয় না, তখন তাদের স্ট্যাক (টেইল কল-জাম্প ≠ কল) দেখানোর কোনও যুক্তি নেই is (২) টিসিও লেজ পুনরাবৃত্তি অপ্টিমাইজেশনের চেয়ে বেশি সাধারণ। আমার উত্তরটি উদাহরণ হিসাবে পুনরাবৃত্তি ব্যবহার করে । (৩) আপনি যদি এমন স্টাইলে প্রোগ্রামিং করেন যা টিসিওর উপর নির্ভর করে তবে এই অপ্টিমাইজেশনটি স্যুইচ করা কোনও বিকল্প নয় - পূর্ণ টিসিও বা পূর্ণ স্ট্যাকের চিহ্নগুলি কোনও ভাষা বৈশিষ্ট্য, বা সেগুলি নয়'re উদাহরণস্বরূপ স্কিম আরও উন্নত ব্যতিক্রম সিস্টেমের সাথে টিসিও ত্রুটিগুলিতে ভারসাম্য রক্ষা করে।
আমন

1
(1) পুরোপুরি একমত। কিন্তু একই যুক্তি দ্বারা, শত এবং হাজার হাজার স্ট্যাক ট্রেস এন্ট্রি রাখার কোনও যুক্তি নেই যা return foo(....);পদ্ধতিতে foo(2) পুরোপুরি সম্মত, অবশ্যই। তবুও, আমরা লুপস, অ্যাসাইনমেন্ট (!), বিবৃতি ক্রমগুলি থেকে অসম্পূর্ণ ট্রেসিং গ্রহণ করি। উদাহরণস্বরূপ, আপনি যদি কোনও ভেরিয়েবলের মধ্যে অপ্রত্যাশিত মান খুঁজে পান তবে আপনি অবশ্যই জানতে চান যে এটি কীভাবে পেয়েছে। তবে আপনি সেই ক্ষেত্রে নিখোঁজ থাকা সম্পর্কে অভিযোগ করবেন না। কারণ এটি কোনওভাবেই আমাদের মস্তিস্কে খোদাই করা হয়েছে যে ক) এটি কেবল কলগুলিতে ঘটে থাকে খ) এটি সমস্ত কলগুলিতে ঘটে। আইএমএইচও, উভয়ই বোধগম্য নয়।
ইগো

(3) অসমত। আকারের এন এর একটি সমস্যা নিয়ে আমার কোডটি ডিবাগ করা কেন অসম্ভব, তার কোনও কারণ আমি দেখতে পাচ্ছি না, কিছু সাধারণ এন স্ট্যাকের সাথে দূরে সরে যাওয়ার জন্য যথেষ্ট ছোট N এবং তারপরে, স্যুইচটি চালু করতে এবং টিসিও চালু করতে - কার্যকরভাবে প্রোবের আকারের উপর সীমাবদ্ধতা ফেলে দেওয়া।
ইঙ্গো

@ ইঙ্গো "অসম্মতি। আকারের এন এর একটি সমস্যা নিয়ে আমার কোডটি ডিবাগ করা কেন অসম্ভব, তার কোনও কারণ আমি দেখতে পাচ্ছি না, কিছু সাধারণ এন স্ট্যাকের সাথে দূরে সরে যাওয়ার জন্য যথেষ্ট ছোট N " যদি টিসিও / টিসিই কোনও সিপিএস রূপান্তরের জন্য হয়, তবে এটিকে বন্ধ করে দেওয়া স্ট্যাকটিকে উপচে ফেলবে এবং প্রোগ্রামটি ক্র্যাশ করবে, সুতরাং কোনও ডিবাগিং সম্ভব হবে না। গুগল ভি 8 জেএস-তে টিসিও বাস্তবায়ন করতে অস্বীকার করেছিল, কারণ এই সমস্যাটি ঘটনাক্রমে ঘটেছিল । তারা কিছু বিশেষ বাক্য গঠন চাইবে যাতে প্রোগ্রামার ঘোষণা করতে পারে যে সে আসলেই টিসিও চায় এবং স্ট্যাকের ট্রেসটি হারাতে চায়। টিসিও দ্বারাও ব্যাতিক্রম হয় কিনা তা কি কেউ জানেন?
শেলবি মুর তৃতীয়

6

একটি প্রোগ্রামে কলগুলির একটি উল্লেখযোগ্য অংশ হ'ল লেজ কল। প্রতিটি সাব্রোটিনের একটি শেষ কল থাকে, তাই প্রতিটি সাব্রোটিনের কমপক্ষে একটি লেজ কল থাকে। টেইল কলগুলির কার্যকারিতা বৈশিষ্ট্যগুলি রয়েছে GOTOতবে সাবউটাইন কলের সুরক্ষা।

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

পিটিসি ব্যতীত, আপনাকে GOTOনিয়ন্ত্রণ প্রবাহ বা এর মতো কিছু হিসাবে ট্রাম্পোলাইনস বা ব্যতিক্রমগুলি ব্যবহার করতে হবে। এটি অনেক কুরুচিপূর্ণ, এবং রাষ্ট্র মেশিনের সরাসরি 1: 1 উপস্থাপনা নয়।

(মনে রাখবেন কীভাবে আমি বোরিং "লুপ" উদাহরণটি চতুরতার সাথে ব্যবহার করে এড়িয়ে গেছি This এটি এমন উদাহরণ যেখানে পিটিসিগুলি এমনকি লুপগুলির সাথে কোনও ভাষায়ও কার্যকর ))

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


The vast majority of calls in a program are tail calls. যদি না বলা হয় "বিশাল সংখ্যাগরিষ্ঠ" পদ্ধতিগুলি তাদের নিজস্ব একাধিক কল সম্পাদন করে। Every subroutine has a last call, so every subroutine has at least one tail call. এই মিথ্যা হিসাবে জাভাস্ক্রিপ্টে গার্বেজ প্রমাণযোগ্য হল: return a + b। (আপনি যদি এমন কিছু উন্মাদ ভাষায় না থাকেন যেখানে প্রাথমিক গাণিতিক ক্রিয়াকলাপগুলি অবশ্যই ফাংশন কল হিসাবে সংজ্ঞায়িত করা হয়))
ম্যাসন হুইলার

1
"দুটি সংখ্যা যুক্ত করা দুটি সংখ্যা যুক্ত করছে।" যেখানে ভাষা নেই সেগুলি বাদে। লিস্প / স্কিমের + অপারেশন সম্পর্কে কী বলা যায় যেখানে কোনও একক পাটিগণিত অপারেটর একটি নির্বিচারে সংখ্যক যুক্তি নিতে পারে? (+ 1 2 3) এটি কার্যকর করার একমাত্র বুদ্ধিমান উপায় হ'ল ফাংশন হিসাবে।
এভিকাটোস

1
@ ম্যাসন হুইলারের: বিমূর্ত বিপরীতটি বলতে কী বোঝ?
জর্জিও

1
@ ম্যাসনহিলার এটি কোনও সন্দেহ নেই, কোনও প্রযুক্তিগত বিষয় যা আমি কখনও দেখেছি তার সর্বাধিক হাতের .েউ উইকিপিডিয়ায় প্রবেশ entry আমি কিছু সন্দেহজনক এন্ট্রি দেখেছি তবে তা ঠিক ... বাহ!
এভিকাটোস

1
@ ম্যাসনওহিলার: আপনি লিস্পের 22 এবং 23 পৃষ্ঠাগুলিতে তালিকা দৈর্ঘ্যের ফাংশনগুলির বিষয়ে কথা বলছেন? টেল-কল সংস্করণ প্রায় 1.2x জটিল হিসাবে, 3x এর কাছাকাছি কোথাও নেই। বিমূর্ত বিপর্যয় বলতে কী বোঝাতে চেয়েছি সে সম্পর্কেও আমি অস্পষ্ট।
মাইকেল শ

4

"জেভিএম টেল-কল অপ্টিমাইজেশান সমর্থন করে না, তাই আমি প্রচুর বিস্ফোরক স্ট্যাকের পূর্বাভাস দিয়েছি"

যে কেউ এটি (1) বলে সে লেজ-কল অপ্টিমাইজেশন বুঝতে পারে না, বা (2) জেভিএম, বা (3) উভয়ই বুঝতে পারে না।

আমি উইকিপিডিয়া থেকে টেইল-কলগুলির সংজ্ঞা দিয়ে শুরু করব (আপনি যদি উইকিপিডিয়া পছন্দ না করেন তবে এখানে একটি বিকল্প আছে ):

কম্পিউটার বিজ্ঞানে, একটি টেইল কল হ'ল একটি সাবরুটিন কল যা এটির চূড়ান্ত ক্রিয়া হিসাবে অন্য পদ্ধতির অভ্যন্তরে ঘটে; এটি একটি রিটার্ন মান উত্পাদন করতে পারে যা তত্ক্ষণাত কলিং পদ্ধতি দ্বারা ফিরে আসে

নীচের কোডে, কলটি bar()হল এর লেজ কল foo():

private void foo() {
    // do something
    bar()
}

টেইল কল অপ্টিমাইজেশন ঘটে যখন ভাষা প্রয়োগ, একটি লেজ কল দেখে সাধারণ পদ্ধতি আহবান (যা একটি স্ট্যাক ফ্রেম তৈরি করে) ব্যবহার করে না, পরিবর্তে একটি শাখা তৈরি করে। এটি একটি অপ্টিমাইজেশন কারণ স্ট্যাক ফ্রেমের জন্য মেমরির প্রয়োজন হয় এবং ফ্রেমের উপরে তথ্য চাপানোর জন্য সিপিইউ চক্রের প্রয়োজন হয় এবং কারণ কল / রিটার্ন জুটিটি নিঃশর্ত জাম্পের চেয়ে বেশি সিপিইউ চক্রের জন্য অনুমান করা হয়।

টিসিও প্রায়শই পুনরাবৃত্তির জন্য প্রয়োগ করা হয় তবে এটি কেবল এটির ব্যবহার নয়। বা এটি সমস্ত পুনরাবৃত্তির ক্ষেত্রে প্রযোজ্য নয়। একটি ফ্যাক্টরিয়াল গণনা করার জন্য সহজ পুনরাবৃত্ত কোড, উদাহরণস্বরূপ, টেল-কল অনুকূলিতকরণ করা যায় না, কারণ ফাংশনে ঘটে যাওয়া শেষ জিনিসটি একটি গুণ গুণ operation

public static int fact(int n) {
    if (n <= 1) return 1;
    else return n * fact(n - 1);
}

টেল কল অপ্টিমাইজেশন বাস্তবায়নের জন্য আপনার দুটি জিনিস প্রয়োজন:

  • একটি প্ল্যাটফর্ম যা সাবট্রোটিন কলগুলির পাশাপাশি শাখা প্রশাখাকে সমর্থন করে।
  • একটি স্ট্যাটিক বিশ্লেষক যা টেল কল অপ্টিমাইজেশন সম্ভব কিনা তা নির্ধারণ করতে পারে।

এটাই. আমি অন্য কোথাও উল্লেখ করেছি যে, জেভিএম (অন্য কোনও টিউরিং-সম্পূর্ণ আর্কিটেকচারের মতো) একটি গোটো আছে। এটি শর্তহীন গোটো হওয়ার কারণে ঘটে থাকে তবে শর্তাধীন শাখা ব্যবহার করে কার্যকারিতাটি সহজেই প্রয়োগ করা যেতে পারে।

স্থিতিশীল বিশ্লেষণ টুকরটি কি জটিল। একক ফাংশনের মধ্যে, এটি কোনও সমস্যা নয়। উদাহরণস্বরূপ, এখানে একটিতে মানগুলি যোগ করতে একটি লেজ-পুনরাবৃত্ত স্কেল ফাংশন রয়েছে List:

def sum(acc:Int, list:List[Int]) : Int = {
  if (list.isEmpty) acc
  else sum(acc + list.head, list.tail)
}

এই ফাংশনটি নিম্নলিখিত বাইটকোডে পরিণত হয়:

public int sum(int, scala.collection.immutable.List);
  Code:
   0:   aload_2
   1:   invokevirtual   #63; //Method scala/collection/immutable/List.isEmpty:()Z
   4:   ifeq    9
   7:   iload_1
   8:   ireturn
   9:   iload_1
   10:  aload_2
   11:  invokevirtual   #67; //Method scala/collection/immutable/List.head:()Ljava/lang/Object;
   14:  invokestatic    #73; //Method scala/runtime/BoxesRunTime.unboxToInt:(Ljava/lang/Object;)I
   17:  iadd
   18:  aload_2
   19:  invokevirtual   #76; //Method scala/collection/immutable/List.tail:()Ljava/lang/Object;
   22:  checkcast   #59; //class scala/collection/immutable/List
   25:  astore_2
   26:  istore_1
   27:  goto    0

goto 0শেষে নোট করুন । তুলনা করে, একটি সমতুল্য জাভা ফাংশন (যা অবশ্যই Iteratorএকটি স্কালার তালিকাটি মাথা এবং লেজে বিভক্ত করার অনুকরণের জন্য ব্যবহার করতে হবে ) নিম্নলিখিত বাইকোডে রূপান্তরিত হয়। নোট যে গত দুটি অপারেশন এখন একটি হয় ডাকা , যে রিকার্সিভ আবাহন দ্বারা উত্পাদিত মান একটি সুনির্দিষ্ট রিটার্ন দ্বারা অনুসরণ করে।

public static int sum(int, java.util.Iterator);
  Code:
   0:   aload_1
   1:   invokeinterface #64,  1; //InterfaceMethod java/util/Iterator.hasNext:()Z
   6:   ifne    11
   9:   iload_0
   10:  ireturn
   11:  iload_0
   12:  aload_1
   13:  invokeinterface #70,  1; //InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object;
   18:  checkcast   #25; //class java/lang/Integer
   21:  invokevirtual   #74; //Method java/lang/Integer.intValue:()I
   24:  iadd
   25:  aload_1
   26:  invokestatic    #43; //Method sum:(ILjava/util/Iterator;)I
   29:  ireturn

একটি একক ফাংশন লেঙ্গুড় কল অপ্টিমাইজেশান তুচ্ছ হল: কম্পাইলার দেখতে পারেন কোন কোড কলের ফলাফলের ব্যবহার করে নেই, তাই এটি প্রতিস্থাপন করতে পারেন ডাকা একটি সঙ্গে goto

আপনার একাধিক পদ্ধতি থাকলে জীবনটি জটিল হয়ে ওঠে। জেভিএমের শাখা প্রশস্তকরণ নির্দেশাবলী, সাধারণ-উদ্দেশ্য প্রসেসরের মতো, যেমন 80x86, একক পদ্ধতিতে সীমাবদ্ধ। এটি এখনও অপেক্ষাকৃত সহজবোধ্য যদি আপনার ব্যক্তিগত পদ্ধতি থাকে: সংকলকটি সেই পদ্ধতিগুলিকে যথাযথভাবে ইনলাইন করতে বিনামূল্যে, তাই লেজ কলগুলি অনুকূল করতে পারে (যদি আপনি ভাবছেন যে এটি কীভাবে কাজ switchকরতে পারে তবে আচরণ নিয়ন্ত্রণের জন্য ব্যবহার করা একটি সাধারণ পদ্ধতি বিবেচনা করুন )। এমনকি আপনি এই কৌশলটি একই শ্রেণীর একাধিক পাবলিক পদ্ধতিতেও প্রসারিত করতে পারেন: সংকলকটি মেথড বডিগুলিকে ইনলাইন করে, পাবলিক ব্রিজের পদ্ধতি সরবরাহ করে এবং অভ্যন্তরীণ কলগুলি জাম্পগুলিতে পরিণত হয়।

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

কোনটি আমাকে আপনার প্রশ্নের দ্বিতীয় অংশে নিয়ে আসে, যা আমি "আমাদের যত্ন নেওয়া উচিত?"

স্পষ্টতই, যদি আপনার ভাষা পুনরাবৃত্তির জন্য এটির একমাত্র আদিম হিসাবে পুনরাবৃত্তি ব্যবহার করে তবে আপনি যত্নশীল। তবে, যে ভাষাগুলির জন্য এই বৈশিষ্ট্যটির প্রয়োজন রয়েছে তারা এটিকে প্রয়োগ করতে পারে; কেবল ইস্যুটি হ'ল কথিত ভাষার সংকলক এমন কোনও শ্রেণি তৈরি করতে পারে যা একটি যথেচ্ছ জাভা ক্লাস দ্বারা কল এবং কল করতে পারে।

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

যে কোডটির জন্য টেল-কল অপটিমাইজযোগ্য, সে কোডটি পুনরাবৃত্ত আকারে অনুবাদ করা প্রায়শই সোজা। উদাহরণস্বরূপ, sum()আমি যে ফাংশনটি আগে দেখিয়েছি তা সাধারণীকরণ করা যেতে পারে foldLeft()। যদি আপনি উত্সটি দেখুন , আপনি দেখতে পাবেন যে এটি আসলে একটি পুনরাবৃত্ত অপারেশন হিসাবে প্রয়োগ করা হয়েছে। জার্গ ডব্লু মিতাগের একটি ফাংশন কলের মাধ্যমে প্রয়োগ করা একটি রাষ্ট্রীয় মেশিনের উদাহরণ রয়েছে; প্রচুর দক্ষ (এবং রক্ষণাবেক্ষণযোগ্য) স্টেট মেশিন বাস্তবায়ন রয়েছে যা জাম্পগুলিতে অনুবাদ করা ফাংশন কলগুলির উপর নির্ভর করে না।

আমি সম্পূর্ণ ভিন্ন কিছু দিয়ে শেষ করব। আপনি যদি এসআইসিপি-র পাদটীকা থেকে গুগল করেন তবে আপনি এখানেই শেষ হতে পারেন । আমি ব্যক্তিগতভাবে আমার কম্পাইলার প্রতিস্থাপন থাকার চেয়ে যে একটি আরো অনেক আকর্ষণীয় জায়গা খুঁজে JSRদ্বারা JUMP


যদি একটি টেল-কল অপকোড বিদ্যমান থাকে, তবে কল করার পদ্ধতিটি কোনও কোড কার্যকর করার পরে প্রয়োজন হবে কিনা তা প্রতিটি কল সাইটে পর্যবেক্ষণ ছাড়া অন্য যে কোনও কারণের প্রয়োজন? এটি হতে পারে যে কিছু ক্ষেত্রে স্ট্যাকের সাথে সামঞ্জস্য করার জন্য এবং একটি লাফ চালানোর কোড তৈরির চেয়ে return foo(123);ইন-লাইনিংয়ের দ্বারা ভালো স্টেটমেন্ট কার্যকর করা যেতে পারে fooতবে টেল-কলটি সাধারণ কল থেকে কেন আলাদা হবে তা আমি দেখতে পাই না I যে বিষয়ে।
সুপারক্যাট

@ সুপের্যাট - আপনার প্রশ্নটি কী তা নিশ্চিত not এই পোস্টের প্রথম পয়েন্টটি হ'ল সংকলকটি জানতে পারে না যে সমস্ত সম্ভাব্য ক্যালির স্ট্যাক ফ্রেমটি দেখতে কেমন হতে পারে (মনে রাখবেন যে স্ট্যাক ফ্রেমটি কেবল ফাংশন আর্গুমেন্টকে নয় তার স্থানীয় ভেরিয়েবলগুলি ধারণ করে)। আমি মনে করি আপনি একটি অপকোড যুক্ত করতে পারেন যা সামঞ্জস্যপূর্ণ ফ্রেমের জন্য একটি রানটাইম চেক করে, তবে এটি আমাকে পোস্টের দ্বিতীয় অংশে নিয়ে আসে: আসল মূল্যটি কী?
কেডগ্রিগরি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.