আমি ড্যাফের ডিভাইসে উইকিপিডিয়ায় নিবন্ধটি পড়েছি এবং এটি পাই না। আমি সত্যিই আগ্রহী, তবে আমি সেখানে ব্যাখ্যাটি বেশ কয়েকবার পড়েছি এবং ডাফের ডিভাইস কীভাবে কাজ করে তা এখনও পাই না।
এর আরও বিশদ ব্যাখ্যা কী হবে?
আমি ড্যাফের ডিভাইসে উইকিপিডিয়ায় নিবন্ধটি পড়েছি এবং এটি পাই না। আমি সত্যিই আগ্রহী, তবে আমি সেখানে ব্যাখ্যাটি বেশ কয়েকবার পড়েছি এবং ডাফের ডিভাইস কীভাবে কাজ করে তা এখনও পাই না।
এর আরও বিশদ ব্যাখ্যা কী হবে?
উত্তর:
অন্য কোথাও কিছু ভাল ব্যাখ্যা আছে, তবে আমাকে এটি দিয়ে চেষ্টা করুন। (এটি হোয়াইটবোর্ডে অনেক সহজ!) উইকিপিডিয়া উদাহরণটি এখানে কিছু স্বীকৃতি সহ।
ধরা যাক আপনি 20 বাইট অনুলিপি করছেন। প্রথম পাসের জন্য প্রোগ্রামটির প্রবাহ নিয়ন্ত্রণ:
int count; // Set to 20
{
int n = (count + 7) / 8; // n is now 3. (The "while" is going
// to be run three times.)
switch (count % 8) { // The remainder is 4 (20 modulo 8) so
// jump to the case 4
case 0: // [skipped]
do { // [skipped]
*to = *from++; // [skipped]
case 7: *to = *from++; // [skipped]
case 6: *to = *from++; // [skipped]
case 5: *to = *from++; // [skipped]
case 4: *to = *from++; // Start here. Copy 1 byte (total 1)
case 3: *to = *from++; // Copy 1 byte (total 2)
case 2: *to = *from++; // Copy 1 byte (total 3)
case 1: *to = *from++; // Copy 1 byte (total 4)
} while (--n > 0); // N = 3 Reduce N by 1, then jump up
// to the "do" if it's still
} // greater than 0 (and it is)
}
এখন, দ্বিতীয় পাসটি শুরু করুন, আমরা কেবল নির্দেশিত কোডটি চালাই:
int count; //
{
int n = (count + 7) / 8; //
//
switch (count % 8) { //
//
case 0: //
do { // The while jumps to here.
*to = *from++; // Copy 1 byte (total 5)
case 7: *to = *from++; // Copy 1 byte (total 6)
case 6: *to = *from++; // Copy 1 byte (total 7)
case 5: *to = *from++; // Copy 1 byte (total 8)
case 4: *to = *from++; // Copy 1 byte (total 9)
case 3: *to = *from++; // Copy 1 byte (total 10)
case 2: *to = *from++; // Copy 1 byte (total 11)
case 1: *to = *from++; // Copy 1 byte (total 12)
} while (--n > 0); // N = 2 Reduce N by 1, then jump up
// to the "do" if it's still
} // greater than 0 (and it is)
}
এখন, তৃতীয় পাসটি শুরু করুন:
int count; //
{
int n = (count + 7) / 8; //
//
switch (count % 8) { //
//
case 0: //
do { // The while jumps to here.
*to = *from++; // Copy 1 byte (total 13)
case 7: *to = *from++; // Copy 1 byte (total 14)
case 6: *to = *from++; // Copy 1 byte (total 15)
case 5: *to = *from++; // Copy 1 byte (total 16)
case 4: *to = *from++; // Copy 1 byte (total 17)
case 3: *to = *from++; // Copy 1 byte (total 18)
case 2: *to = *from++; // Copy 1 byte (total 19)
case 1: *to = *from++; // Copy 1 byte (total 20)
} while (--n > 0); // N = 1 Reduce N by 1, then jump up
// to the "do" if it's still
} // greater than 0 (and it's not, so bail)
} // continue here...
20 বাইট এখন অনুলিপি করা হয়।
দ্রষ্টব্য: মূল ডাফের ডিভাইস (উপরে দেখানো) to
ঠিকানায় আই / ও ডিভাইসে অনুলিপি করা হয়েছে । সুতরাং, পয়েন্টারটি বাড়ানোর প্রয়োজন ছিল না *to
। দুটি মেমরি বাফারগুলির মধ্যে অনুলিপি করার সময় আপনাকে ব্যবহার করতে হবে *to++
।
do
এত কিছুর দিকে তাকাবেন না । পরিবর্তে, switch
এবং while
পুরানো ফ্যাশন হিসাবে গণিত GOTO
স্ট্যাটাস বা jmp
অফসেট সহ এসেম্বলারের বিবৃতি হিসাবে দেখুন। switch
কিছু গণিত করে এবং তারপর jmp
সঠিক জায়গায় s। while
একটি বুলিয়ান চেক করে এবং তারপর অন্ধভাবে jmp
সম্পর্কে যেখানে ডান দিকে গুলি do
ছিল।
ডাঃ Dobb এর জার্নালে ব্যাখ্যা শ্রেষ্ঠ যে আমি বিষয়ে পাওয়া যায়।
এটি আমার আহা মুহুর্ত হচ্ছে:
for (i = 0; i < len; ++i) {
HAL_IO_PORT = *pSource++;
}
হয়ে:
int n = len / 8;
for (i = 0; i < n; ++i) {
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
HAL_IO_PORT = *pSource++;
}
n = len % 8;
for (i = 0; i < n; ++i) {
HAL_IO_PORT = *pSource++;
}
হয়ে:
int n = (len + 8 - 1) / 8;
switch (len % 8) {
case 0: do { HAL_IO_PORT = *pSource++;
case 7: HAL_IO_PORT = *pSource++;
case 6: HAL_IO_PORT = *pSource++;
case 5: HAL_IO_PORT = *pSource++;
case 4: HAL_IO_PORT = *pSource++;
case 3: HAL_IO_PORT = *pSource++;
case 2: HAL_IO_PORT = *pSource++;
case 1: HAL_IO_PORT = *pSource++;
} while (--n > 0);
}
len%8
4 হলেও , এটি কেস 4, কেস 2, কেস 2, এবং কেস 1 কার্যকর করবে এবং তারপরে পিছনে ঝাঁপিয়ে পড়বে এবং পরবর্তী লুপ থেকে সমস্ত কেস কার্যকর করবে। এটি সেই অংশ যা ব্যাখ্যা করা দরকার, লুপটি এবং সুইচ বিবৃতিটি "ইন্টারঅ্যাক্ট" করে।
len % 8
বাইটগুলি অনুলিপি করা হবে না?
ডাফের ডিভাইসে দুটি মূল বিষয় রয়েছে। প্রথমত, যা আমি সন্দেহ করি তা বোঝা সহজতর অংশ, লুপটি অনিয়ন্ত্রিত। লুপটি শেষ হয়েছে কিনা তা পরীক্ষা করে এবং লুপের শীর্ষে ফিরে এসে জড়িত কিছু ওভারহেডকে এড়িয়ে আরও গতির জন্য এটি বৃহত্তর কোড আকারের ব্যবসা করে। যখন জাম্পিংয়ের পরিবর্তে সরলরেখার কোডটি চালায় তখন সিপিইউ দ্রুত চালাতে পারে।
দ্বিতীয় দিকটি হ'ল সুইচ স্টেটমেন্ট। এটি কোডটিকে প্রথমবারের মধ্য দিয়ে লুপের মাঝখানে লাফ দেওয়ার অনুমতি দেয় । বেশিরভাগ লোকের কাছে অবাক করা বিষয়টি হ'ল এই জাতীয় জিনিস অনুমোদিত। ঠিক আছে, এটি অনুমোদিত। এক্সিকিউশন গণনা করা কেস লেবেল থেকে শুরু হয় এবং তারপরে এটি প্রতিটি ক্রমাগত কার্যনির্বাহী বিবৃতিতে পড়ে , যেমন অন্য কোনও সুইচ স্টেটমেন্টের মতো। শেষ কেস লেবেলের পরে, এক্সিকিউশনটি লুপের নীচে পৌঁছে যায়, যে সময়ে এটি শীর্ষে ফিরে আসে। লুপ শীর্ষে অবস্থিত ভিতরে তাই সুইচ আর না পুনরায় মূল্যায়ন করা হয়, সুইচ বিবৃতি।
মূল লুপটি আটবার অবিরাম হয়, সুতরাং পুনরাবৃত্তির সংখ্যা আট দ্বারা বিভক্ত হয়। যদি অনুলিপি করা বাইটের সংখ্যা আটটির একাধিক না হয়, তবে কিছু বাইট বাকি আছে। বেশিরভাগ অ্যালগরিদম যা বাইটের ব্লকগুলি একবারে অনুলিপি করে তা বাকি বাক্যগুলি হ্যান্ডেল করবে, তবে ডাফের ডিভাইস শুরুতেই এগুলি পরিচালনা করে। count % 8
বাকীটি কী হবে তা নির্ধারণ করার জন্য ফাংশনটি স্যুইচ স্টেটমেন্টের জন্য গণনা করে , সেই অনেকগুলি বাইটের জন্য কেস লেবেলে ঝাঁপ দেয় এবং সেগুলি অনুলিপি করে। তারপরে লুপ আটটি বাইটের গ্রুপগুলি অনুলিপি করে চলেছে।
ডফস ডিভাইসের বিন্দুটি হ'ল একটি শক্ত মেমকি প্রয়োগের ক্ষেত্রে তুলনার সংখ্যা হ্রাস করা।
মনে করুন আপনি একটি থেকে বি থেকে 'গণনা' বাইটগুলি অনুলিপি করতে চান, সরাসরি এগিয়ে যাওয়ার পদ্ধতিটি নিম্নলিখিতটি করা:
do {
*a = *b++;
} while (--count > 0);
এটি 0 এর ওপরে কিনা তা দেখতে আপনাকে কতবার গণনার তুলনা করতে হবে? 'গণনা' বার।
এখন, ডাফ ডিভাইসটি একটি স্যুইচ কেসের একটি অদ্ভুত অনিচ্ছাকৃত পার্শ্ব প্রতিক্রিয়া ব্যবহার করে যা আপনাকে 8 / গণনা করার জন্য প্রয়োজনীয় তুলনা সংখ্যা হ্রাস করতে দেয়।
এখন ধরুন আপনি ডাফস ডিভাইসটি ব্যবহার করে 20 বাইট অনুলিপি করতে চান, আপনার কতটি তুলনা প্রয়োজন? শুধুমাত্র 3, যেহেতু আপনি সর্বশেষ প্রথমটি ব্যতীত এক সময় আটটি বাইট অনুলিপি করেন যেখানে আপনি কেবল 4 টি অনুলিপি করেন।
আপডেট করা: আপনাকে 8 টি তুলনা / কেস-ইন-স্যুইচ বিবৃতি করতে হবে না, তবে এটি ফাংশনের আকার এবং গতির মধ্যে একটি বাণিজ্য-যুক্তিসঙ্গত reasonable
আমি যখন প্রথমবার এটি পড়ি তখন আমি এটিকে স্বয়ংক্রিয়ভাবে তৈরি করেছি
void dsend(char* to, char* from, count) {
int n = (count + 7) / 8;
switch (count % 8) {
case 0: do {
*to = *from++;
case 7: *to = *from++;
case 6: *to = *from++;
case 5: *to = *from++;
case 4: *to = *from++;
case 3: *to = *from++;
case 2: *to = *from++;
case 1: *to = *from++;
} while (--n > 0);
}
}
এবং আমার কি ধারণা ছিল না যে কি ঘটছে।
এই প্রশ্নটি যখন জিজ্ঞাসা করা হয়েছিল তখন নাও হতে পারে তবে এখন উইকিপিডিয়ায় খুব ভাল ব্যাখ্যা রয়েছে
ডিটিতে দুটি বৈশিষ্ট্যের কারণে ডিভাইসটি বৈধ, বৈধ সি:
- ভাষার সংজ্ঞাটিতে স্যুইচ স্টেটমেন্টের স্বাচ্ছন্দ্য বিবরণ। ডিভাইসের উদ্ভাবনের সময় এটি সি প্রোগ্রামিং ল্যাঙ্গুয়েজের প্রথম সংস্করণ ছিল যার জন্য কেবল প্রয়োজন যে স্যুইচটির নিয়ন্ত্রিত বিবৃতিটি একটি সিনট্যাক্টিক্যালি বৈধ (যৌগিক) বিবৃতি হওয়া উচিত যার মধ্যে কেস লেবেলগুলি কোনও উপ-বিবৃতি উপস্থাপনের জন্য উপস্থিত হতে পারে। একটি ব্রেক স্টেটমেন্টের অভাবে, নিয়ন্ত্রণের প্রবাহ একটি কেস লেবেল দ্বারা নিয়ন্ত্রিত একটি স্টেটমেন্ট থেকে পরবর্তী দ্বারা নিয়ন্ত্রিত একটি বিবৃতি থেকে নিয়ন্ত্রণের সাথে মিলিত হবে, এর অর্থ এই কোডটি গণনা অনুলিপিগুলির উত্তরসূরি নির্দিষ্ট করে মেমোরি-ম্যাপযুক্ত আউটপুট পোর্টে ক্রমিক উত্স ঠিকানাগুলি।
- সিটিতে লুপের মাঝখানে আইনীভাবে লাফ দেওয়ার ক্ষমতা
1: ডফস ডিভাইসটি লুপ আনআরোলিংয়ের একটি বিশেষ প্রতিচ্ছবি। লুপ আনرولিং কী?
যদি আপনার কোনও লুপে N বার সঞ্চালনের অপারেশন থাকে তবে আপনি লুপ N / n বার প্রয়োগ করে এবং তারপরে লুপ ইনলাইনিং (আন্রোলিং) এ লুপ কোড এন বারের সাথে উদাহরণস্বরূপ প্রতিস্থাপন করে: প্রোগ্রামের আকারটি গতির জন্য ট্রেড করতে পারেন:
for (int i=0; i<N; i++) {
// [The loop code...]
}
সঙ্গে
for (int i=0; i<N/n; i++) {
// [The loop code...]
// [The loop code...]
// [The loop code...]
...
// [The loop code...] // n times!
}
N% n == 0 - ডফের দরকার নেই যদি এটি দুর্দান্ত কাজ করে! যদি এটি সত্য না হয় তবে আপনাকে বাকীটি পরিচালনা করতে হবে - যা একটি ব্যথা।
2: ড্যাফস ডিভাইসটি কীভাবে এই মানক লুপটি আন্রোলিং থেকে আলাদা হয়?
ডফস ডিভাইসটি যখন বাকি% লুপ চক্রগুলি মোকাবেলা করার জন্য কেবল একটি চতুর উপায়, যখন N% n! = 0. সম্পূর্ণভাবে / যখন মান / লুপ আন্রোলিং অনুসারে N / n সংখ্যাটি কার্যকর করে (কারণ কেস 0 প্রযোজ্য)। লুপের মাধ্যমে শেষ দৌড়ে ('N / n + 1'th বার) কেসটি শুরু হয় এবং আমরা N% n কেটে চলে যাই এবং লুপ কোডটি' বাকী 'সংখ্যাটি চালাই।
আপনি যা চাইছেন তা আমি 100% নিশ্চিত না হলেও, এখানে ...
ডফের ডিভাইসটির ঠিকানাটি যে লুপটি আনমন্ডিংয়ের মধ্যে রয়েছে তা হ'ল (আপনি যে পোস্ট করেছেন উইকি লিঙ্কটিতে আপনি অবশ্যই সন্দেহ প্রকাশ করেছেন)। এটির সাথে মূলত যা সমান হয় তা হ'ল মেমরির পদক্ষেপের চেয়ে রান-টাইম দক্ষতার একটি অপ্টিমাইজেশন। ডাফের ডিভাইসটি কেবল কোনও পুরানো সমস্যার চেয়ে সিরিয়াল অনুলিপি নিয়ে কাজ করে, তবে একটি লুপের তুলনায় যে পরিমাণ তুলনা করা প্রয়োজন তার সংখ্যা হ্রাস করে কীভাবে অনুকূলিতকরণ করা যায় তার একটি সর্বোত্তম উদাহরণ।
বিকল্প উদাহরণ হিসাবে, এটি বুঝতে সহজ করে তুলতে পারে, কল্পনা করুন যে আপনি লুপ করতে চান এমন আইটেমের একটি অ্যারে রেখেছেন এবং প্রতিবার এগুলিতে 1 যোগ করুন ... সাধারণভাবে, আপনি লুপের জন্য এবং প্রায় 100 বার লুপ ব্যবহার করতে পারেন । এটি মোটামুটি যৌক্তিক বলে মনে হয় এবং এটি হ'ল ... তবে লুপটি আনওয়াইন্ড করে একটি অপ্টিমাইজেশন তৈরি করা যেতে পারে (সম্ভবত খুব বেশি দূরে নয় ... অথবা আপনি কেবল লুপটি ব্যবহার করতে পারবেন না)।
লুপের জন্য একটি নিয়মিত:
for(int i = 0; i < 100; i++)
{
myArray[i] += 1;
}
হয়ে
for(int i = 0; i < 100; i+10)
{
myArray[i] += 1;
myArray[i+1] += 1;
myArray[i+2] += 1;
myArray[i+3] += 1;
myArray[i+4] += 1;
myArray[i+5] += 1;
myArray[i+6] += 1;
myArray[i+7] += 1;
myArray[i+8] += 1;
myArray[i+9] += 1;
}
ডাফের ডিভাইস যা করে তা হ'ল এই ধারণাটি বাস্তবায়িত করে সি তে, তবে (যেমন আপনি উইকিতে দেখেছেন) সিরিয়াল অনুলিপি সহ। আপনি উপরের অবাঞ্ছিত উদাহরণ সহ যা দেখছেন তা মূলের 100 এর সাথে তুলনা করে 10 - এটি একটি গৌণ, তবে সম্ভবত তাত্পর্যপূর্ণ, অপ্টিমাইজেশনের পরিমাণ।
এখানে একটি অ-বিশদ বিবরণ যা ডফের ডিভাইসের কর্কস বলে আমি মনে করি:
বিষয়টি হ'ল সি মূলত অ্যাসেম্বলি ভাষার জন্য একটি সুন্দর মুখোমুখি (পিডিপি -7 অ্যাসেমব্লিকে নির্দিষ্ট করার জন্য; আপনি যদি অধ্যয়ন করেন তবে দেখতে পাবেন যে মিলগুলি কতটা আকর্ষণীয়)। এবং, সমাবেশের ভাষায়, আপনার কাছে আসলে লুপ নেই - আপনার কাছে লেবেল এবং শর্তসাপেক্ষে - শাখার নির্দেশাবলী রয়েছে। সুতরাং লুপটি কোথাও একটি লেবেল এবং একটি শাখা সহ নির্দেশের সামগ্রিক ক্রমের কেবলমাত্র একটি অংশ:
instruction
label1: instruction
instruction
instruction
instruction
jump to label1 some condition
এবং একটি স্যুইচ নির্দেশ শাখা করা / কিছুটা এগিয়ে লাফিয়ে:
evaluate expression into register r
compare r with first case value
branch to first case label if equal
compare r with second case value
branch to second case label if equal
etc....
first_case_label:
instruction
instruction
second_case_label:
instruction
instruction
etc...
সমাবেশে এটি কীভাবে এই দুটি কন্ট্রোল স্ট্রাকচারকে একত্রিত করা যায় তা সহজেই অনুমেয় এবং আপনি যখন সেভাবে মনে করেন, সি-তে তাদের সংমিশ্রণটি এত অদ্ভুত বলে মনে হয় না।
এটি একটি উত্তর যা আমি ডাফের ডিভাইস সম্পর্কে অন্য একটি পোস্টে পোস্ট করেছিলাম যা সদৃশ হিসাবে প্রশ্নটি বন্ধ হওয়ার আগে কিছু উপকার পেয়েছিল। আমি মনে করি এটি কেন আপনার এই নির্মাণটি এড়ানো উচিত সে সম্পর্কে এখানে কিছুটা মূল্যবান প্রসঙ্গ সরবরাহ করে।
"এটি ডাফের ডিভাইস It's
যেহেতু এখানে বেশিরভাগ উত্তরগুলি সাধারণভাবে ইতিবাচক বলে মনে হয় আমি ডাউনসাইডগুলি হাইলাইট করতে যাচ্ছি।
এই কোডের সাহায্যে একটি সংকলক লুপের বডিটিতে কোনও অপ্টিমাইজেশন প্রয়োগ করতে লড়াই করতে চলেছে। আপনি যদি স্রেফ লুপ হিসাবে কোডটি লিখে থাকেন তবে আধুনিক সংকলকটি আপনার জন্য এই তালিকাভুক্তি নিয়ন্ত্রণ করতে সক্ষম হবে। এইভাবে আপনি পঠনযোগ্যতা এবং কার্য সম্পাদন বজায় রাখবেন এবং লুপ বডিতে অন্যান্য অপ্টিমাইজেশান প্রয়োগ হওয়ার কিছুটা আশা রয়েছে।
অন্যরা দ্বারা উল্লিখিত উইকিপিডিয়া নিবন্ধটি এমনকি আরও জানায় যে যখন এই 'প্যাটার্ন'টি Xfree86 উত্স কোডের কার্যকারিতা থেকে সরিয়ে নেওয়া হয়েছিল তখন বাস্তবে উন্নতি হয়েছিল।
এই ফলাফলটি আপনার যে কোনও কোডের প্রয়োজন হতে পারে বলে মনে হয় অন্ধভাবে হাত দেওয়ার জন্য of এটি কম্পাইলারটিকে সঠিকভাবে কাজ করতে বাধা দেয়, আপনার কোডটি কম পঠনযোগ্য এবং বাগের প্রবণতায় পরিণত করে এবং সাধারণত এটি ধীর করে দেয়। আপনি যদি প্রথমে জিনিসগুলি সঠিকভাবে করছিলেন, অর্থাত্ সরল কোড লেখা, তারপরে বাধাগ্রস্থের জন্য প্রোফাইলিং, তারপর অনুকূলকরণ, আপনি কখনও এই জাতীয় কিছু ব্যবহার করার কথা ভাবেননি think যাইহোক আধুনিক সিপিইউ এবং সংকলক সহ নয়।
এটি বুঝতে পেরে ভাল, তবে আপনি যদি সত্যিই এটি ব্যবহার করেন তবে আমি অবাক হব। "
সবেমাত্র পরীক্ষা-নিরীক্ষা করে, ইন্টারলিভিং সুইচ এবং লুপ ছাড়াই আরও একটি রূপ পেয়েছে:
int n = (count + 1) / 8;
switch (count % 8)
{
LOOP:
case 0:
if(n-- == 0)
break;
putchar('.');
case 7:
putchar('.');
case 6:
putchar('.');
case 5:
putchar('.');
case 4:
putchar('.');
case 3:
putchar('.');
case 2:
putchar('.');
case 1:
putchar('.');
default:
goto LOOP;
}