এটি আমার কাছে মনে হয় যেন লোকেরা কোনও goto
বক্তব্যকে অনেক অপছন্দ করে , তাই আমি এটি কিছুটা সোজা করার প্রয়োজন অনুভব করেছি।
আমি বিশ্বাস করি যে 'আবেগ' লোকেদের goto
শেষ পর্যন্ত সম্ভাব্য কার্য সম্পাদনের প্রভাব সম্পর্কে কোড বোঝার জন্য এবং (ভুল ধারণা) সম্পর্কে ফোটে। প্রশ্নের উত্তর দেওয়ার আগে, আমি প্রথমে এটি কীভাবে সংকলিত হয়েছে তার কয়েকটি বিশদ .ুকে যাব।
যেমনটি আমরা সবাই জানি, সি # আইএল-তে সংকলিত হয়, যা এসএসএ সংকলক ব্যবহার করে এসেম্বলারের জন্য সংকলিত হয়। আমি কীভাবে এটি সমস্ত কাজ করে সে সম্পর্কে কিছুটা অন্তর্দৃষ্টি দেব এবং তারপরে প্রশ্নের উত্তরটি নিজেই দেওয়ার চেষ্টা করব।
সি # থেকে আইএল পর্যন্ত
প্রথমে আমাদের সি # কোডের একটি টুকরা দরকার। সহজ শুরু করা যাক:
foreach (var item in array)
{
// ...
break;
// ...
}
হুডের নীচে কী ঘটে তা আপনাকে একটি ভাল ধারণা দেওয়ার জন্য আমি এই পদক্ষেপটি করব do
প্রথম অনুবাদ: foreach
সমতুল্য for
লুপ থেকে (দ্রষ্টব্য: আমি এখানে একটি অ্যারে ব্যবহার করছি, কারণ আমি আইডিস্পোসেবলের বিশদ পেতে চাই না - এই ক্ষেত্রে আমাকে একটি আইনুমেবলও ব্যবহার করতে হবে):
for (int i=0; i<array.Length; ++i)
{
var item = array[i];
// ...
break;
// ...
}
দ্বিতীয় অনুবাদ: একটি for
এবং break
সহজ সমতুল্যে অনুবাদ করা হয়েছে:
int i=0;
while (i < array.Length)
{
var item = array[i];
// ...
break;
// ...
++i;
}
তৃতীয়ত অনুবাদ (এই আইএল কোডের সমতূল্য): আমরা পরিবর্তন break
এবং while
একটি শাখা মধ্যে:
int i=0; // for initialization
startLoop:
if (i >= array.Length) // for condition
{
goto exitLoop;
}
var item = array[i];
// ...
goto exitLoop; // break
// ...
++i; // for post-expression
goto startLoop;
সংকলক একক পদক্ষেপে এই জিনিসগুলি করার সময় এটি আপনাকে প্রক্রিয়াটির অন্তর্দৃষ্টি দেয়। সি # প্রোগ্রাম থেকে যে আইএল কোডটি বিকশিত হয়েছিল তা হ'ল শেষ সি # কোডের আক্ষরিক অনুবাদ । আপনি নিজের জন্য এখানে দেখতে পারেন: https://dotnetfiddle.net/QaiLRz ('আইএল দেখুন' ক্লিক করুন)
এখন, আপনি এখানে একটি জিনিস লক্ষ্য করেছেন যে প্রক্রিয়া চলাকালীন, কোডটি আরও জটিল হয়ে ওঠে। এটি পর্যবেক্ষণ করার সবচেয়ে সহজ উপায়টি হ'ল একই জিনিসটি আকাকম্পল করতে আমাদের আরও বেশি সংখ্যক কোডের প্রয়োজন। এছাড়াও, আপনি এখানে তর্ক হতে পারে foreach
, for
, while
এবং break
জন্য আসলে স্বল্প হাত goto
, যা আংশিকভাবে সত্য।
আইএল থেকে এসেমব্লার পর্যন্ত
.NET JIT সংকলক একটি এসএসএ সংকলক। আমি এখানে এসএসএ ফর্মের সমস্ত বিশদ এবং কীভাবে একটি অপ্টিমাইজিং সংকলক তৈরি করব তা যাব না, এটি কেবলমাত্র অনেক বেশি, তবে কী হবে তা সম্পর্কে প্রাথমিক ধারণা দিতে পারি। আরও গভীর বোঝার জন্য, সংকলকগুলি অনুকূলকরণের উপর পড়া শুরু করা ভাল (একটি সংক্ষিপ্ত পরিচিতির জন্য আমি এই বইটি পছন্দ করি: http://ssabook.gforge.inria.fr/latest/book.pdf সংযোজকগুলি অনুকূলকরণের পরিচিতির ) এবং এলএলভিএম (llvm.org) ।
প্রতিটি অপ্টিমাইজ করা সংকলক কোডটি সহজ এবং ভবিষ্যদ্বাণীমূলক নিদর্শনগুলি অনুসরণ করে তার উপর নির্ভর করে । ফর লুপের ক্ষেত্রে, আমরা শাখা বিশ্লেষণ করতে গ্রাফ তত্ত্ব ব্যবহার করি এবং তারপরে আমাদের শাখাগুলিতে সাইক্লির মতো জিনিসগুলি (যেমন শাখা পিছনের দিকে) অনুকূলিত করি।
তবে আমাদের লুপগুলি বাস্তবায়নের জন্য আমাদের এখন ফরোয়ার্ড শাখা রয়েছে। আপনি যেমন অনুমান করতে পারেন, এটি আসলে জেআইটি প্রথম ধাপগুলি ঠিক করতে চলেছে, এর মতো:
int i=0; // for initialization
if (i >= array.Length) // for condition
{
goto endOfLoop;
}
startLoop:
var item = array[i];
// ...
goto endOfLoop; // break
// ...
++i; // for post-expression
if (i >= array.Length) // for condition
{
goto startLoop;
}
endOfLoop:
// ...
আপনি দেখতে পাচ্ছেন, আমাদের এখন একটি পশ্চাদপদ শাখা রয়েছে যা আমাদের ছোট লুপ। এখানে এখনও কদর্য একমাত্র জিনিসটি সেই শাখা যা আমরা আমাদের break
বক্তব্যের কারণে শেষ করেছিলাম । কিছু ক্ষেত্রে, আমরা এটিকে একই পথে স্থানান্তর করতে পারি, তবে অন্যদের মধ্যে এটি থাকার জন্য রয়েছে।
সুতরাং সংকলক কেন এটি করে? ঠিক আছে, আমরা যদি লুপটি আনরোল করতে পারি তবে আমরা এটি ভেক্টরাইজ করতে সক্ষম হতে পারি। আমরা এমনকি প্রমাণ করতে সক্ষম হতে পারি যে সেখানে কেবল ধ্রুবক যুক্ত হচ্ছে, যার অর্থ আমাদের পুরো লুপটি পাতলা বাতাসে বিলীন হতে পারে। সংক্ষিপ্তসার হিসাবে: নিদর্শনগুলি অনুমানযোগ্য করে (শাখাগুলিকে অনুমানযোগ্য করে) আমরা প্রমাণ করতে পারি যে কিছু শর্ত আমাদের লুপকে ধারণ করে, যার অর্থ আমরা জেআইটি অপ্টিমাইজেশনের সময় যাদু করতে পারি।
যাইহোক, শাখাগুলি সেই দুর্দান্ত অনুমানযোগ্য নিদর্শনগুলিকে ভাঙতে ঝোঁক করে, এটি অপ্টিমাইজারের কিছু তাই অত্যাধিক-অপছন্দ। ভাঙ্গা, চালিয়ে যাও, যাও - তারা সকলেই এই ভবিষ্যদ্বাণীমূলক নিদর্শনগুলি ভাঙ্গার ইচ্ছাকৃত- এবং তাই সত্যই 'সুন্দর' নয়।
আপনার এই মুহুর্তে উপলব্ধি করা উচিত যে একটি সরল foreach
আরও অনুমানযোগ্য তবে goto
বিবৃতিগুলির একটি গোছা যা পুরো জায়গা জুড়ে। অপ্টিমাইজারের দৃষ্টিকোণ থেকে (1) পঠনযোগ্যতা এবং (2) এর পরিপ্রেক্ষিতে এটি উভয়ই ভাল সমাধান।
উল্লেখযোগ্য আরেকটি বিষয় হ'ল এটি কম্পাইলারদের ভেরিয়েবলগুলিতে নিবন্ধগুলি নির্ধারণের জন্য অনুকূলিতকরণের জন্য অত্যন্ত প্রাসঙ্গিক (একটি প্রক্রিয়া যা রেজিস্টার বরাদ্দ বলা হয় )। আপনারা জানেন যে, আপনার সিপিইউতে কেবলমাত্র সীমাবদ্ধ সংখ্যা রয়েছে এবং এগুলি আপনার হার্ডওয়ারে মেমরির সবচেয়ে দ্রুততম টুকরো। অভ্যন্তরের সর্বাধিক লুপের মধ্যে কোডে ব্যবহৃত ভেরিয়েবলগুলি নিবন্ধক নিয়োগের সম্ভাবনা বেশি থাকে, তবে আপনার লুপের বাইরের ভেরিয়েবলগুলি কম গুরুত্বপূর্ণ (কারণ এই কোডটি সম্ভবত কম আঘাত করা হয়েছে)।
সহায়তা, খুব জটিলতা ... আমার কি করা উচিত?
তল লাইনটি হ'ল আপনার নিজের ভাষাতে আপনার নির্মিত ভাষা নির্মাণগুলি সর্বদা ব্যবহার করা উচিত, যা সাধারণত (ছদ্মবেশী) আপনার সংকলকটির জন্য অনুমানযোগ্য নিদর্শন তৈরি করে। (: বিশেষভাবে সম্ভব হলে অদ্ভুত শাখা এড়ানোর চেষ্টা break
, continue
, goto
বা return
কিছুই মাঝখানে)।
এখানে সুসংবাদটি হ'ল এই অনুমানযোগ্য নিদর্শনগুলি উভয়ই পড়া সহজ (মানুষের জন্য) এবং স্পট করা সহজ (সংকলকগুলির জন্য) উভয়ই।
এই নিদর্শনগুলির মধ্যে একটিকে এসইএসই বলা হয়, যা সিঙ্গল এন্ট্রি সিঙ্গেল এক্সিটের জন্য দাঁড়ায়।
এবং এখন আমরা আসল প্রশ্ন পেতে।
কল্পনা করুন যে আপনার কাছে এরকম কিছু রয়েছে:
// a is a variable.
for (int i=0; i<100; ++i)
{
for (int j=0; j<100; ++j)
{
// ...
if (i*j > a)
{
// break everything
}
}
}
এটি অনুমানযোগ্য প্যাটার্ন করার সহজতম উপায় হ'ল if
সম্পূর্ণরূপে মুছে ফেলা :
int i, j;
for (i=0; i<100 && i*j <= a; ++i)
{
for (j=0; j<100 && i*j <= a; ++j)
{
// ...
}
}
অন্যান্য ক্ষেত্রে আপনি পদ্ধতিটি 2 টি পদ্ধতিতেও বিভক্ত করতে পারেন:
// Outer loop in method 1:
for (i=0; i<100 && processInner(i); ++i)
{
}
private bool processInner(int i)
{
int j;
for (j=0; j<100 && i*j <= a; ++j)
{
// ...
}
return i*j<=a;
}
অস্থায়ী পরিবর্তনশীল? ভাল, খারাপ না কদর্য?
আপনি এমনকি লুপের মধ্য থেকে কোনও বুলেটিয়ান ফেরত দেওয়ার সিদ্ধান্ত নিতে পারেন (তবে আমি ব্যক্তিগতভাবে এসইএসই ফর্মটি পছন্দ করি কারণ সংকলকটি এটি দেখতে পাবে এবং আমি মনে করি এটি পড়ার জন্য আরও পরিষ্কার)।
কিছু লোক মনে করেন এটি অস্থায়ী পরিবর্তনশীল ব্যবহার করা পরিষ্কার, এবং এর মতো সমাধানের প্রস্তাব দিন:
bool more = true;
for (int i=0; i<100; ++i)
{
for (int j=0; j<100; ++j)
{
// ...
if (i*j > a) { more = false; break; } // yuck.
// ...
}
if (!more) { break; } // yuck.
// ...
}
// ...
আমি ব্যক্তিগতভাবে এই পদ্ধতির বিরোধী। কোডটি কীভাবে সংকলিত হয় তা আবার দেখুন। এখন এই দুর্দান্ত, অনুমানযোগ্য নিদর্শনগুলির সাথে এটি কী করবে তা ভেবে দেখুন। ছবিটি নাও?
ঠিক আছে, আমাকে এটি বানান। কি ঘটবে তা হ'ল:
- সংকলকটি শাখা হিসাবে সবকিছু লিখবে।
- একটি অপ্টিমাইজেশন পদক্ষেপ হিসাবে, সংযোজকটি
more
নিয়ন্ত্রণের প্রবাহে কেবল ব্যবহৃত হতে পারে এমন অদ্ভুত পরিবর্তনশীলটিকে সরিয়ে দেওয়ার প্রয়াসে ডেটা প্রবাহ বিশ্লেষণ করবে ।
- যদি সফল হয় তবে চলকটি
more
প্রোগ্রাম থেকে মুছে ফেলা হবে এবং কেবল শাখা থাকবে branches এই শাখাগুলি অনুকূলিত করা হবে, সুতরাং আপনি কেবল অভ্যন্তরীণ লুপ থেকে একটি একক শাখা পাবেন।
- যদি ব্যর্থ হয় তবে চলকটি
more
অবশ্যই অভ্যন্তরের সর্বাধিক লুপে ব্যবহৃত হয়, সুতরাং সংকলক যদি এটি অপ্টিমাইজ করে না, তবে এটি কোনও নিবন্ধকে বরাদ্দ করার উচ্চতর সুযোগ রয়েছে (যা মূল্যবান নিবন্ধের স্মৃতি খায়)।
সুতরাং, সংক্ষেপে বলি: আপনার সংকলকটির অপ্টিমাইজারটি more
কেবল নিয়ন্ত্রণ প্রবাহের জন্য ব্যবহৃত হয় তা নির্ধারণ করার জন্য অনেক ঝামেলার জাহান্নামে চলে যাবে , এবং সর্বোত্তম ক্ষেত্রে এটির জন্য বাইরের বাইরের একটি শাখায় অনুবাদ করবে লুপ.
অন্য কথায়, সর্বোত্তম মামলার দৃশ্যপট হ'ল এটি এর সমতুল্য সমাপ্ত হবে:
for (int i=0; i<100; ++i)
{
for (int j=0; j<100; ++j)
{
// ...
if (i*j > a) { goto exitLoop; } // perhaps add a comment
// ...
}
// ...
}
exitLoop:
// ...
এ সম্পর্কে আমার ব্যক্তিগত মতামতটি বেশ সহজ: আমরা যদি এই বিষয়টিকেই পাশাপাশি রেখেছিলাম তবে আসুন আমরা সংকলক এবং পঠনযোগ্যতা উভয়ের জন্যই বিশ্বকে সহজ করে তুলি এবং এখনই এটি লিখি।
TL; ড:
শেষের সারি:
- লুপের জন্য যদি সম্ভব হয় তবে একটি সাধারণ শর্ত ব্যবহার করুন। যথাসম্ভব আপনার হাতে থাকা উচ্চ-স্তরের ভাষা নির্মাণকে আঁকুন।
- যদি সবকিছু ব্যর্থ হয় এবং আপনি উভয়ের সাথে ছেড়ে যান
goto
বা bool more
, পূর্বেরটিকে পছন্দ করুন।