อุปกรณ์ของ Duff ทำงานอย่างไร


147

ฉันได้อ่านบทความเกี่ยวกับ Wikipedia บนอุปกรณ์ของ Duffแล้วและฉันไม่เข้าใจ ฉันสนใจจริงๆ แต่ฉันได้อ่านคำอธิบายที่นั่นสองสามครั้งและฉันก็ยังไม่เข้าใจว่าอุปกรณ์ของ Duff ทำงานอย่างไร

คำอธิบายรายละเอียดเพิ่มเติมจะเป็นอย่างไร

คำตอบ:


240

มีคำอธิบายดีๆที่อื่น แต่ให้ฉันลองดู (นี่เป็นไวท์บอร์ดที่ง่ายกว่ามาก!) นี่คือตัวอย่างของ Wikipedia ที่มีสัญลักษณ์

สมมติว่าคุณกำลังคัดลอก 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 ไบต์

หมายเหตุ: อุปกรณ์ของดัฟฟ์ดั้งเดิม (แสดงด้านบน) คัดลอกไปยังอุปกรณ์ I / O ตามที่toอยู่ ดังนั้นจึงไม่จำเป็นที่จะต้องเพิ่มพ*toอยน์เตอร์ *to++เมื่อคัดลอกระหว่างสองหน่วยความจำบัฟเฟอร์ที่คุณจะต้องใช้


1
กรณีที่ 0: clause สามารถข้ามไปได้และตรวจสอบ clause อื่น ๆ ที่อยู่ภายใน do ในขณะที่ลูปที่เป็นอาร์กิวเมนต์ของ clause clause ได้อย่างไร หากประโยคเดียวที่อยู่นอกสิ่งที่ต้องทำขณะที่ข้ามลูปทำไมสวิตช์ไม่จบ
Aurelius

14
อย่ามองวงเล็บปีกกาอย่างหนัก อย่ามองที่doมาก ให้ดูที่switchและที่whileเป็นแบบเก่าที่คำนวณGOTOหรือjmpงบแอสเซมเบลอร์ด้วยอ็อฟเซ็ต switchไม่คณิตศาสตร์บางส่วนแล้วjmps สถานที่ที่เหมาะสม whileไม่เช็คบูลแล้วสุ่มสี่สุ่มห้าjmps ไปทางขวาเกี่ยวกับที่doเป็น
Clinton Pierce

หากเป็นสิ่งที่ดีมากทำไมทุกคนไม่ใช้สิ่งนี้ มีข้อเสียหรือไม่?
AlphaGoku

@AlphaGoku การอ่าน
LF

108

คำอธิบายดร. Dobb ของวารสารที่ดีที่สุดที่ผมพบว่าในหัวข้อ

นี่เป็นช่วงเวลา AHA ของฉัน:

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);
}

โพสต์ที่ดี (บวกฉันต้องค้นหาคำตอบที่ดีจากคุณเพื่อ upvote;) 2 ลง 13 ไป: stackoverflow.com/questions/359727#486543 ) เพลิดเพลินไปกับตราคำตอบที่ดี
VonC

13
ความจริงที่สำคัญที่นี่และทำให้อุปกรณ์ของ Duff ไม่สามารถเข้าใจได้สำหรับฉันเป็นเวลานานที่สุดก็คือโดยการเล่นโวหารของ C หลังจากครั้งแรกที่มันมาถึงในขณะที่มันกระโดดข้ามและดำเนินการงบทั้งหมด ดังนั้นแม้ว่าจะlen%8เป็น 4 ก็จะดำเนินการกรณีที่ 4, กรณีที่ 2, กรณีที่ 2 และกรณีที่ 1 และจากนั้นกระโดดกลับและดำเนินการทุกกรณีจากการวนรอบต่อไปเป็นต้นไป นี่คือส่วนที่ต้องการอธิบายวิธีการวนรอบและคำสั่งสลับ "โต้ตอบ"
ShreevatsaR

2
บทความดร. ดอบส์นั้นดี แต่นอกเหนือจากลิงก์คำตอบไม่ได้เพิ่มอะไรเลย ดูคำตอบของ Rob Kennedy ด้านล่างซึ่งเป็นจุดสำคัญเกี่ยวกับส่วนที่เหลือของขนาดการถ่ายโอนที่ถูกจัดการก่อนตามด้วยศูนย์การถ่ายโอนข้อมูลตั้งแต่ 8 ไบต์ขึ้นไป ในความคิดของฉันที่เป็นกุญแจสำคัญในการทำความเข้าใจรหัสนี้
Richard Chambers

3
ฉันทำบางสิ่งขาดหายไปหรือในบางส่วนของข้อมูลโค้ดที่สองlen % 8จะไม่ถูกคัดลอก?
มือใหม่

ฉันติดอยู่แล้วลืมไปว่าถ้าคุณไม่เขียนคำสั่ง break ในตอนท้ายของรายการคำสั่งของ case, C (หรือภาษาอื่น ๆ ) จะดำเนินการคำสั่ง ดังนั้นหากคุณสงสัยว่าทำไมอุปกรณ์ของดัฟฟ์ทำงานได้เลยนี่เป็นส่วนสำคัญของมัน
goonerify

75

มีสองสิ่งสำคัญสำหรับอุปกรณ์ของ Duff ก่อนอื่นซึ่งฉันสงสัยว่าเป็นส่วนที่เข้าใจง่ายกว่าการวนซ้ำนั้นไม่ได้เลื่อน การค้าขนาดรหัสขนาดใหญ่กว่าสำหรับความเร็วเพิ่มเติมโดยการหลีกเลี่ยงค่าใช้จ่ายที่เกี่ยวข้องในการตรวจสอบว่าการวนซ้ำเสร็จแล้วและกระโดดกลับไปด้านบนของการวนรอบ ซีพียูสามารถทำงานได้เร็วขึ้นเมื่อรันโค้ดแบบเส้นตรงแทนที่จะกระโดด

กว้างยาวที่สองคือคำสั่งสลับ จะช่วยให้รหัสที่จะกระโดดลงในช่วงกลางของลูปเป็นครั้งแรกผ่าน ส่วนที่น่าประหลาดใจสำหรับคนส่วนใหญ่คือสิ่งที่ได้รับอนุญาต ได้รับอนุญาต การดำเนินการเริ่มต้นที่ป้ายชื่อเคสที่คำนวณได้และจากนั้นจะผ่านไปยังแต่ละคำสั่งการมอบหมายต่อเนื่องเช่นเดียวกับคำสั่งเปลี่ยนอื่น ๆ หลังจากเลเบลเคสสุดท้ายการดำเนินการมาถึงด้านล่างของลูปตรงจุดที่มันกระโดดกลับไปด้านบน ด้านบนของลูปอยู่ในคำสั่ง switch ดังนั้นสวิตช์จะไม่ถูกประเมินอีกครั้ง

การวนซ้ำดั้งเดิมนั้นไม่ได้วนรอบแปดครั้งดังนั้นจำนวนการวนซ้ำจะถูกหารด้วยแปด หากจำนวนไบต์ที่จะคัดลอกไม่ใช่หลายแปดก็มีบางส่วนที่เหลือ อัลกอริทึมส่วนใหญ่ที่คัดลอกบล็อกของไบต์ในแต่ละครั้งจะจัดการกับไบต์ที่เหลือในตอนท้าย แต่อุปกรณ์ของ Duff จัดการกับพวกเขาที่จุดเริ่มต้น ฟังก์ชั่นจะคำนวณcount % 8หาคำสั่ง switch เพื่อคำนวณว่าส่วนที่เหลือจะเป็นเท่าไหร่ข้ามไปที่ป้ายชื่อเคสสำหรับหลายไบต์และคัดลอก จากนั้นลูปจะยังคงคัดลอกกลุ่มแปดไบต์


5
คำอธิบายนี้สมเหตุสมผลมากขึ้น กุญแจสำคัญสำหรับฉันที่จะเข้าใจว่าส่วนที่เหลือจะถูกคัดลอกก่อนแล้วส่วนที่เหลือในบล็อก 8 ไบต์ซึ่งเป็นเรื่องผิดปกติตั้งแต่ที่กล่าวถึงส่วนใหญ่คุณจะคัดลอกในบล็อก 8 ไบต์แล้วคัดลอกส่วนที่เหลือ การทำส่วนที่เหลือก่อนเป็นกุญแจสำคัญในการทำความเข้าใจอัลกอริทึมนี้
Richard Chambers

+1 สำหรับการกล่าวถึงตำแหน่งที่บ้า / การซ้อนของสวิตช์ / ในขณะที่ลูป นึกไม่ออกว่ามาจากภาษาอย่าง Java ...
Parobay

13

จุดของอุปกรณ์ดัฟฟ์คือการลดจำนวนการเปรียบเทียบในการใช้งาน memcpy ที่เข้มงวด

สมมติว่าคุณต้องการคัดลอก 'count' bytes จาก a ถึง b วิธีการส่งต่อโดยตรงคือการทำสิ่งต่อไปนี้:

  do {                      
      *a = *b++;            
  } while (--count > 0);

คุณต้องเปรียบเทียบจำนวนครั้งเพื่อดูว่าเป็น 0 ข้างต้นหรือไม่ 'นับ' ครั้ง

ตอนนี้อุปกรณ์ดัฟฟ์ใช้ผลข้างเคียงที่น่ารังเกียจโดยไม่ตั้งใจของตัวเรือนสวิตช์ซึ่งช่วยให้คุณลดจำนวนการเปรียบเทียบที่จำเป็นในการนับ / 8

ตอนนี้สมมติว่าคุณต้องการคัดลอก 20 ไบต์โดยใช้อุปกรณ์ดัฟฟ์คุณต้องการเปรียบเทียบกี่ชุด? เพียง 3 เนื่องจากคุณคัดลอกแปดไบต์ในเวลายกเว้นสุดท้ายคนแรกที่คุณคัดลอกเพียง 4

อัปเดต: คุณไม่ต้องทำการเปรียบเทียบ 8 รายการ / คำสั่งสลับตัวพิมพ์ใหญ่ - เล็ก แต่การแลกเปลี่ยนระหว่างขนาดฟังก์ชั่นและความเร็วนั้นสมเหตุสมผล


3
โปรดทราบว่าอุปกรณ์ของดัฟฟ์ไม่ได้ จำกัด อยู่ที่การทำซ้ำ 8 รายการในคำสั่งสลับ
แปลกหน้า

ทำไมคุณไม่สามารถใช้แทน --count, count = count-8 ได้ และใช้วงที่สองเพื่อจัดการกับส่วนที่เหลือ?
hhafez

1
Hhafez คุณสามารถใช้วงที่สองเพื่อจัดการกับส่วนที่เหลือ แต่ตอนนี้คุณมีโค๊ดมากเป็นสองเท่าในการทำสิ่งเดียวกันโดยไม่เพิ่มความเร็ว
ร็อบเคนเนดี

โจฮานคุณย้อนกลับไป 4 ไบต์ที่เหลือจะถูกคัดลอกในการวนซ้ำครั้งแรกของลูปไม่ใช่ครั้งสุดท้าย
ร็อบเคนเนดี

8

เมื่อฉันอ่านมันเป็นครั้งแรกฉันทำการฟอร์แมตใหม่โดยอัตโนมัติ

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);
    }
}

และฉันก็ไม่รู้ว่าเกิดอะไรขึ้น

อาจไม่ใช่ตอนที่ถามคำถามนี้ แต่ตอนนี้Wikipedia มีคำอธิบายที่ดีมาก

อุปกรณ์ถูกต้องตามกฎหมาย C โดยอาศัยสองคุณลักษณะใน C:

  • ข้อมูลจำเพาะที่ผ่อนคลายของคำสั่งสวิตช์ในคำจำกัดความของภาษา ในช่วงเวลาของการประดิษฐ์อุปกรณ์นี้เป็นรุ่นแรกของภาษาการเขียนโปรแกรม C ซึ่งต้องการเพียงว่าคำสั่งควบคุมของสวิทช์เป็นคำสั่งที่ถูกต้อง syntactically (สารประกอบ) ภายในกรณีที่ป้ายชื่อสามารถปรากฏนำหน้าคำสั่งย่อยใด ๆ ร่วมกับความจริงที่ว่าหากไม่มีคำสั่ง break การไหลของการควบคุมจะลดลงจากคำสั่งที่ควบคุมโดยป้ายกำกับกรณีหนึ่งไปยังที่ควบคุมโดยถัดไปซึ่งหมายความว่ารหัสระบุการสืบทอดสำเนานับจาก แหล่งที่มาตามลำดับไปยังพอร์ตเอาต์พุตที่แม็พหน่วยความจำ
  • ความสามารถในการกระโดดอย่างถูกกฎหมายในช่วงกลางของวงในซี

6

1: อุปกรณ์ดัฟฟ์เป็นสิ่งที่ทำให้เกิดการวนซ้ำของการคลายออก การเปิดลูปคืออะไร
หากคุณมีการดำเนินการเพื่อดำเนินการ N ครั้งในลูปคุณสามารถแลกเปลี่ยนขนาดโปรแกรมเพื่อความเร็วได้โดยการดำเนินการลูป 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: อุปกรณ์ Duffs แตกต่างจากการวนลูปมาตรฐานนี้อย่างไร
อุปกรณ์ดัฟฟ์เป็นวิธีที่ชาญฉลาดในการจัดการกับวนรอบส่วนที่เหลือเมื่อ N% n! = 0 ทั้งทำ / ในขณะที่ดำเนินการจำนวน N / n จำนวนครั้งตามการเปิดลูปมาตรฐาน (เพราะกรณีที่ใช้ 0) ในการเรียกใช้ครั้งสุดท้ายผ่านลูป (เวลา 'N / n + 1'th) เคสเตะเข้าและเราข้ามไปยังเคส N% n และรันโค้ดลูปตามจำนวน' ส่วนที่เหลือ '


ฉันได้รับความสนใจในอุปกรณ์ Duffs ต่อไปนี้คำถามนี้: stackoverflow.com/questions/17192246/switch-case-weird-scoping ดังนั้นคิดว่ารหัส Id ไปที่ clarifing ดัฟฟ์ - ไม่แน่ใจว่ามันเป็นการปรับปรุงคำตอบที่มีอยู่ ...
Ricibob

3

แม้ว่าฉันจะไม่แน่ใจ 100% ว่าคุณต้องการอะไร แต่ที่นี่จะไป ...

ปัญหาที่อยู่อุปกรณ์ของ Duff เป็นหนึ่งในวงคลี่คลาย (เนื่องจากคุณไม่ต้องสงสัยเลยว่าได้เห็นในลิงก์ Wiki ที่คุณโพสต์) สิ่งนี้โดยทั่วไปหมายถึงการเพิ่มประสิทธิภาพของเวลาทำงานมากกว่าการใช้หน่วยความจำ อุปกรณ์ของดัฟฟ์เกี่ยวข้องกับการทำสำเนาแบบอนุกรมมากกว่าปัญหาเก่า ๆ แต่เป็นตัวอย่างแบบคลาสสิกของวิธีการเพิ่มประสิทธิภาพที่สามารถทำได้โดยการลดจำนวนครั้งที่การเปรียบเทียบจะต้องทำในลูป

เป็นอีกทางเลือกหนึ่งซึ่งอาจทำให้เข้าใจง่ายขึ้นลองนึกภาพว่าคุณมีรายการที่คุณต้องการวนซ้ำและเพิ่ม 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;
}

สิ่งที่อุปกรณ์ของดัฟฟ์ใช้คือการนำความคิดนี้ไปใช้ใน C แต่ (เท่าที่คุณเห็นบนวิกิ) ด้วยสำเนาอนุกรม สิ่งที่คุณเห็นด้านบนด้วยตัวอย่างที่ไม่ได้ผูกมัดคือการเปรียบเทียบ 10 รายการเมื่อเปรียบเทียบกับต้นฉบับ 100 รายการซึ่งเป็นจำนวนที่น้อยกว่า แต่อาจมีการเพิ่มประสิทธิภาพที่สำคัญ


8
คุณไม่มีส่วนสำคัญ มันไม่ได้เป็นเพียงการวนรอบคลี่คลาย คำสั่ง switch จะข้ามไปที่กึ่งกลางของลูป นั่นคือสิ่งที่ทำให้อุปกรณ์ดูสับสน การวนซ้ำของคุณด้านบนดำเนินการหลายสำเนา 10 ชุดเสมอ แต่ดัฟฟ์ทำตัวเลขเท่าใดก็ได้
ร็อบเคนเนดี

2
เป็นจริง - แต่ฉันพยายามทำให้คำอธิบายของ OP ง่ายขึ้น บางทีฉันอาจจะยังไม่ชัดเจนพอ! :)
James B

2

นี่คือคำอธิบายที่ไม่มีรายละเอียดซึ่งเป็นสิ่งที่ฉันรู้สึกว่าเป็นปมของอุปกรณ์ของ Duff:

สิ่งนี้คือ C เป็นพื้นดีสำหรับภาษาแอสเซมบลี (PDP-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...

ในการประกอบมันเป็นไปได้อย่างง่ายดายว่าจะรวมโครงสร้างการควบคุมทั้งสองนี้อย่างไรและเมื่อคุณคิดอย่างนั้นการรวมกันใน C จะดูไม่แปลกอีกต่อไป


1

นี่เป็นคำตอบที่ฉันโพสต์ไปยังคำถามอื่นเกี่ยวกับอุปกรณ์ของ Duff ที่มี upvaotes ก่อนที่คำถามจะปิดซ้ำ ฉันคิดว่ามันให้บริบทที่มีค่าเล็กน้อยที่นี่ทำไมคุณควรหลีกเลี่ยงการสร้างนี้

"นี่คืออุปกรณ์ของดัฟฟ์มันเป็นวิธีการวนลูปที่ไม่ต้องเพิ่มลูปการแก้ไขรองเพื่อจัดการกับเวลาที่จำนวนการวนซ้ำวนซ้ำไม่รู้ว่าเป็นปัจจัยทวีคูณแน่นอน

เนื่องจากคำตอบส่วนใหญ่ที่นี่ดูเหมือนจะเป็นบวกโดยทั่วไปเกี่ยวกับมันฉันจะเน้นข้อเสีย

ด้วยรหัสนี้คอมไพเลอร์จะพยายามใช้การปรับให้เหมาะสมกับร่างกายวน หากคุณเพิ่งเขียนโค้ดเป็นลูปธรรมดาคอมไพเลอร์สมัยใหม่ควรจะสามารถจัดการกับการคลี่คลายสำหรับคุณ วิธีนี้คุณจะรักษาความสามารถในการอ่านและประสิทธิภาพและมีความหวังในการเพิ่มประสิทธิภาพอื่น ๆ ที่นำไปใช้กับลูปเนื้อหา

บทความ Wikipedia ที่ผู้อื่นอ้างอิงอ้างถึงแม้เมื่อ 'รูปแบบ' นี้ถูกลบออกจากประสิทธิภาพของซอร์สโค้ด Xfree86 ก็ดีขึ้น

ผลลัพธ์นี้เป็นเรื่องปกติของการเพิ่มประสิทธิภาพของรหัสใด ๆ ที่คุณคิดว่าอาจจำเป็นต้องใช้ มันป้องกันคอมไพเลอร์จากการทำงานอย่างถูกต้องทำให้โค้ดของคุณอ่านน้อยลงและมีแนวโน้มที่จะเกิดข้อบกพร่องและมักจะช้าลง หากคุณกำลังทำสิ่งที่ถูกต้องตั้งแต่แรกคือการเขียนโค้ดอย่างง่ายจากนั้นทำโปรไฟล์หาคอขวดจากนั้นปรับให้เหมาะสมคุณจะไม่เคยคิดที่จะใช้สิ่งนี้ ไม่ได้มีซีพียูและคอมไพเลอร์รุ่นใหม่อยู่ดี

ไม่เป็นไรที่จะเข้าใจ แต่ฉันจะแปลกใจถ้าคุณเคยใช้มันจริง ๆ "


0

เพียงแค่การทดสอบพบว่ามีตัวแปรอื่นเข้ามาโดยไม่ต้องใช้สวิตช์และลูป

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;
}

เงื่อนไขการยกเลิกของคุณอยู่ที่ไหน
user2338150
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.