গণনা করার চেয়ে গণনা করা কি দ্রুত?


131

আমাদের কম্পিউটার বিজ্ঞানের শিক্ষক একবার বলেছিলেন যে কোনও কারণে এটি গণনা করার চেয়ে গণনা করা আরও দক্ষ। উদাহরণস্বরূপ, যদি আপনাকে ফর লুপ ব্যবহার করতে হয় এবং লুপ সূচকটি কোথাও ব্যবহার করা হয় না (যেমন স্ক্রিনে এন * এর একটি লাইন প্রিন্ট করা) আমার অর্থ এই কোডটি:

for (i = N; i >= 0; i--)  
  putchar('*');  

বেশী ভালো:

for (i = 0; i < N; i++)  
  putchar('*');  

এটা কি সত্যি? এবং যদি তা হয় তবে কেন কেউ জানেন?


6
কোন কম্পিউটার বিজ্ঞানী? কোন প্রকাশনায়?
bmargulies

26
এটি অনুমেয় যে আপনি পুনরাবৃত্তি প্রতি একটি ন্যানোসেকেন্ড বা couldলি ম্যামথগুলির পরিবারের এক চুলের মতোই সঞ্চয় করতে পারবেন। putcharসময় (বা দিতে নিতে) এর 99.9999% ব্যবহার করে।
মাইক ডুনলাভে

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

61
প্রথম লুপটি স্পষ্টতই ধীরতর, কারণ এটি পুতচরকে ১১ বার কল করে, অন্যদিকে এটি দ্বিতীয় বার কেবল 10 বার কল করে।
পল কুলিনিউইচজ

17
আপনি কি লক্ষ্য করেছেন যে iস্বাক্ষর না থাকলে, প্রথম লুপটি একটি অসীম?
শাহবাজ

উত্তর:


371

এটা কি সত্যি? এবং যদি তা হয় তবে কেন কেউ জানে?

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

সুতরাং, আপনি যদি শূন্যের পরিবর্তে আপনার লুপগুলি তুলনা করতে চান তবে Nকী ঘটতে পারে?

  • আপনি একটি রেজিস্টার সংরক্ষণ করতে পারেন
  • আপনি একটি ছোট বাইনারি এনকোডিংয়ের সাথে তুলনা নির্দেশিকা পেতে পারেন
  • যদি পূর্ববর্তী কোনও নির্দেশিকা একটি পতাকা সেট করতে ঘটে (সম্ভবত কেবলমাত্র x86 ফ্যামিলি মেশিনে), আপনার সম্ভবত একটি সুস্পষ্ট তুলনা নির্দেশের প্রয়োজনও হবে না

কোন ফলাফল সম্ভবত এই পার্থক্য আছে পরিমাপযোগ্য উন্নতি উপর বাস্তব প্রোগ্রাম একটি আধুনিক আউট-অফ-অর্ডার প্রসেসর উপর? অত্যন্ত সম্ভাবনা নেই। আসলে, আপনি মুখ্য হবেন যদি আপনি কোনও মাইক্রোব্যাঙ্কমার্কেও পরিমাপযোগ্য উন্নতি করতে পারেন।

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


3
++ এবং তদ্ব্যতীত, putcharলুপ ওভারহেডের চেয়ে দীর্ঘতরতার অনেকগুলি অর্ডার নেয়।
মাইক ডুনলাভে

41
এটি কঠোরভাবে পৌরাণিক কাহিনী নয়: যদি তিনি কিছু ধরণের উবার-অনুকূলিত রিয়েল-টাইম সিস্টেম করেন তবে এটি কার্যকর হবে। তবে এই ধরণের হ্যাকার সম্ভবত ইতিমধ্যে এই সমস্তটি জানত এবং অবশ্যই অবশ্যই আরকানার সাথে এন্ট্রি-লেভেলের সিএস শিক্ষার্থীদের গুলিয়ে ফেলবে না।
পল নাথান

4
@ জোশুয়া: কীভাবে এই অপ্টিমাইজেশন সনাক্তকরণযোগ্য হবে? যেমন প্রশ্নকর্তা বলেছেন, লুপ সূচকটি লুপটিতে ব্যবহৃত হয় না, তাই পুনরাবৃত্তির সংখ্যা একই থাকে তবে আচরণের কোনও পরিবর্তন নেই। নির্ভুলতার প্রমাণ হিসাবে, পরিবর্তনশীল প্রতিস্থাপন তৈরি করে j=N-iদেখায় যে দুটি লুপ সমান।
স্মারক

7
সংক্ষিপ্তসার জন্য +1। এটি ঘামবেন না কারণ আধুনিক হার্ডওয়্যারটিতে এটি কার্যত কোনও পার্থক্য করে না। 20 বছর আগে এটি কার্যত কোনও পার্থক্য তৈরি করে নি। আপনি যদি মনে করেন যে আপনার যত্ন নিতে হবে, উভয় উপায়ে সময় কাটাতে হবে, কোনও স্পষ্ট পার্থক্য না দেখে এবং পরিষ্কারভাবে এবং সঠিকভাবে কোডটি লেখার দিকে ফিরে যান
ডোনাল ফেলো

3
আমি জানিনা আমার শরীরের পক্ষে উর্ধ্বতন করা উচিত বা সংক্ষিপ্তসারটির জন্য ডাউনভোট করা উচিত।
ডানুবিয়ান নাবিক

29

আপনি যে সংখ্যাগুলি ব্যবহার করছেন তার পরিসীমা সম্পর্কে কম্পাইলার কী পরিমাণ কমাতে পারে তার উপর নির্ভর করে কিছু হার্ডওয়্যারের ক্ষেত্রে যা ঘটতে পারে তা এখানে রয়েছে: i<Nপ্রতিটি সময় বাড়ানো লুপের সাথে আপনাকে লুপের চারপাশে পরীক্ষা করতে হবে। ক্রমহ্রাসমান সংস্করণের জন্য, বহনকারী পতাকা (বিয়োগের একটি পার্শ্ব প্রতিক্রিয়া হিসাবে সেট) স্বয়ংক্রিয়ভাবে আপনাকে জানাতে পারে কিনা i>=0। এটি লুপের চারপাশে প্রতিবারের পরীক্ষা সংরক্ষণ করে।

বাস্তবে, আধুনিক পাইপলাইনযুক্ত প্রসেসরের হার্ডওয়্যারগুলিতে, এই জিনিসগুলি প্রায় অবশ্যই অপ্রাসঙ্গিক কারণ এখানে নির্দেশের থেকে ঘড়ির চক্রের কোনও 1-1 ম্যাপিং নেই। (যদিও আপনি যদি কোনও মাইক্রোক্রন্ট্রোলারের কাছ থেকে নির্দিষ্ট সময়সীমার ভিডিও সংকেত তৈরির মতো কাজ করে থাকেন তবে আমি এটি কল্পনা করতে পারি But তবে তারপরে আপনি যাই হোক না কেন সমাবেশ ভাষায় লিখবেন d)


2
শূন্য পতাকা কি বহনকারী পতাকা নয়?
বব

2
@ Bob এক্ষেত্রে আপনি শূন্যে পৌঁছে, ফলাফল মুদ্রণ করতে, আরও হ্রাস পেতে চাইতে পারেন এবং তারপরে আপনি ক্যারি (বা orrowণ) নিয়ে শূন্যের নীচে চলে গেছেন। তবে কিছুটা আলাদাভাবে লিখিতভাবে একটি হ্রাসমান লুপ এর পরিবর্তে শূন্য পতাকা ব্যবহার করতে পারে।
সিগফেপ

1
কেবল পুরোপুরি পেডেন্টিক হতে, সমস্ত আধুনিক হার্ডওয়্যার পাইপলাইন করা হয় না। এম্বেড থাকা প্রসেসরের এই ধরণের মাইক্রোপটিমাইজেশনের সাথে আরও অনেক প্রাসঙ্গিকতা থাকবে।
পল নাথান

@Paul আমি Atmel AVRs আমি কিছু অভিজ্ঞতা ভুলে যায়নি মাইক্রোকন্ট্রোলারের উল্লেখ নেই ...
sigfpe

27

ইন্টেল x86 নির্দেশিকায়, শূন্য থেকে গণনা করার জন্য একটি লুপ তৈরি করা সাধারণত একটি লুপের চেয়ে কম নির্দেশাবলীর সাহায্যে করা যায় যা একটি শূন্য-বহির্গমন শর্তকে গণ্য করে। বিশেষত, ইসিএক্স রেজিস্টারটি traditionতিহ্যগতভাবে x86 asm এ লুপের কাউন্টার হিসাবে ব্যবহৃত হয় এবং ইন্টেল নির্দেশিকা সেটটিতে একটি বিশেষ জেসিএক্সজেড জাম্প নির্দেশ রয়েছে যা পরীক্ষার ফলাফলের ভিত্তিতে শূন্য এবং জাম্পের জন্য ইসিএক্স রেজিস্টার পরীক্ষা করে।

যাইহোক, আপনার লুপটি ইতিমধ্যে ক্লকচক্র গণনার ক্ষেত্রে খুব সংবেদনশীল না হলে পারফরম্যান্সের পার্থক্য নগণ্য হবে। শূন্যের নিচে গণনা করা গণনার তুলনায় লুপের প্রতিটি পুনরাবৃত্তির বাইরে 4 বা 5 ঘড়ির চক্র শেভ করতে পারে, সুতরাং এটি কোনও দরকারী প্রযুক্তির চেয়ে সত্যই অভিনবত্ব more

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


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

1
@ মার্ক: ডেল্ফি সংকলকটিও ১৯৯ 1996 সালে শুরু হয়েছে
dthorpe

4
@ মার্করান্সম আসলে, সংকলকটি লুপটিতে কীভাবে ব্যবহৃত হয় তার উপর নির্ভর করে লুপ সূচক ভেরিয়েবলটি ব্যবহার করা হলেও কাউন্ট ডাউন ব্যবহার করে লুপটি কার্যকর করতে সক্ষম হতে পারে। যদি লুপ সূচক ভেরিয়েবলটি কেবল স্থিতিশীল অ্যারেগুলিতে সূচিত করতে ব্যবহৃত হয় (সংকলনের সময় জ্ঞাত আকারের অ্যারে), অ্যারে সূচি পিটিআর + অ্যারে আকার হিসাবে করা যেতে পারে - লুপ সূচক ভার, যা এখনও x86 এ একক নির্দেশ হতে পারে। এসেম্বলারের ডিবাগিং করা এবং লুপটি গণনা করা কিন্তু অ্যারে সূচকগুলি উপরে উঠতে দেখতে বেশ বুনো!
dthorpe

1
প্রকৃতপক্ষে আজ আপনার সংকলক সম্ভবত লুপ এবং জেকক্স নির্দেশাবলী ব্যবহার করবে না কারণ তারা ডিক / জেএনজেড জুটির চেয়ে ধীর।
ফুজ

1
@FUZxxl আপনার লুপটি অদ্ভুত উপায়ে না লেখার আরও বেশি কারণ রয়েছে। মানব পঠনযোগ্য স্পষ্ট কোড লিখুন এবং সংকলকটিকে এর কাজটি করতে দিন।
dthorpe

23

হ্যাঁ..!!

এন থেকে নিচে 0 পর্যন্ত গণনা করা কিছুটা দ্রুত গতিতে 0 থেকে N পর্যন্ত গণনা করা হয় যে হার্ডওয়ারটি কীভাবে তুলনা পরিচালনা করবে এই অর্থে ..

প্রতিটি লুপে তুলনা নোট করুন

i>=0
i<N

বেশিরভাগ প্রসেসরের শূন্য নির্দেশাবলীর সাথে তুলনা থাকে..যে প্রথমটি মেশিন কোডে অনুবাদ করা হবে:

  1. লোড i
  2. তুলনা করুন এবং শূন্যের চেয়ে কম হলে লাফ দিন

তবে দ্বিতীয়টির জন্য প্রতিটি বার এন ফর্ম মেমরি লোড করা দরকার

  1. লোড i
  2. লোড এন
  3. সাব আই এবং এন
  4. তুলনা করুন এবং শূন্যের চেয়ে কম হলে লাফ দিন

সুতরাং এটি গণনা বা উপরের কারণে নয় .. তবে কীভাবে আপনার কোডটি মেশিন কোডে অনুবাদ করা হবে ..

সুতরাং 10 থেকে 100 পর্যন্ত গণনা 100 থেকে 10 গণনা ফর্মের সমান
তবে i = 100 থেকে 0 থেকে গণনা i = 0 থেকে 100 এর চেয়ে দ্রুত - বেশিরভাগ ক্ষেত্রে
এবং i = N থেকে 0 গণনা i = এর চেয়ে দ্রুত হয় 0 থেকে এন

  • নোট করুন যে আজকাল সংকলকগণ আপনার জন্য এই অপ্টিমাইজেশনটি করতে পারে (যদি এটি যথেষ্ট স্মার্ট হয়)
  • এটিও নোট করুন যে পাইপলাইন বেল্ডির অসাধারণ- মত প্রভাব তৈরি করতে পারে (ভাল কি হবে তা নিশ্চিত হতে পারে না)
  • শেষ অবধি: দয়া করে নোট করুন যে আপনি উপস্থাপন করেছেন দুটি লুপ সমতুল্য নয় .. প্রথম প্রিন্ট করে আরও একটি * .... ....

সম্পর্কিত: এন ++ এন = এন + 1 এর চেয়ে দ্রুত কার্যকর করা হয় কেন?


6
সুতরাং আপনি যা বলছেন তা গণনা করা দ্রুত নয়, অন্য কোনও মানের তুলনায় শূন্যের সাথে তুলনা করা আরও দ্রুত। মানে 10 থেকে 100 পর্যন্ত গণনা এবং 100 থেকে 10 পর্যন্ত গণনা একই হবে?
বব

8
হ্যাঁ .. এটি "গণনা বা উপরে উঠার" বিষয় নয় ... এটি "কিসের সাথে তুলনা করার" বিষয়
বেতামু

3
যদিও এটি একত্রিতকারী স্তর সত্য। দুটি জিনিস বাস্তবে অসত্যকে একত্রিত করার জন্য - লম্বা পাইপ এবং অনুমানমূলক নির্দেশাবলী ব্যবহার করে আধুনিক হার্ডওয়্যার একটি অতিরিক্ত চক্র ব্যয় না করে "সাব আই এবং এন" এ ঝাঁকুনি দেয় - এবং - এমনকি ক্রুডেস্ট সংকলক "সাব আই এবং এন "অস্তিত্বের বাইরে।
জেমস অ্যান্ডারসন

2
@nico একটি প্রাচীন সিস্টেম হতে হবে না। এটি কেবলমাত্র একটি নির্দেশিকা সেট হতে হবে যেখানে শূন্য অপারেশনের সাথে তুলনা করা থাকে যা কোনওভাবেই নিবন্ধকের মানটির সাথে তুলনা করার তুলনায় তুলনায় দ্রুত / উন্নত। x86 এর এটি jcxz এ রয়েছে। x64 এখনও আছে। প্রাচীন নয়। এছাড়াও, আরআইএসসি আর্কিটেকচারগুলি প্রায়শই বিশেষ ক্ষেত্রে শূন্য থাকে। ডিসি এক্সএক্সপি আলফা চিপ (এমআইপিএস পরিবারে), উদাহরণস্বরূপ, একটি "শূন্য রেজিস্টার" ছিল - শূন্য হিসাবে পড়া, লেখার কিছুই হয় না। শূন্য মানের একটি সাধারণ নিবন্ধের পরিবর্তে শূন্য রেজিস্ট্রারের সাথে তুলনা করা আন্তঃশিক্ষা নির্ভরতা হ্রাস করে এবং আদেশ কার্যকর করতে সহায়তা করে।
23th12

5
@ বিটামু: আমি প্রায়শই ভাবছি যে কেন আরও ভাল / আরও সঠিক উত্তরগুলি (যা আপনার) বেশি ভোটের দ্বারা বেশি প্রশংসা করা হয় না এবং এই সিদ্ধান্তে পৌঁছে যে স্ট্যাকওভারফ্লো ভোটের ক্ষেত্রেও প্রায়শই কোনও ব্যক্তির সুনাম (পয়েন্টে) দ্বারা প্রভাবিত হয় যে উত্তর দেয় ( যা খুব খারাপ) এবং উত্তর সঠিকতার দ্বারা নয়
আর্টুর

12

সি থেকে স্যুডো-অ্যাসেম্বলি:

for (i = 0; i < 10; i++) {
    foo(i);
}

পরিণত হয়

    clear i
top_of_loop:
    call foo
    increment i
    compare 10, i
    jump_less top_of_loop

যখন:

for (i = 10; i >= 0; i--) {
    foo(i);
}

পরিণত হয়

    load i, 10
top_of_loop:
    call foo
    decrement i
    jump_not_neg top_of_loop

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

x = x - 0

শব্দার্থগতভাবে একই

compare x, 0

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

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


এটি কি সম্ভব যে কেবলমাত্র 1 টির পরিবর্তে 2 টি নির্দেশাবলীর ভিন্নতা রয়েছে?
পেসারিয়ার

এছাড়াও, কেন এটি সম্পর্কে নিশ্চিত হওয়া শক্ত? iলুপের মধ্যে যতক্ষণ না ভার ব্যবহার করা হয় ততক্ষণ আপনি অবশ্যই এটি উল্টাতে পারবেন তাই না?
পেসারিয়ার

6

এই ক্ষেত্রে দ্রুত গণনা করুন:

for (i = someObject.getAllObjects.size(); i >= 0; i--) {…}

কারণ someObject.getAllObjects.size()শুরুতে একবার মৃত্যুদন্ড কার্যকর করা হয়।


অবশ্যই, অনুরূপ আচরণ size()লুপটি আউট কল করার মাধ্যমে অর্জন করা যেতে পারে , যেমন পিটার উল্লেখ করেছেন:

size = someObject.getAllObjects.size();
for (i = 0; i < size; i++) {…}

5
এটি "অবশ্যই দ্রুত" নয়। অনেক ক্ষেত্রে size আকার () কলটি গণনা করার সময় লুপ থেকে উত্তোলন করা যেতে পারে, সুতরাং এটি কেবল একবার একবার কল হতে পারে। স্পষ্টতই এটি ভাষা এবং সংকলক নির্ভর (এবং কোড নির্ভরশীল; উদাহরণস্বরূপ, সি ++ এ আকার () ভার্চুয়াল হলে এটি উত্তোলন করা হবে না) তবে এটি উভয় দিক থেকেই নির্দিষ্ট নয়।
পিটার

3
@ পিটার: কেবলমাত্র যদি সংকলক নির্দিষ্ট আকারের জন্য জানতে পারে তবে লুপটি জুড়ে আদর্শ () আদর্শবান। এটি সম্ভবত প্রায় সবসময় না যদি, যদি না লুপ খুব সহজ।
লরেন্স ডল

@ লরেন্সডল, আপনার যদি গতিশীল কোড সংকলন ব্যবহার না করে সংকলকটি অবশ্যই তা জানতে পারবে exec
পেসারিয়ার

4

আপ চেয়ে গণনা করা কি দ্রুত?

হতে পারে. তবে সময়ের চেয়ে 99% এরও বেশি সময় এটি বিবেচনা করে না, সুতরাং লুপটি বন্ধ করার জন্য আপনার সর্বাধিক 'বুদ্ধিমান' পরীক্ষা করা উচিত, এবং বুদ্ধিমানের দ্বারা, আমি বোঝাতে চাইছি যে এটি একটি পাঠক দ্বারা চিন্তা করার জন্য কমপক্ষে চিন্তাভাবনা করে লুপটি কী করছে (এটি কী বন্ধ করে দেয় তা সহ)। কোডটি কী করছে তার মানসিক (বা ডকুমেন্টেড) মডেলের সাথে আপনার কোডটি মিলিয়ে নিন।

লুপটি যদি কোনও অ্যারের মাধ্যমে এটি কাজ করে (বা তালিকা, বা যাই হোক না কেন), একটি ইনক্রিমেন্টিং কাউন্টারটি প্রায়শই আরও ভালভাবে মিলবে যে কীভাবে পাঠক লুপটি কী করছে তা ভাবতে পারে - আপনার লুপটিকে এভাবে কোড করুন।

তবে আপনি যদি কোনও ধারকটির মধ্যে কাজ করছেন Nযা আইটেম রয়েছে এবং আপনি যেভাবে আইটেমগুলি সরিয়ে ফেলছেন, এটি কাউন্টারটি ডাউন করার জন্য আরও জ্ঞানীয় ধারণা তৈরি করতে পারে।

উত্তরে 'হয়তো' সম্পর্কে আরও কিছু বিশদ:

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

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

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


3

কিছু পুরানো সিপিইউতে DJNZ== "হ্রাস এবং শূন্য না হলে লাফিয়ে দেওয়ার" মতো নির্দেশাবলী রয়েছে । এটি কার্যকর লুপগুলির জন্য মঞ্জুরিপ্রাপ্ত যেখানে আপনি একটি প্রাথমিক গণনা মান একটি রেজিস্টারে লোড করেছেন এবং তারপরে আপনি কার্যকরভাবে একটি নির্দেশ দিয়ে হ্রাসমান লুপ পরিচালনা করতে পারবেন। আমরা এখানে 1980 এর দশকের আইএসএ নিয়ে কথা বলছি - আপনার শিক্ষক যদি মনে করেন যে এই "থাম্বের নিয়ম" এখনও আধুনিক সিপিইউগুলির সাথে প্রযোজ্য with


3

বব

আপনি মাইক্রোপটিমাইজেশন না করা পর্যন্ত নয়, আপনার সিপিইউ হস্তান্তর করার জন্য আপনার কাছে ম্যানুয়ালটি থাকবে। আরও, যদি আপনি এই ধরণের জিনিসটি করছিলেন তবে আপনার সম্ভবত সম্ভবত এই প্রশ্নটি জিজ্ঞাসা করার প্রয়োজন হবে না। :-) তবে, আপনার শিক্ষক স্পষ্টতই সেই ধারণার সাথে সাবস্ক্রাইব করবেন না ....

আপনার লুপ উদাহরণে 4 টি বিষয় বিবেচনা করতে হবে:

for (i=N; 
 i>=0;             //thing 1
 i--)             //thing 2
{
  putchar('*');   //thing 3
}
  • তুলনা

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

  • সমন্বয়

কিছু ক্ষেত্রে এটি আপ বা ডাউন সামঞ্জস্য করা আরও দ্রুত। সাধারণত একটি ভাল সংকলক এটি খুঁজে বের করতে পারে এবং যদি লুপটি আবার করতে পারে তবে তা আবার করবে। সমস্ত সংকলক যদিও ভাল হয় না।

  • লুপ বডি

আপনি পুতচর দিয়ে একটি সিস্কেল অ্যাক্সেস করছেন। যে ব্যাপকভাবে ধীর। এছাড়াও, আপনি পর্দায় উপস্থাপন করছেন (অপ্রত্যক্ষভাবে)। এটা আরও ধীর। 1000: 1 অনুপাত বা আরও বেশি ভাবেন। এই পরিস্থিতিতে লুপের দেহ পুরোপুরি এবং সম্পূর্ণভাবে লুপ সামঞ্জস্যকরণ / তুলনা ব্যয়কে ছাড়িয়ে যায়।

  • ক্যাশে

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


3

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

স্কেপটিকল? স্মৃতিচারণে / ডাউন হয়ে যাওয়া সময় লুপগুলিতে কেবল একটি প্রোগ্রাম লিখুন। আমি যে আউটপুট পেয়েছি তা এখানে:

Average Up Memory   = 4839 mus
Average Down Memory = 5552 mus

Average Up Memory   = 18638 mus
Average Down Memory = 19053 mus

(যেখানে "মিউস" মাইক্রোসেকেন্ডগুলি বোঝায়) এই প্রোগ্রামটি চালানো থেকে:

#include <chrono>
#include <iostream>
#include <random>
#include <vector>

//Sum all numbers going up memory.
template<class Iterator, class T>
inline void sum_abs_up(Iterator first, Iterator one_past_last, T &total) {
  T sum = 0;
  auto it = first;
  do {
    sum += *it;
    it++;
  } while (it != one_past_last);
  total += sum;
}

//Sum all numbers going down memory.
template<class Iterator, class T>
inline void sum_abs_down(Iterator first, Iterator one_past_last, T &total) {
  T sum = 0;
  auto it = one_past_last;
  do {
    it--;
    sum += *it;
  } while (it != first);
  total += sum;
}

//Time how long it takes to make num_repititions identical calls to sum_abs_down().
//We will divide this time by num_repitions to get the average time.
template<class T>
std::chrono::nanoseconds TimeDown(std::vector<T> &vec, const std::vector<T> &vec_original,
                                  std::size_t num_repititions, T &running_sum) {
  std::chrono::nanoseconds total{0};
  for (std::size_t i = 0; i < num_repititions; i++) {
    auto start_time = std::chrono::high_resolution_clock::now();
    sum_abs_down(vec.begin(), vec.end(), running_sum);
    total += std::chrono::high_resolution_clock::now() - start_time;
    vec = vec_original;
  }
  return total;
}

template<class T>
std::chrono::nanoseconds TimeUp(std::vector<T> &vec, const std::vector<T> &vec_original,
                                std::size_t num_repititions, T &running_sum) {
  std::chrono::nanoseconds total{0};
  for (std::size_t i = 0; i < num_repititions; i++) {
    auto start_time = std::chrono::high_resolution_clock::now();
    sum_abs_up(vec.begin(), vec.end(), running_sum);
    total += std::chrono::high_resolution_clock::now() - start_time;
    vec = vec_original;
  }
  return total;
}

template<class Iterator, typename T>
void FillWithRandomNumbers(Iterator start, Iterator one_past_end, T a, T b) {
  std::random_device rnd_device;
  std::mt19937 generator(rnd_device());
  std::uniform_int_distribution<T> dist(a, b);
  for (auto it = start; it != one_past_end; it++)
    *it = dist(generator);
  return ;
}

template<class Iterator>
void FillWithRandomNumbers(Iterator start, Iterator one_past_end, double a, double b) {
  std::random_device rnd_device;
  std::mt19937_64 generator(rnd_device());
  std::uniform_real_distribution<double> dist(a, b);
  for (auto it = start; it != one_past_end; it++)
    *it = dist(generator);
  return ;
}

template<class ValueType>
void TimeFunctions(std::size_t num_repititions, std::size_t vec_size = (1u << 24)) {
  auto lower = std::numeric_limits<ValueType>::min();
  auto upper = std::numeric_limits<ValueType>::max();
  std::vector<ValueType> vec(vec_size);

  FillWithRandomNumbers(vec.begin(), vec.end(), lower, upper);
  const auto vec_original = vec;
  ValueType sum_up = 0, sum_down = 0;

  auto time_up   = TimeUp(vec, vec_original, num_repititions, sum_up).count();
  auto time_down = TimeDown(vec, vec_original, num_repititions, sum_down).count();
  std::cout << "Average Up Memory   = " << time_up/(num_repititions * 1000) << " mus\n";
  std::cout << "Average Down Memory = " << time_down/(num_repititions * 1000) << " mus"
            << std::endl;
  return ;
}

int main() {
  std::size_t num_repititions = 1 << 10;
  TimeFunctions<int>(num_repititions);
  std::cout << '\n';
  TimeFunctions<double>(num_repititions);
  return 0;
}

উভয় sum_abs_upএবং sum_abs_downএকই জিনিস (সংখ্যার ভেক্টর যোগফল) এবং একই পদ্ধতিতে একই পার্থক্য রয়েছে যা sum_abs_upস্মৃতিতে sum_abs_downচলে যাওয়ার সময় স্মৃতিতে যায়। আমি এমনকি vecরেফারেন্স দিয়েও পাস করি যাতে উভয় ফাংশন একই মেমরির অবস্থানগুলিতে অ্যাক্সেস করে। তবুও, sum_abs_upধারাবাহিকভাবে তুলনায় দ্রুত sum_abs_down। এটি নিজে চালিয়ে যান (আমি এটি g ++ -O3 দিয়ে সংকলন করেছি)।

আমি সময় নির্ধারণ করছি যে লুপটি কত টাইট হয় তা লক্ষ করা গুরুত্বপূর্ণ। যদি কোনও লুপের দেহ বড় হয় তবে লুপের দেহটি কার্যকর করতে যেহেতু এটির পুনরায় যন্ত্রটি মেমরির উপরের বা নিচে চলে যায় তাতে কিছু আসে যায় না, সম্ভবত এটি পুরোপুরি আধিপত্য বিস্তার করবে। এছাড়াও, এটি উল্লেখ করা জরুরী যে কিছু বিরল লুপগুলির সাথে মেমরি ডাউন করা কখনও কখনও এটির চেয়ে দ্রুত হয়। তবে এ জাতীয় লুপগুলির ক্ষেত্রেও কখনও এমনটি ঘটেনি যে স্মৃতিশক্তি উপরে যাওয়া সর্বদা নীচে যাওয়ার চেয়ে ধীর ছিল (ছোট আকারের লুপগুলি স্মৃতিতে চলে যায়, যার বিপরীতে প্রায়শই সত্য হয়; বাস্তবে, আমি কয়েক হাতের মুদ্রার জন্য) ' সময়সীমার পরে, মেমরির উপরে গিয়ে পারফরম্যান্সের বৃদ্ধি 40 +% ছিল)।

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

এফওয়াইআই vec_originalরয়েছে পরীক্ষার জন্য, এটি পরিবর্তন করা সহজ করার জন্য sum_abs_upএবং sum_abs_downএমন একটি vecপরিবর্তন যাতে ভবিষ্যতের সময়গুলিকে প্রভাবিত না করে এই পরিবর্তনগুলিকে পরিবর্তন করে তোলে । আমি অত্যন্ত সঙ্গে প্রায় বাজানো সুপারিশ sum_abs_upএবং sum_abs_downএবং ফলাফল সময়জ্ঞান।


2

দিক নির্বিশেষে সর্বদা উপসর্গ ফর্মটি ব্যবহার করুন (i++ এর পরিবর্তে ++ i)!

for (i=N; i>=0; --i)  

অথবা

for (i=0; i<N; ++i) 

ব্যাখ্যা: http://www.eskimo.com/~scs/cclass/notes/sx7b.html

এছাড়াও আপনি লিখতে পারেন

for (i=N; i; --i)  

তবে আমি আশা করব যে আধুনিক সংকলকগণ ঠিক এই অপ্টিমাইজেশানগুলি করতে সক্ষম হবেন।


লোকেরা এর আগে কখনও অভিযোগ করতে দেখেনি। তবে লিঙ্কটি পড়ার পরে এটি প্রকৃত অর্থে আসে :) আপনাকে ধন্যবাদ।
টমি জ্যাকোবসেন

3
ওম, কেন তিনি সর্বদা উপসর্গ ফর্মটি ব্যবহার করবেন? যদি কোনও অ্যাসাইনমেন্ট চলছে না, সেগুলি অভিন্ন এবং আপনি যে নিবন্ধটি লিঙ্ক করেছেন এমনকি এটি বলেছেন যে পোস্টফিক্স ফর্মটি আরও সাধারণ।
বব ডেভিল

3
কেন একজনকে সর্বদা উপসর্গ ফর্মটি ব্যবহার করা উচিত? এই উদাহরণে, এটি শব্দার্থগতভাবে অভিন্ন।
বেন জোটো

2
পোস্টফিক্স ফর্মটি সম্ভাব্যভাবে অবজেক্টের একটি অপ্রয়োজনীয় অনুলিপি তৈরি করতে পারে, যদিও মানটি কখনই ব্যবহার করা হয় না, সংকলক সম্ভবত এটি যাইহোক প্রিফিক্স ফর্মটিতে অনুকূল করে তুলবে।
নিক লুইস

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

2

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

এই উইকিপিডিয়া পৃষ্ঠা অনুসারে: দ্বিতীয় লাফ দিন , "... মূলত জোয়ারের ঘর্ষণের কারণে প্রতি শতাব্দীতে সৌর দিনটি 1.7 এমএস দীর্ঘ হয়" " তবে আপনি যদি নিজের জন্মদিন অবধি দিন গণনা করেন তবে সময় মতো এই ক্ষুদ্র পার্থক্যের বিষয়ে আপনি কি সত্যিই যত্নশীল?

এটি আরও গুরুত্বপূর্ণ যে উত্স কোডটি পড়া এবং বুঝতে সহজ। এই দুটি লুপগুলি কেন পঠনযোগ্যতা গুরুত্বপূর্ণ তা একটি ভাল উদাহরণ - তারা একই সংখ্যক বার লুপ করে না।

আমি বাজি ধরব যে বেশিরভাগ প্রোগ্রামাররা (i = 0; i <N; i ++) পড়ে এবং ততক্ষণে বুঝতে পারে যে এটি এন-এ লুপ করে। আমার পক্ষে (i = 1; i <= N; i ++) এর একটি লুপটি কিছুটা কম পরিষ্কার এবং (i = N; i> 0; i--) এর সাথে আমাকে এক মুহুর্তের জন্য এটি ভাবতে হবে । কোডের উদ্দেশ্যটি কোনও চিন্তাভাবনা ছাড়াই সরাসরি মস্তিষ্কে চলে যায় তবে এটি সেরা।


উভয় কনস্ট্রাক্টস বোঝার জন্য ঠিক তত সহজ। এমন কিছু লোক আছে যে দাবী করে যে আপনার কাছে 3 বা 4 টি পুনরাবৃত্তি থাকলে লুপ তৈরির চেয়ে নির্দেশটি অনুলিপি করা ভাল কারণ এটি তাদের পক্ষে বোঝা সহজ।
ডানুবিয়ান নাবিক

2

আশ্চর্যজনকভাবে, এটি প্রদর্শিত হয় যে একটি পার্থক্য আছে। কমপক্ষে, পিএইচপি-তে নিম্নলিখিত মানদণ্ড বিবেচনা করুন:

<?php

print "<br>".PHP_VERSION;
$iter = 100000000;
$i=$t1=$t2=0;

$t1 = microtime(true);
for($i=0;$i<$iter;$i++){}
$t2 = microtime(true);
print '<br>$i++ : '.($t2-$t1);

$t1 = microtime(true);
for($i=$iter;$i>0;$i--){}
$t2 = microtime(true);
print '<br>$i-- : '.($t2-$t1);

$t1 = microtime(true);
for($i=0;$i<$iter;++$i){}
$t2 = microtime(true);
print '<br>++$i : '.($t2-$t1);

$t1 = microtime(true);
for($i=$iter;$i>0;--$i){}
$t2 = microtime(true);
print '<br>--$i : '.($t2-$t1);

ফলাফল আকর্ষণীয়:

PHP 5.2.13
$i++ : 8.8842368125916
$i-- : 8.1797409057617
++$i : 8.0271911621094
--$i : 7.1027431488037


PHP 5.3.1
$i++ : 8.9625310897827
$i-- : 8.5790238380432
++$i : 5.9647901058197
--$i : 5.4021768569946

কারও কারও জানা থাকলে জেনে ভালো লাগবে :)

সম্পাদনা : আপনি 0 থেকে গণনা শুরু না করে ফলাফলগুলি একই, তবে অন্যান্য স্বেচ্ছাসেবী মান। সুতরাং সম্ভবত শূন্যের সাথে তুলনা হয় যা একটি পার্থক্য করে?


এটি ধীর হওয়ার কারণটি হল প্রিফিক্স অপারেটরের কোনও অস্থায়ী সঞ্চয় করার দরকার নেই। Consider foo = $ i ++ বিবেচনা করুন; তিনটি জিনিস ঘটে: $ আমি একটি অস্থায়ীতে সংরক্ষণ করা হয়, $ i বাড়ানো হয় এবং তারপরে oo foo সেই অস্থায়ী মান নির্ধারিত হয়। $ I ++ এর ক্ষেত্রে; একটি স্মার্ট সংকলক বুঝতে পারে যে অস্থায়ী অপ্রয়োজনীয়। পিএইচপি ঠিক না। সি ++ এবং জাভা সংকলকগুলি এই সাধারণ অপ্টিমাইজেশানটি তৈরি করতে যথেষ্ট স্মার্ট।
সুস্পষ্ট সংকলক

এবং কেন $ i--+ $ i ++ এর চেয়ে দ্রুত?
ts।

আপনার বেঞ্চমার্কের কতগুলি পুনরাবৃত্তি আপনি চালিয়েছেন? আপনি কি আউটডাইডারগুলি ক্লিপ করেছেন এবং প্রতিটি ফলাফলের জন্য গড়ে নিয়েছেন? আপনার কম্পিউটারটি মানদণ্ডের সময় অন্য কিছু করছিল? যে ~ 0.5 পার্থক্যটি কেবলমাত্র অন্যান্য সিপিইউ ক্রিয়াকলাপ, বা পাইপলাইন ব্যবহারের ফলস্বরূপ বা ... বা ... ভাল, আপনি ধারণাটি পেতে পারেন।
আট-বিট গুরু

হ্যাঁ, আমি এখানে গড় দিচ্ছি। বেঞ্চমার্ক বিভিন্ন মেশিনে চালিত হয়েছিল, এবং পার্থক্যটি দুর্ঘটনাক্রমে।
ts।

@ কনস্পিকিউস কম্পাইলার => আপনি জানেন বা আপনি মনে করেন?
ts।

2

এটা তোলে করতে দ্রুততর হবে।

এনআইওএস ২ য় প্রসেসরে আমি বর্তমানে লুপের জন্য প্রচলিত with

for(i=0;i<100;i++)

সমাবেশ উত্পাদন:

ldw r2,-3340(fp) %load i to r2
addi r2,r2,1     %increase i by 1
stw r2,-3340(fp) %save value of i
ldw r2,-3340(fp) %load value again (???)
cmplti r2,r2,100 %compare if less than equal 100
bne r2,zero,0xa018 %jump

আমরা যদি গণনা করি

for(i=100;i--;)

আমরা একটি অ্যাসেমব্লি পাই যার জন্য 2 টি নির্দেশাবলী কম দরকার।

ldw r2,-3340(fp)
addi r3,r2,-1
stw r3,-3340(fp)
bne r2,zero,0xa01c

যদি আমাদের নেস্ট লুপ থাকে, যেখানে অভ্যন্তরীণ লুপটি অনেক কার্যকর হয় তবে আমাদের একটি পরিমাপযোগ্য পার্থক্য থাকতে পারে:

int i,j,a=0;
for(i=100;i--;){
    for(j=10000;j--;){
        a = j+1;
    }
}

যদি ভিতরের লুপটি উপরের মতো লেখা থাকে তবে কার্যকর করার সময়টি হয়: 0.121999999999999997344 সেকেন্ড। যদি অভ্যন্তরীণ লুপটি traditionalতিহ্যগতভাবে লেখা হয় তবে কার্যকর করার সময়টি হয়: 0.1719999999999999862323 সেকেন্ড। সুতরাং লুপ গণনা প্রায় 30% দ্রুত।

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

addi r2,r2,-1
bne r2,zero,0xa01c

এই বিশেষ উদাহরণে সংকলক এমনকি নোটও করে, সেই পরিবর্তনশীল a সর্বদা লুপের প্রয়োগের পরে 1 হবে এবং লুপগুলি পুরোপুরি এড়িয়ে যায়।

তবে আমি অনুভব করেছি যে কখনও কখনও লুপের বডি যথেষ্ট জটিল হয়ে গেলে, সংকলকটি এই অপটিমাইজেশনটি করতে সক্ষম হয় না, তাই সর্বদা দ্রুত লুপ সম্পাদন করার নিরাপদ উপায়টি হ'ল:

register int i;
for(i=10000;i--;)
{ ... }

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


2

আপনার শিক্ষক যা বলেছেন তা হ'ল কিছু স্পষ্টতা ছাড়াই কিছু তিরস্কার statement এটি হ'ল না যে হ্রাস হ্রাস বৃদ্ধির চেয়ে দ্রুত তবে আপনি ইনক্রিমেন্টের তুলনায় হ্রাস সহ অনেক দ্রুত লুপ তৈরি করতে পারেন।

এটি সম্পর্কে দীর্ঘায়িত না করে, লুপ কাউন্টার ইত্যাদি ব্যবহারের প্রয়োজন ছাড়াই - নীচের বিষয়গুলি কেবল গতি এবং লুপ গণনা (শূন্য নয়)।

এখানে বেশিরভাগ লোকেরা 10 টি পুনরাবৃত্তির সাহায্যে লুপ প্রয়োগ করে:

int i;
for (i = 0; i < 10; i++)
{
    //something here
}

99% ক্ষেত্রে এটি সবার প্রয়োজন হতে পারে তবে পিএইচপি, পাইথন, জাভাস্ক্রিপ্টের পাশাপাশি সময় সমালোচনামূলক সফটওয়্যার (সাধারণত এমবেডড, ওএস, গেমস ইত্যাদি) এর পুরো বিশ্ব রয়েছে যেখানে সিপিইউ টিক্স সত্যই গুরুত্বপূর্ণ তাই এর সমাবেশ কোডটি সংক্ষেপে দেখুন:

int i;
for (i = 0; i < 10; i++)
{
    //something here
}

সংকলনের পরে (অপ্টিমাইজেশন ছাড়াই) সংকলিত সংস্করণটি এর মতো দেখতে পাওয়া যাবে (ভিএস ২০১৫):

-------- C7 45 B0 00 00 00 00  mov         dword ptr [i],0  
-------- EB 09                 jmp         labelB 
labelA   8B 45 B0              mov         eax,dword ptr [i]  
-------- 83 C0 01              add         eax,1  
-------- 89 45 B0              mov         dword ptr [i],eax  
labelB   83 7D B0 0A           cmp         dword ptr [i],0Ah  
-------- 7D 02                 jge         out1 
-------- EB EF                 jmp         labelA  
out1:

পুরো লুপটি 8 নির্দেশ (26 বাইট)। এতে - 2 টি শাখা সহ 6 টি নির্দেশনা (17 বাইট) রয়েছে। হ্যাঁ হ্যাঁ আমি জানি এটি আরও ভাল করা যায় (এটির একটি উদাহরণ)।

এখন এই ঘন ঘন নির্মাণটি বিবেচনা করুন যা আপনি প্রায়শই এমবেডড বিকাশকারী দ্বারা লিখিত দেখতে পাবেন:

i = 10;
do
{
    //something here
} while (--i);

এটি 10 ​​বারও পুনরাবৃত্তি করে (হ্যাঁ আমি জানি লুপের সাথে দেখানো তুলনায় আমি মানটি আলাদা তবে আমরা এখানে পুনরাবৃত্তির গণনা সম্পর্কে যত্নশীল)। এটি এতে সংকলিত হতে পারে:

00074EBC C7 45 B0 01 00 00 00 mov         dword ptr [i],1  
00074EC3 8B 45 B0             mov         eax,dword ptr [i]  
00074EC6 83 E8 01             sub         eax,1  
00074EC9 89 45 B0             mov         dword ptr [i],eax  
00074ECC 75 F5                jne         main+0C3h (074EC3h)  

5 নির্দেশাবলী (18 বাইট) এবং কেবল একটি শাখা। লুপটিতে 11 টি নির্দেশনা রয়েছে (11 বাইট)

সেরা জিনিসটি হ'ল কিছু সিপিইউতে (x86 / x64 সামঞ্জস্যপূর্ণ অন্তর্ভুক্ত) নির্দেশনা রয়েছে যা কোনও নিবন্ধক হ্রাস করতে পারে, পরে শূন্যের সাথে ফলাফলের তুলনা করে এবং ফলাফল শূন্যের চেয়ে আলাদা হলে শাখা সম্পাদন করে। কার্যত সমস্ত পিসি সিপাস এই নির্দেশনাটি প্রয়োগ করে। এটি লুপটি ব্যবহার করা আসলে একটি মাত্র (হ্যাঁ এক) 2 বাইট নির্দেশ:

00144ECE B9 0A 00 00 00       mov         ecx,0Ah  
label:
                          // something here
00144ED3 E2 FE                loop        label (0144ED3h)  // decrement ecx and jump to label if not zero

কোনটি দ্রুত তা আমাকে ব্যাখ্যা করতে হবে?

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

কিছু ক্ষেত্রে নির্বিশেষে যে কোনও মন্তব্য হিসাবে আপনি কেন আমি ভুল ইত্যাদি ইত্যাদি হিসাবে চিহ্নিত করতে পারি E আমি নিশ্চিত করি - হ্যাঁ এটি কীভাবে, কেন এবং কখন জানলে ডাউনলোড লুপ করা সুবিধাজনক।

পুনশ্চ. হ্যাঁ আমি জানি যে বুদ্ধিমান সংকলক (যথাযথ অপ্টিমাইজেশন স্তর সহ) লুপের জন্য পুনরায় লিখবে (আরোহী লুপের কাউন্টার সহ) ধ্রুবক লুপ পুনরাবৃত্তির জন্য সমতুল্য ... (বা এটি আনরোল করুন) ...


1

না, এটি সত্য নয়। এটি আরও দ্রুততর হতে পারে এমন একটি পরিস্থিতি হ'ল আপনি যখন অন্যথায় কোনও লুপের প্রতিটি পুনরাবৃত্তির সময়সীমাগুলি পরীক্ষা করতে কোনও ফাংশন কল করবেন।

for(int i=myCollection.size(); i >= 0; i--)
{
   ...
}

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


1
পরিষ্কার এবং দক্ষ হওয়ার জন্য আপনার কমপক্ষে অভ্যাস থাকা উচিত for(int i=0, siz=myCollection.size(); i<siz; i++)
লরেন্স ডল

1

মুল বক্তব্যটি হ'ল গণনা করার সময় আপনাকে i >= 0হ্রাস করতে পৃথকভাবে চেক করার দরকার নেই i। পালন:

for (i = 5; i--;) {
  alert(i);  // alert boxes showing 4, 3, 2, 1, 0
}

তুলনা এবং হ্রাস উভয়ই iএক অভিব্যক্তিতে করা যেতে পারে।

এটি কেন কম x86 নির্দেশিকায় ফোটে সে জন্য অন্যান্য উত্তরগুলি দেখুন।

এটি আপনার আবেদনে কোনও অর্থপূর্ণ পার্থক্য করে কিনা, এ সম্পর্কে আমি ভাল ধারণা করি যে আপনার কতগুলি লুপ রয়েছে এবং তারা কতটা গভীরভাবে বাসা বেঁধেছে তার উপর নির্ভর করে। তবে আমার কাছে এটি এটি করা যেমন পাঠযোগ্য তেমনি যাইহোক আমি এটি করি।


আমি মনে করি এটি দুর্বল শৈলী, কারণ এটি একটি পাঠক উপর নির্ভর করে যে একটি চক্র সাশ্রয় করার সম্ভাব্য মূল্যের জন্য i-- এর রিটার্ন মান i- এর পুরানো মান। এটি কেবল তখনই তাত্পর্যপূর্ণ হবে যদি সেখানে প্রচুর লুপ পুনরাবৃত্তি ঘটে থাকে এবং চক্রটি পুনরাবৃত্তির দৈর্ঘ্যের একটি উল্লেখযোগ্য ভগ্নাংশ ছিল, এবং আসলে রান সময় প্রদর্শিত হয়েছিল। এরপরে, কেউ (i = 5; --i;) চেষ্টা করবেন কারণ তারা শুনেছেন যে সি ++ এ আপনি যখন সামান্য তুচ্ছ প্রকারের হয়ে থাকেন তখন আপনি কিছুটা অস্থায়ী তৈরি করা এড়াতে চাইতে পারেন, এবং এখন আপনি বাগ বাগানে চলে যাচ্ছেন ভুল কোডকে ভুল দেখাচ্ছে বলে আপনার সুযোগটি দূরে ছুঁড়ে ফেলেছে।
mabraham

0

এখন, আমি মনে করি আপনার যথেষ্ট সমাবেশ বক্তৃতা ছিল :) আমি আপনাকে শীর্ষ-> ডাউন পদ্ধতির জন্য আরও একটি কারণ উপস্থাপন করতে চাই।

উপরে থেকে যাওয়ার কারণ খুব সহজ। লুপের মুখ্য অংশে আপনি ভুলক্রমে সীমানাটি পরিবর্তন করতে পারেন যা ভুল আচরণ বা এমনকি অবসানহীন লুপে শেষ হতে পারে।

জাভা কোডের এই ছোট্ট অংশটি দেখুন (এই কারণে ভাষাটি আমার কাছে অনুমান করা যায় না):

    System.out.println("top->down");
    int n = 999;
    for (int i = n; i >= 0; i--) {
        n++;
        System.out.println("i = " + i + "\t n = " + n);
    }
    System.out.println("bottom->up");
    n = 1;
    for (int i = 0; i < n; i++) {
        n++;
        System.out.println("i = " + i + "\t n = " + n);
    }

সুতরাং আমার বক্তব্যটি হ'ল আপনার নীচ থেকে নীচে যাওয়া বা সীমানা হিসাবে ধ্রুবক থাকার কথা বিবেচনা করা উচিত।


তাই না? !! আপনার ব্যর্থ উদাহরণটি সত্যই পাল্টা স্বজ্ঞাত, যা বলে, একটি খড়ের লোকের যুক্তি - কেউ কখনও এটি লিখেনি। একজন লিখতেন for (int i=0; i < 999; i++) {
লরেন্স ডল

@ সফটওয়্যার বানর কিছু গণনার ফলাফল হিসাবে কল্পনা করুন ... উদাহরণস্বরূপ আপনি কিছু সংগ্রহের মাধ্যমে পুনরাবৃত্তি করতে চাইতে পারেন এবং এর আকারটি সীমানা, তবে কিছু পার্শ্ব প্রতিক্রিয়া হিসাবে, আপনি লুপের শরীরে সংগ্রহে নতুন উপাদান যুক্ত করেন।
গ্যাব্রিয়েল Ščerbák

আপনি যদি কথোপকথনটিই তা করতে চান, তবে for(int xa=0; xa<collection.size(); xa++) { collection.add(SomeObject); ... }
লরেন্স ডল

@ সফটওয়্যার বানর আমি বিশেষত সংগ্রহ সম্পর্কে বিশেষভাবে কথা বলার চেয়ে আরও সাধারণ হতে চেয়েছিলাম, কারণ যে বিষয়ে আমি যুক্তি দিচ্ছি তা সংগ্রহের সাথে কিছু করার নেই
গ্যাব্রিয়েল এরক

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

-1

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

এটি আরও বেশি সত্য যখন পুনরাবৃত্তির সংখ্যাটি একটি ধ্রুবক নয় তবে একটি পরিবর্তনশীল হয়।

তুচ্ছ মামলায় সংকলক স্বয়ংক্রিয়ভাবে একটি লুপের গণনার দিকটি অপ্টিমাইজ করতে সক্ষম হতে পারে তবে আরও জটিল ক্ষেত্রে এটি হতে পারে যে প্রোগ্রামার জানে যে লুপের দিকটি সামগ্রিক আচরণের সাথে অপ্রাসঙ্গিক তবে সংকলক তা প্রমাণ করতে পারে না।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.