อันไหนเร็วกว่า: ในขณะที่ (1) หรือขณะที่ (2)


587

นี่เป็นคำถามสัมภาษณ์ที่ผู้จัดการอาวุโสถาม

ไหนเร็วกว่ากัน

while(1) {
    // Some code
}

หรือ

while(2) {
    //Some code
}

ผมบอกว่าทั้งสองมีความเร็วในการทำงานเดียวกันเป็นภายในแสดงออกwhileในที่สุดก็ควรประเมินหรือtrue falseในกรณีนี้ทั้งสองประเมินtrueและไม่มีคำแนะนำแบบมีเงื่อนไขพิเศษภายในwhileเงื่อนไข ดังนั้นทั้งสองจะมีความเร็วในการดำเนินการเท่ากันและฉันชอบในขณะที่ (1)

แต่ผู้สัมภาษณ์กล่าวอย่างมั่นใจว่า: "ตรวจสอบพื้นฐานของคุณwhile(1)เร็วกว่าwhile(2)" (เขาไม่ได้ทดสอบความมั่นใจของฉัน)

มันเป็นเรื่องจริงเหรอ?

ดูเพิ่มเติม: "สำหรับ (;;)" เร็วกว่า "ขณะ (TRUE)" หรือไม่ ถ้าไม่ทำไมผู้คนถึงใช้มัน?


202
คอมไพเลอร์ที่มีคุณสมบัติเหมาะสมครึ่งหนึ่งจะปรับทั้งสองแบบให้เหมาะกับสิ่งใด

64
ในการสร้างที่ปรับให้เหมาะสมทุก ๆ (n), n! = 0 หรือสำหรับ (;;) จะถูกแปลเป็น Assembly วนวนไม่รู้จบพร้อมป้ายกำกับในตอนเริ่มต้นและข้ามไปในท้าย รหัสเดียวกันประสิทธิภาพเดียวกัน
Alex F

60
ไม่น่าแปลกใจที่การเพิ่มประสิทธิภาพหุ้นนำ0x100000f90: jmp 0x100000f90(ที่อยู่แตกต่างกันอย่างเห็นได้ชัด) สำหรับทั้งเกร็ดเล็กเกร็ดน้อย ผู้สัมภาษณ์อาจป้องกันความเสี่ยงจากการทดสอบการลงทะเบียนเทียบกับการกระโดดที่ง่าย ทั้งคำถามและการคาดคะเนของพวกเขานั้นเป็นง่อย
WhozCraig

50
คำถามนี้โดยผู้สัมภาษณ์ตกอยู่ภายใต้การอุปถัมภ์เช่นเดียวกับdilbert.com/strips/comic/1995-11-17 คุณจะได้พบกับคนที่เชื่อในสิ่งที่พวกเขาพูดอย่างแท้จริงโดยไม่คำนึงถึงความฉลาดของความโง่เขลาในคำพูดของพวกเขา เพียงเลือกจากสิ่งต่อไปนี้: การข่มขู่, สาบาน, หัวเราะ, ร้องไห้, การรวมกันของบางอย่างที่กล่าวมาข้างต้น :)
GMasucci

3
@ ไมค์ W: ใครจะสงสัยว่าคอมไพเลอร์ควรทำอะไร: แปลเป็นคำสั่ง Halt หรือพิจารณาว่าลูปออกหลังจากเวลาที่ไม่สิ้นสุดและปรับการหน่วงเวลาที่ไม่มีที่สิ้นสุดให้เหมาะสม
Yves Daoust

คำตอบ:


681

ลูปทั้งสองนั้นไม่มีที่สิ้นสุด แต่เราสามารถเห็นได้ว่าอันไหนใช้คำสั่ง / ทรัพยากรเพิ่มเติมต่อการวนซ้ำ

ด้วยการใช้ gcc ฉันได้รวบรวมสองโปรแกรมต่อไปนี้เพื่อประกอบในระดับการปรับให้เหมาะสมต่าง ๆ :

int main(void) {
    while(1) {}
    return 0;
}


int main(void) {
    while(2) {}
    return 0;
}

ถึงแม้ไม่มีการเพิ่มประสิทธิภาพ ( -O0) ที่สร้างขึ้นประกอบเป็นเหมือนกันสำหรับทั้งสองโปรแกรม ดังนั้นจึงไม่มีความแตกต่างของความเร็วระหว่างสองลูป

สำหรับการอ้างอิงต่อไปนี้เป็นชุดประกอบที่สร้างขึ้น (ใช้gcc main.c -S -masm=intelกับแฟล็กการปรับให้เหมาะสม):

ด้วย-O0:

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    push    rbp
    .seh_pushreg    rbp
    mov rbp, rsp
    .seh_setframe   rbp, 0
    sub rsp, 32
    .seh_stackalloc 32
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

ด้วย-O1:

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .text
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

ด้วย-O2และ-O3(เอาต์พุตเดียวกัน):

    .file   "main.c"
    .intel_syntax noprefix
    .def    __main; .scl    2;  .type   32; .endef
    .section    .text.startup,"x"
    .p2align 4,,15
    .globl  main
    .def    main;   .scl    2;  .type   32; .endef
    .seh_proc   main
main:
    sub rsp, 40
    .seh_stackalloc 40
    .seh_endprologue
    call    __main
.L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

ในความเป็นจริงชุดประกอบที่สร้างขึ้นสำหรับลูปจะเหมือนกันสำหรับการเพิ่มประสิทธิภาพทุกระดับ:

 .L2:
    jmp .L2
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

บิตที่สำคัญคือ:

.L2:
    jmp .L2

ฉันไม่สามารถอ่านการชุมนุมได้ดี แต่เห็นได้ชัดว่านี่เป็นการวนซ้ำแบบไม่มีเงื่อนไข jmpการเรียนการสอนโดยไม่มีเงื่อนไขรีเซ็ตโปรแกรมกลับไปที่.L2ป้ายโดยไม่ได้เปรียบเทียบกับมูลค่าที่แท้จริงและแน่นอนทันทีไม่เช่นนั้นอีกครั้งจนกว่าโปรแกรมจะสิ้นสุดอย่างใด ตรงนี้สอดคล้องกับรหัส C / C ++:

L2:
    goto L2;

แก้ไข:

น่าสนใจพอถึงแม้จะไม่มีการเพิ่มประสิทธิภาพแต่ลูปต่อไปนี้ทั้งหมดสร้างเอาต์พุตเดียวกัน (โดยไม่มีเงื่อนไขjmp) ที่แน่นอนในชุดประกอบ:

while(42) {}

while(1==1) {}

while(2==2) {}

while(4<7) {}

while(3==3 && 4==4) {}

while(8-9 < 0) {}

while(4.3 * 3e4 >= 2 << 6) {}

while(-0.1 + 02) {}

และถึงความประหลาดใจของฉัน:

#include<math.h>

while(sqrt(7)) {}

while(hypot(3,4)) {}

สิ่งต่าง ๆ ที่น่าสนใจยิ่งขึ้นด้วยฟังก์ชั่นที่ผู้ใช้กำหนด:

int x(void) {
    return 1;
}

while(x()) {}


#include<math.h>

double x(void) {
    return sqrt(7);
}

while(x()) {}

ที่-O0ตัวอย่างสองตัวอย่างนี้โทรxและทำการเปรียบเทียบสำหรับการวนซ้ำแต่ละครั้ง

ตัวอย่างแรก (ส่งคืน 1):

.L4:
    call    x
    testl   %eax, %eax
    jne .L4
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

ตัวอย่างที่สอง (กลับมาsqrt(7)):

.L4:
    call    x
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jp  .L4
    xorpd   %xmm1, %xmm1
    ucomisd %xmm1, %xmm0
    jne .L4
    movl    $0, %eax
    addq    $32, %rsp
    popq    %rbp
    ret
    .seh_endproc
    .ident  "GCC: (tdm64-2) 4.8.1"

อย่างไรก็ตามที่-O1และข้างบนพวกเขาทั้งสองผลิตแอสเซมบลีเดียวกันกับตัวอย่างก่อนหน้า ( jmpกลับไม่มีเงื่อนไขกลับไปที่ป้ายชื่อก่อนหน้า)

TL; DR

ภายใต้ GCC ลูปต่าง ๆ จะถูกคอมไพล์ไปยังแอสเซมบลีที่เหมือนกัน คอมไพเลอร์ประเมินค่าคงที่และไม่รบกวนการเปรียบเทียบจริงใด ๆ

คุณธรรมของเรื่องราวคือ:

  • มีเลเยอร์การแปลอยู่ระหว่างซอร์สโค้ด C ++ และคำสั่ง CPU และเลเยอร์นี้มีนัยสำคัญต่อประสิทธิภาพ
  • ดังนั้นประสิทธิภาพไม่สามารถประเมินได้โดยดูจากซอร์สโค้ดเท่านั้น
  • คอมไพเลอร์ควรฉลาดพอที่จะเพิ่มประสิทธิภาพกรณีเล็ก ๆ น้อย ๆ โปรแกรมเมอร์ไม่ควรเสียเวลาคิดถึงพวกเขาในกรณีส่วนใหญ่

196
บางทีผู้สัมภาษณ์ไม่ได้ใช้ gcc
MM

106
@ Matt McNabb นั้นเป็นจุดที่ดี แต่ถ้าผู้สัมภาษณ์ใช้การปรับให้เหมาะสมที่สุดสำหรับคอมไพเลอร์พวกเขาจำเป็นต้องมีความชัดเจนมากเกี่ยวกับเรื่องนั้นในคำถามของพวกเขาและพวกเขาต้องยอมรับคำตอบว่า "ไม่มีความแตกต่าง" คอมไพเลอร์บางส่วน (มากที่สุด)
Jonathan Hartley

109
เพื่อทดสอบข้อสงสัยใด ๆ ฉันได้ทดสอบสิ่งนี้ใน clang 3.4.2 และลูปทั้งคู่สร้างการชุมนุมที่เหมือนกันในทุก-Oระดับ
Martin Tournoij

16
ฉันไม่พบสิ่งนี้เลยเพราะทุกสิ่งที่คุณวางไว้ในส่วนเงื่อนไขของลูปเป็นค่าคงที่เวลาคอมไพล์ เช่นนี้ฉันจะสงสัยว่าคอมไพเลอร์จะเห็นว่าลูปจะเป็นจริงหรือเท็จเสมอและjmpกลับไปที่จุดเริ่มต้นหรือลบลูปอย่างสมบูรณ์ด้วยความเคารพ
sherrellbc

10
@hippietrail ฉันไม่เห็นว่าเนื้อหาของลูปอาจมีผลต่อการเพิ่มประสิทธิภาพเหล่านี้ได้อย่างไร (ยกเว้นความเป็นไปได้ของbreakข้อความใด ๆ) แต่ฉันเพิ่งทดสอบมันต่อไปและไม่แม้ว่าโค้ดในลูปการกระโดดจะเป็นอย่างไร แน่นอนและไม่มีเงื่อนไขสำหรับทั้งสองและwhile(1) while(2)อย่าลังเลที่จะทดสอบตัวเองหากคุณเป็นกังวล
ApproachDarknessFish

282

ใช่while(1)เร็วกว่าwhile(2)มากสำหรับมนุษย์ที่จะอ่าน! ถ้าฉันเห็นwhile(1)ใน codebase ที่ไม่คุ้นเคยฉันรู้ทันทีว่าผู้เขียนตั้งใจอะไรและลูกตาของฉันสามารถไปยังบรรทัดถัดไปได้

ถ้าผมเห็นผมก็อาจจะหยุดในเพลงของฉันและพยายามที่จะคิดออกว่าทำไมผู้เขียนไม่ได้เขียนwhile(2) while(1)นิ้วของผู้เขียนลื่นบนคีย์บอร์ดหรือไม่? ผู้ดูแลฐานข้อมูลนี้ใช้while(n)เป็นกลไกการแสดงความคิดเห็นที่คลุมเครือหรือไม่ มันเป็นวิธีแก้ปัญหาอย่างหยาบสำหรับคำเตือนปลอมในเครื่องมือวิเคราะห์แบบคงที่ที่ขาดหรือไม่? หรือนี่เป็นเงื่อนงำที่ฉันอ่านรหัสที่สร้างขึ้น? มันเป็นข้อผิดพลาดที่เกิดจากการหาและแทนที่ทั้งหมดหรือการผสานที่ไม่ดีหรือรังสีคอสมิคหรือไม่? บางทีบรรทัดของรหัสนี้ควรจะทำสิ่งที่แตกต่างกันอย่างมาก บางทีมันควรจะอ่านหรือwhile(w) while(x2)ฉันควรจะหาผู้แต่งในประวัติของไฟล์และส่งอีเมล "WTF" ให้พวกเขา ... และตอนนี้ฉันเสียบริบททางจิตwhile(2)while(1) จะใช้เวลาเสี้ยววินาที!

ฉันพูดเกินจริง แต่เพียงเล็กน้อย การอ่านรหัสเป็นสิ่งสำคัญมาก และนั่นก็คุ้มค่าที่จะกล่าวถึงในการสัมภาษณ์!


ความคิดของฉันถูกต้องและทำไมอย่างแม่นยำในขณะที่ (1) เร็วขึ้นฮ่า ๆ ถึงแม้ว่าฉันคิดว่ามันเป็นเพียงแค่กรณีเก่า ๆ ของผู้จัดการที่พยายามโน้มน้าวตัวเองว่าเขารู้เกี่ยวกับการเข้ารหัสเมื่อเขาทำไม่ได้เราทุกคนเห็น เจได
ekerner

8
แน่นอนว่านี่ไม่ใช่การพูดเกินจริงเลย แน่นอนว่าจะใช้svn-annotate หรือgit blame(หรืออะไรก็ตาม) ที่นี่และโดยทั่วไปจะใช้เวลาไม่กี่นาทีในการโหลดประวัติตำหนิไฟล์ จากนั้นในที่สุดก็ตัดสินใจ "อาเข้าใจแล้วผู้เขียนเขียนบรรทัดนี้เมื่อออกจากโรงเรียนมัธยม" เพิ่งหายไป 10 นาที ...
v.oddou

11
ลงคะแนนเพราะฉันเบื่อการประชุม นักพัฒนามีโอกาสที่อ่อนแอในการเขียนตัวเลขที่เขาชอบจากนั้นมีคนที่น่าเบื่อและสนใจว่าทำไมมันถึงไม่เป็น1เช่นนั้น
Utkan Gezer

1
ลงเนื่องจากโวหาร การอ่านในขณะที่ (1) เป็นความเร็วเดียวกับในขณะที่ (2)
hdante

4
ฉันได้ผ่านกระบวนการทางจิตที่แน่นอนตามที่อธิบายไว้ในคำตอบนี้เมื่อหนึ่งนาทีที่ผ่านมาเมื่อฉันเห็นwhile(2)ในโค้ด (ลงมาเพื่อพิจารณา "ผู้ดูแลฐานข้อมูลนี้ใช้หรือไม่ในขณะที่ (n) เป็นกลไกการแสดงความคิดเห็นที่คลุมเครือ ) ไม่เลยคุณไม่ได้พูดเกินจริง!
Hisham HM

151

คำตอบที่มีอยู่แสดงรหัสที่สร้างขึ้นโดยคอมไพเลอร์เฉพาะสำหรับเป้าหมายเฉพาะที่มีชุดตัวเลือกเฉพาะไม่ตอบคำถามอย่างเต็มที่ - หากคำถามถูกถามในบริบทเฉพาะนั้น ("ซึ่งเร็วกว่าการใช้ gcc 4.7.2 สำหรับ x86_64 ด้วยตัวเลือกเริ่มต้น? "ตัวอย่างเช่น)

เท่าที่คำนิยามภาษาเกี่ยวข้องในเครื่องนามธรรม while (1)ประเมินค่าคงที่จำนวนเต็ม1และwhile (2)ประเมินค่าคงที่จำนวนเต็ม2; ในทั้งสองกรณีผลลัพธ์จะถูกเปรียบเทียบเพื่อความเท่าเทียมกับศูนย์ มาตรฐานภาษาไม่ได้พูดอะไรเลยเกี่ยวกับประสิทธิภาพที่สัมพันธ์กันของโครงสร้างทั้งสอง

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

ในขณะที่คอมไพเลอร์ C ต้องประเมินนิพจน์คงที่บางอย่างในเวลารวบรวมเมื่อปรากฏในบริบทที่ต้องการนิพจน์คงที่ ตัวอย่างเช่นสิ่งนี้:

int n = 4;
switch (n) {
    case 2+2: break;
    case 4:   break;
}

ต้องมีการวินิจฉัย; lazy คอมไพเลอร์ไม่มีตัวเลือกในการชะลอการประเมินผล2+2จนถึงเวลาดำเนินการ เนื่องจากคอมไพเลอร์ต้องมีความสามารถในการประเมินนิพจน์คงที่ในเวลารวบรวมจึงไม่มีเหตุผลที่ดีที่จะไม่ใช้ประโยชน์จากความสามารถนั้นแม้ว่าจะไม่จำเป็นก็ตาม

มาตรฐาน C ( N1570 6.8.5p4) บอกว่า

คำสั่งวนซ้ำทำให้คำสั่งที่เรียกว่าวนลูปจะถูกดำเนินการซ้ำ ๆ จนกระทั่งนิพจน์การควบคุมเปรียบเทียบเท่ากับ 0

ดังนั้นการแสดงออกอย่างต่อเนื่องที่เกี่ยวข้องอยู่1 == 0และ2 == 0ทั้งที่ประเมินกับค่าint 0(การเปรียบเทียบเหล่านี้มีความหมายในความหมายของwhileลูปซึ่งไม่มีอยู่ในนิพจน์ C จริง)

คอมไพเลอร์ไร้เดียงสาอย่างวิปริตสามารถสร้างรหัสที่แตกต่างกันสำหรับการสร้างทั้งสอง ตัวอย่างเช่นสำหรับครั้งแรกที่มันสามารถสร้างวง จำกัด ที่ไม่มีเงื่อนไข (การรักษา1เป็นกรณีพิเศษ) 2 != 0และเป็นครั้งที่สองที่สามารถสร้างการเปรียบเทียบเทียบเท่าอย่างชัดเจนเวลาทำงานไป แต่ฉันไม่เคยเจอคอมไพเลอร์ C ที่จะทำตัวเป็นอย่างนั้นและฉันก็สงสัยว่าคอมไพเลอร์ตัวนั้นมีอยู่จริง

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

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

Bottom line: while (1)และwhile(2) เกือบจะแน่นอนมีประสิทธิภาพเดียวกัน พวกเขามีความหมายเดียวกันและไม่มีเหตุผลที่ดีสำหรับผู้รวบรวมที่จะไม่สร้างรหัสที่เหมือนกัน

และถึงแม้ว่ามันจะถูกกฎหมายอย่างสมบูรณ์แบบสำหรับคอมไพเลอร์ในการสร้างโค้ดที่เร็วwhile(1)กว่าสำหรับwhile(2)แต่ก็เป็นเรื่องถูกกฎหมายสำหรับคอมไพเลอร์ในการสร้างโค้ดที่เร็วwhile(1)กว่าสำหรับกว่าที่จะเกิดขึ้นอีกครั้งwhile(1)ในโปรแกรมเดียวกัน

(มีคำถามอีกข้อหนึ่งที่คุณถามไว้: คุณจัดการกับผู้สัมภาษณ์ที่ยืนยันในประเด็นทางเทคนิคที่ไม่ถูกต้องได้อย่างไรซึ่งอาจเป็นคำถามที่ดีสำหรับไซต์สถานที่ทำงาน )


8
"ในกรณีนี้นิพจน์คงที่ที่เกี่ยวข้อง (โดยนัย) คือ 1! = 0 และ 2! = 0 ซึ่งทั้งคู่ประเมินเป็นค่า int 1" ... นี่ซับซ้อนเกินไปและไม่ถูกต้อง มาตรฐานบอกเพียงว่าการควบคุมการแสดงออกwhileต้องเป็นแบบสเกลาร์และลำตัววนซ้ำจนกว่านิพจน์จะเปรียบเทียบเท่ากับ 0 ไม่ได้บอกว่ามีนัยโดยนัยexpr != 0ที่ประเมิน ... ที่จะต้องใช้ผลลัพธ์ของ ที่ - 0 หรือ 1 - ในทางกลับกันเมื่อเทียบกับ 0, infinitum ad ไม่นิพจน์ถูกเปรียบเทียบกับ 0 แต่การเปรียบเทียบนั้นไม่ได้สร้างมูลค่า ป.ล. ฉันโหวตขึ้น
Jim Balter

3
@JimBalter: ฉันเห็นจุดของคุณและฉันจะอัปเดตคำตอบของฉันเพื่อแก้ไข สิ่งที่ฉันหมายถึงคือการที่ถ้อยคำมาตรฐาน "... จนกระทั่งการแสดงออกของการควบคุมเปรียบเทียบเท่ากับ 0" หมายถึงการประเมิน<expr> == 0; นั่นคือสิ่งที่ "เปรียบเทียบเท่ากับ 0" หมายถึงใน C การเปรียบเทียบนั้นเป็นส่วนหนึ่งของความหมายของwhileลูป ไม่มีความหมายทั้งในมาตรฐานหรือในคำตอบของฉันว่าผลลัพธ์จะต้องนำมาเปรียบเทียบ0อีกครั้ง (ฉันควรจะเขียน==มากกว่า!=) แต่คำตอบของฉันส่วนนั้นไม่ชัดเจนและฉันจะอัปเดต
Keith Thompson

1
"" เปรียบเทียบเท่ากับ 0 "หมายถึงใน C" - แต่ภาษานั้นอยู่ในมาตรฐานไม่ใช่ใน C ... การใช้งานนั้นทำการเปรียบเทียบไม่ได้สร้างการเปรียบเทียบภาษา C คุณเขียน "ทั้งสองซึ่งประเมินเป็นค่า int 1" - แต่ไม่มีการประเมินดังกล่าวเกิดขึ้น ถ้าคุณดูที่โค้ดที่สร้างสำหรับwhile (2), while (pointer), while (9.67)โดยไร้เดียงสาที่สุดคอมไพเลอร์ไม่ได้เพิ่มประสิทธิภาพ, คุณจะไม่เห็นรหัสใด ๆ ที่สร้างขึ้นที่อัตราผลตอบแทนหรือ0 1"ฉันควรจะเขียน == มากกว่า! =" - ไม่นั่นจะไม่สมเหตุสมผลเลย
Jim Balter

2
@JimBalter: อืมม ฉันไม่ได้ตั้งใจจะแนะนำว่า "เปรียบเทียบเท่ากับ 0" แสดงถึงการมีอยู่ของ... == 0นิพจน์ C จุดของฉันคือทั้ง "เปรียบเทียบเท่ากับ 0" ที่ต้องการโดยคำอธิบายของมาตรฐานของwhileลูปและการx == 0แสดงออกที่ชัดเจนมีเหตุผลการดำเนินการเดียวกัน และฉันคิดว่าคอมไพเลอร์ไร้เดียงสาอย่างเจ็บปวดอาจสร้างโค้ดที่สร้างintมูลค่า0หรือ1สำหรับwhileลูปใด ๆ- แม้ว่าฉันไม่เชื่อว่าคอมไพเลอร์จริง ๆ นั้นค่อนข้างไร้เดียงสา
Keith Thompson

12
หมายเหตุ: มีอยู่แล้วที่ทำงานคำถาม: workplace.stackexchange.com/questions/4314/...
โจ

141

รอสักครู่ ผู้สัมภาษณ์เขาดูเหมือนผู้ชายคนนี้หรือไม่?

ป้อนคำอธิบายรูปภาพที่นี่

มันแย่มากที่ผู้สัมภาษณ์ตัวเองล้มเหลวในการสัมภาษณ์ครั้งนี้จะเกิดอะไรขึ้นถ้าโปรแกรมเมอร์คนอื่นใน บริษัท นี้ "ผ่าน" การทดสอบนี้?

ไม่การประเมินข้อความ1 == 0และ2 == 0 ควรเร็วพอ ๆ กัน เราสามารถจินตนาการการใช้งานคอมไพเลอร์ที่ไม่ดีซึ่งอาจเร็วกว่าอีกอัน แต่ไม่มีเหตุผลที่ดีว่าทำไมจึงควรเร็วกว่าอีกอัน

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

ข้อสงวนสิทธิ์:นี่ไม่ใช่การ์ตูนต้นฉบับของ Dilbert นี้เป็นเพียงการตอบโต้กับผู้ใช้ได้


6
นี้จะเป็นตลกหากมีการนอกจากนี้ยังมีคำตอบสำหรับคำถาม
Cody Gray

ไม่ แต่จริงๆแล้วเราทุกคนสามารถจินตนาการได้อย่างง่ายดายพอสมควรว่าคอมไพเลอร์ทั้งหมดที่เขียนโดย บริษัท ที่จริงจังจะผลิตรหัสที่สมเหตุสมผล ลองใช้ "กรณีที่ไม่ได้รับการปรับให้เหมาะสม" / O0 บางทีมันอาจจะจบลงด้วยการโพสต์ข้อความ จากนั้นเป็นคำถามของ CPU cmpตัวถูกดำเนินการจะทำงานในรอบที่น้อยลงเมื่อเปรียบเทียบกับ 1 ถึง 0 มากกว่า 2 ถึง 0 หรือไม่ ใช้เวลากี่รอบในการประมวลผล cmp โดยทั่วไป? มันเป็นตัวแปรตามรูปแบบบิต? พวกเขามีรูปแบบบิต "ซับซ้อน" ที่ช้าลงcmpหรือไม่ ฉันไม่รู้จักเป็นการส่วนตัว คุณสามารถจินตนาการการตรวจสอบการใช้งานที่งี่เง่าสุด ๆ ทีละน้อยจากอันดับ 0 ถึง n (เช่น n = 31)
v.oddou

5
นั่นคือประเด็นของฉันเช่นกันcmpตัวถูกดำเนินการควรจะเร็วพอ ๆ กันสำหรับ 1 และ 200 บางทีเราอาจจินตนาการถึงการใช้งานที่งี่เง่าในกรณีที่ไม่เป็นเช่นนั้น แต่เราสามารถจินตนาการถึงการนำไปปฏิบัติที่ไม่งี่เง่าwhile(1)ได้เร็วกว่าwhile(200)ที่ไหน ในทำนองเดียวกันถ้าในยุคก่อนประวัติศาสตร์การใช้งานอย่างเดียวที่เป็นไปได้นั้นเป็นเรื่องงี่เง่าแบบนั้นเราควรเอะอะในวันนี้ไหม? ฉันไม่คิดอย่างนั้นนี่คือการพูดคุยกับเจ้านายที่มีผมแหลมและอัญมณีที่แท้จริง!
janos

@ v.ouddou "ตัวดำเนินการ cmp จะทำงานในรอบที่น้อยกว่าเมื่อเปรียบเทียบกับ 1 ถึง 0 มากกว่า 2 ถึง 0" - ไม่คุณควรเรียนรู้ว่ารอบการทำงานคืออะไร "ฉันไม่รู้ว่าเป็นการส่วนตัวคุณสามารถจินตนาการการใช้งานงี่เง่าการตรวจสอบบิตจากอันดับ 0 ถึง n" - หรือวิธีอื่น ๆ ยังคงทำให้ผู้สัมภาษณ์เป็นคนงี่เง่า clueless และทำไมต้องกังวลเกี่ยวกับการตรวจสอบทีละนิด? การติดตั้งอาจเป็นผู้ชายคนหนึ่งในกล่องที่ตัดสินใจหยุดพักกลางวันระหว่างการประเมินโปรแกรมของคุณ
Jim Balter

79

คำอธิบายของคุณถูกต้อง นี่เป็นคำถามที่ทดสอบความมั่นใจในตนเองของคุณนอกเหนือจากความรู้ด้านเทคนิค

โดยวิธีการถ้าคุณตอบ

โค้ดทั้งสองนั้นเร็วพอ ๆ กันเพราะทั้งคู่ใช้เวลาไม่สิ้นสุด

ผู้สัมภาษณ์จะบอกว่า

แต่while (1)สามารถทำซ้ำได้มากขึ้นต่อวินาที คุณอธิบายได้ไหม (นี่เป็นเรื่องไร้สาระ; ทดสอบความมั่นใจของคุณอีกครั้ง)

ดังนั้นเมื่อตอบกลับอย่างที่คุณทำคุณประหยัดเวลาที่คุณจะเสียเวลาพูดคุยคำถามที่ไม่ดีนี้


นี่คือตัวอย่างรหัสที่สร้างโดยคอมไพเลอร์ในระบบของฉัน (MS Visual Studio 2012) โดยปิดการปรับให้เหมาะสม:

yyy:
    xor eax, eax
    cmp eax, 1     (or 2, depending on your code)
    je xxx
    jmp yyy
xxx:
    ...

เมื่อเปิดการเพิ่มประสิทธิภาพ:

xxx:
    jmp xxx

ดังนั้นรหัสที่สร้างขึ้นจึงเหมือนกันทุกประการอย่างน้อยที่สุดเมื่อคอมไพเลอร์ปรับให้เหมาะสม


28
รหัสนี้เป็นสิ่งที่คอมไพเลอร์ส่งออกในระบบของฉัน ฉันไม่ได้แต่งหน้า
Anatolyg

10
icepack "ตัวถูกดำเนินการในขณะที่อยู่ในประเภทบูลีน" - เรื่องไร้สาระที่สุด คุณเป็นผู้สัมภาษณ์ใช่ไหม ฉันขอแนะนำให้คุณคุ้นเคยกับภาษา C และมาตรฐานของมันก่อนที่จะทำการอ้างสิทธิ์ดังกล่าว
Jim Balter

29
"ฉันไม่ได้ทำมันขึ้นมา" - โปรดอย่าใส่ใจกับ icepack ผู้พูดไร้สาระ C ไม่มีประเภทบูลีน (มันมี _Bool ใน stdbool.h แต่มันไม่เหมือนกันและความหมายของคำนำwhileหน้านั้นมีความยาวมาก่อน) และตัวถูกดำเนินการwhileไม่ได้เป็นบูลีนหรือ _Bool หรือประเภทเฉพาะอื่น ๆ ตัวถูกดำเนินการของwhileสามารถเป็นนิพจน์ใด ๆ ... ในขณะที่หยุดพักที่ 0 และดำเนินการต่อที่ไม่ใช่ 0
Jim Balter

36
"โค้ดทั้งสองชิ้นนั้นเร็วพอ ๆ กันเพราะทั้งคู่ใช้เวลาในการทำให้สมบูรณ์" ทำให้ฉันคิดถึงสิ่งที่น่าสนใจ วิธีเดียวที่จะยุติวง จำกัด จะเป็นบิตที่จะพลิกโดยอนุภาคหรือฮาร์ดแวร์ล้มเหลว: การเปลี่ยนแปลงคำสั่งจากไปwhile (00000001) {} while (00000000) {}ยิ่งบิตที่แท้จริงมากขึ้นคุณจะมีโอกาสน้อยที่ค่าจะพลิกเป็นเท็จ น่าเศร้าที่ 2 มีเพียงบิตเดียวเท่านั้น 3 อย่างไรก็ตามจะทำงานได้นานขึ้นอย่างมีนัยสำคัญ นอกจากนี้ยังนำไปใช้กับคอมไพเลอร์ซึ่งไม่ได้ปรับให้เหมาะกับสิ่งนี้เสมอ (VC ++)
Jonathan Dickinson

8
@ mr5 ไม่ เพื่อให้ได้ผลลัพธ์ที่เป็นจริงเช่นนี้คุณกำลังพูดถึงเวลาดำเนินการในหมื่นปี เป็นการทดลองทางความคิดเท่านั้น หากคุณทักทายจากการแข่งขันอมตะคุณอาจต้องการใช้ -1 เพื่อป้องกันการพลิกบิตจากโปรแกรมของคุณ
Jonathan Dickinson

60

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

1 = 00000001
2 = 00000010

หาก "เป็นศูนย์" อัลกอริทึมเริ่มต้นจากด้านขวาของตัวเลขและต้องตรวจสอบแต่ละบิตจนกว่าจะถึงบิตที่ไม่เป็นศูนย์while(1) { }ลูปจะต้องตรวจสอบสองเท่าของบิตต่อการวนซ้ำเป็นwhile(2) { }ลูป

สิ่งนี้ต้องใช้แบบจำลองทางจิตที่ผิดมากในการทำงานของคอมพิวเตอร์ แต่มันมีตรรกะภายในของตัวเอง วิธีหนึ่งในการตรวจสอบจะถามว่าwhile(-1) { }หรือwhile(3) { }จะเป็นอย่างเท่าเทียมกันอย่างรวดเร็วหรือถ้าwhile(32) { }จะ ได้ช้า


39
ฉันสันนิษฐานว่าความเข้าใจผิดของผู้สัมภาษณ์จะเป็นเหมือน "2 คือ int ที่ต้องเปลี่ยนเป็นบูลีนเพื่อใช้ในการแสดงออกตามเงื่อนไขขณะที่ 1 เป็นบูลีนอยู่แล้ว"
Russell Borogove

7
และถ้าอัลกอริทึมการเปรียบเทียบเริ่มต้นจากด้านซ้ายมันเป็นวิธีอื่น ๆ
Paŭlo Ebermann

1
+1 นั่นคือสิ่งที่ฉันคิด คุณสามารถจินตนาการได้อย่างสมบูรณ์แบบว่ามีใครบางคนที่เชื่อว่าพื้นฐานของcmpอัลกอริทึมของ CPU คือการตรวจสอบบิตเชิงเส้นที่มีการออกจากลูปก่อนในความแตกต่างแรก
v.oddou

1
@PeterCordes "ฉันไม่เคยได้ยินใครเลย (นอกเหนือจากคุณ) ให้เหตุผลว่าการ-O0ส่งออกของ gcc หรือเสียงดังกราวนั้นไม่เพียงพอจริง ๆ " ฉันไม่เคยพูดเรื่องแบบนี้และฉันไม่ได้มี gcc หรือเสียงดังกราวในใจเลย คุณต้องเข้าใจฉันผิด ฉันแค่ไม่เห็นความคิดเห็นของคุณว่าสิ่งที่ MSVC สร้างนั้นเป็นเฮฮา
blubberdiblub

1
@ PeterCordes ฉันผิดใน MSVC เบรกพอยต์บนwhile(1)ไม่ทำลายก่อนวง แต่ในการแสดงออก แต่มันยังคงยุบตัวอยู่ในลูปเมื่อปรับการแสดงออกให้เหมาะสม ใช้รหัสนี้กับตัวแบ่งจำนวนมาก ในโหมดการแก้จุดบกพร่องไม่ได้เพิ่มประสิทธิภาพให้คุณได้รับนี้ เมื่อทำการออปติไมซ์เบรกพอยต์หลาย ๆ ตัวจะตกลงมารวมกันหรือแม้แต่ทะลักเข้าสู่ฟังก์ชั่นถัดไป (IDE จะแสดงเบรกพอยต์ผลลัพธ์ขณะทำการดีบั๊ก) - การถอดแยกที่สอดคล้องกันที่สอดคล้องกันถอดชิ้นส่วน
blubberdiblub

32

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

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

แต่แน่นอนฉันไม่รู้ฉันเพิ่งเสนอตัวเลือกเดียว


มันยอดเยี่ยมจริง ๆ แต่ในขณะที่ (2) vs ในขณะที่ (1) ถูกเอามาจากการ์ตูน dilbert อย่างชัดเจน มันไม่สามารถถูกประดิษฐ์ขึ้นมาโดยคนที่อยู่ในใจที่ถูกต้องของเขา (ใครจะเกิดขึ้นในขณะที่ (2) เป็นสิ่งที่เป็นไปได้ที่จะเขียนต่อไป?) หากสมมติฐานของคุณเป็นจริงแน่นอนคุณจะให้ปัญหาที่ไม่ซ้ำกันเพื่อให้คุณสามารถ google สำหรับมัน เช่นเดียวกับ "ในขณะที่ (0xf00b442) ช้ากว่าขณะที่ (1)" ธนาคารจะค้นหาคำถามของ inerviewee ได้อย่างไร? คุณคิดว่าพวกเขาเป็น NSA และมีสิทธิ์เข้าถึงที่เก็บคีย์หรือไม่
v.oddou

25

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

ในเวลานั้นโปรแกรมเมอร์ใช้เวลาพอสมควรในการลองใช้วิธีการต่าง ๆ เพื่อทำให้โค้ดของตนเร็วขึ้นและมีประสิทธิภาพมากขึ้นเนื่องจากเวลา CPU ของ 'minicomputer กลาง' มีค่ามาก ผู้คนเขียนคอมไพเลอร์ ฉันคาดเดาว่าคอมไพเลอร์หนึ่งเดียวเท่านั้นที่ บริษัท ของเขาจัดทำในเวลานั้นได้รับการปรับให้เหมาะสมบนพื้นฐานของ 'งบที่พบบ่อยที่สามารถปรับให้เหมาะสม' และใช้ทางลัดเล็กน้อยเมื่อพบกับ (1) และประเมินทุกอย่าง อื่นรวมถึงขณะที่ (2) การมีประสบการณ์ดังกล่าวสามารถอธิบายตำแหน่งของเขาและความมั่นใจในมันได้

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


19

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


14
@GKFX หากคุณได้รับคำตอบจากคุณและพวกเขาบอกคุณว่าคุณผิดมีเหตุผลที่คุณไม่สามารถขอให้พวกเขาอธิบายว่าทำไม หาก Anatolyg นั้นถูกต้องและเป็นการทดสอบความมั่นใจในตนเองของคุณคุณควรจะอธิบายว่าทำไมคุณตอบแบบที่คุณทำและถามพวกเขาแบบเดียวกัน
P.Turpie

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

18

เพื่อประโยชน์ของคำถามนี้ฉันควรที่จะเพิ่มฉันจำได้ว่า Doug Gwyn จากคณะกรรมการ C เขียนว่าคอมไพเลอร์ต้นซีที่ไม่มีการเพิ่มประสิทธิภาพผ่านจะสร้างการทดสอบในการชุมนุมสำหรับwhile(1)(เปรียบเทียบกับfor(;;)ที่ไม่มีมัน)

ฉันจะตอบผู้สัมภาษณ์ด้วยการให้บันทึกทางประวัติศาสตร์นี้แล้วบอกว่าแม้ว่าฉันจะประหลาดใจมากที่คอมไพเลอร์ทำสิ่งนี้ผู้เรียบเรียงอาจมี:

  • โดยไม่มีเครื่องมือเพิ่มประสิทธิภาพคอมไพเลอร์สร้างการทดสอบสำหรับทั้งสอง while(1)และwhile(2)
  • ด้วยการเพิ่มประสิทธิภาพผ่านคอมไพเลอร์ได้รับคำสั่งให้เพิ่มประสิทธิภาพ (ด้วยการกระโดดที่ไม่มีเงื่อนไข) ทั้งหมดwhile(1)เพราะพวกเขาถือว่าเป็นสำนวน สิ่งนี้จะปล่อยให้การwhile(2)ทดสอบเกิดขึ้นและทำให้เกิดความแตกต่างระหว่างประสิทธิภาพทั้งสอง

แน่นอนฉันจะเพิ่มผู้สัมภาษณ์ที่ไม่ได้พิจารณาwhile(1)และwhile(2)โครงสร้างเดียวกันเป็นสัญญาณของการเพิ่มประสิทธิภาพที่มีคุณภาพต่ำเนื่องจากสิ่งเหล่านี้เป็นโครงสร้างที่เท่าเทียมกัน


10

คำถามอีกข้อหนึ่งก็คือดูว่าคุณกล้าที่จะบอกผู้จัดการของคุณว่าเขา / เธอผิดหรือเปล่า! และคุณสามารถสื่อสารได้อย่างนุ่มนวลเพียงใด

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


8

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

คำตอบของฉันจะเป็น; มันไม่สำคัญว่าฉันจะมุ่งเน้นไปที่ปัญหาทางธุรกิจที่เรากำลังแก้ไขอยู่ ท้ายที่สุดนั่นคือสิ่งที่ฉันจะได้รับการจ่ายเงิน

นอกจากนี้ฉันจะเลือกใช้ while(1) {}เพราะมันเป็นเรื่องธรรมดามากขึ้นและเพื่อนร่วมทีมคนอื่น ๆ ก็ไม่จำเป็นต้องใช้เวลาคิดว่าทำไมใครบางคนถึงมีจำนวนสูงกว่า 1

ตอนนี้ไปเขียนรหัส ;-)


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

6

หากคุณกังวลเกี่ยวกับการปรับให้เหมาะสมคุณควรใช้

for (;;)

เพราะนั่นไม่มีการทดสอบ (โหมดถากถาง)


2
นั่นเป็นเหตุผลว่าทำไมจึงควรใช้อย่างชาญฉลาดwhile(2)เพราะจะเหลือพื้นที่สำหรับปรับให้เหมาะสมจากนั้นคุณเพียงสลับไปใช้for (;;)เมื่อคุณพร้อมที่จะเพิ่มประสิทธิภาพ
Groo

5

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

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


3

ฉันเคยโปรแกรม C และรหัสแอสเซมบลีกลับมาเมื่อเรื่องไร้สาระเช่นนี้อาจสร้างความแตกต่าง เมื่อมันสร้างความแตกต่างเราเขียนมันในสภา

ถ้าฉันถูกถามคำถามนั้นฉันจะทำซ้ำอ้างถึงชื่อเสียงของ Donald Knuth 1974 เกี่ยวกับการเพิ่มประสิทธิภาพก่อนวัยอันควรและเดินถ้าผู้สัมภาษณ์ไม่หัวเราะและไปต่อ


1
ให้เขายังกล่าวว่า "ในสาขาวิชาวิศวกรรมที่จัดตั้งขึ้นมีการปรับปรุง 12%, ได้รับง่ายไม่เคยถูกมองว่าเป็นขอบและฉันเชื่อว่ามุมมองเดียวกันควรเหนือกว่าในด้านวิศวกรรมซอฟต์แวร์" ฉันคิดว่าคุณเดินไม่ยุติธรรม
อลิซ

3

บางทีผู้สัมภาษณ์ตั้งคำถามโง่ ๆ อย่างตั้งใจและต้องการให้คุณทำ 3 คะแนน:

  1. การใช้เหตุผลขั้นพื้นฐานลูปทั้งคู่ไม่มีที่สิ้นสุดมันยากที่จะพูดคุยเกี่ยวกับการแสดง
  2. ความรู้เกี่ยวกับระดับการปรับให้เหมาะสม เขาต้องการได้ยินจากคุณถ้าคุณให้คอมไพเลอร์ทำการปรับให้เหมาะสมสำหรับคุณมันจะปรับสภาพโดยเฉพาะอย่างยิ่งถ้าบล็อกนั้นไม่ว่างเปล่า
  3. ความรู้เกี่ยวกับสถาปัตยกรรมไมโครโปรเซสเซอร์ สถาปัตยกรรมส่วนใหญ่มีคำสั่ง CPU พิเศษสำหรับการเปรียบเทียบกับ 0 (ในขณะที่ไม่จำเป็นต้องเร็วกว่า)

2

นี่คือปัญหา: ถ้าคุณเขียนโปรแกรมและวัดความเร็วความเร็วของลูปทั้งสองอาจแตกต่างกัน! สำหรับการเปรียบเทียบที่สมเหตุสมผล:

unsigned long i = 0;
while (1) { if (++i == 1000000000) break; }

unsigned long i = 0;
while (2) { if (++i == 1000000000) break; }

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

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


1
เหตุผล "เส้นของแคช" จะใช้ได้กับชิปที่มีแคชขนาดเล็กมากเท่านั้น
sharptooth

10
นี่คือด้านข้างจุด คำถามเกี่ยวกับความเร็วถือว่า "เป็นอย่างอื่นที่เท่าเทียมกัน"
Jim Balter

6
@JimBalter: นี่ไม่ใช่คำถามของความเร็วมันเป็นคำถามเกี่ยวกับการโต้เถียงกับผู้สัมภาษณ์ และความเป็นไปได้ที่การทำแบบทดสอบจริงอาจพิสูจน์ผู้สัมภาษณ์ว่า "ถูกต้อง" - ด้วยเหตุผลที่ไม่มีอะไรเกี่ยวข้องกับข้อโต้แย้งของเขา แต่เป็นเรื่องบังเอิญ ซึ่งจะทำให้คุณตกอยู่ในสถานการณ์ลำบากใจหากคุณพยายามพิสูจน์ว่าเขาผิด
gnasher729

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

4
@ gnasher729 "นี่ไม่ใช่คำถามของความเร็ว" - ฉันจะไม่อภิปรายคนที่ใช้เทคนิคเชิงโวหารที่ไม่สุจริต
Jim Balter

2

พวกเขาทั้งสองเท่ากัน - เหมือนกัน

ตามข้อมูลจำเพาะอะไรก็ตามที่ไม่ใช่ 0 ถือว่าเป็นจริงดังนั้นแม้จะไม่มีการเพิ่มประสิทธิภาพใด ๆ และคอมไพเลอร์ที่ดีจะไม่สร้างรหัสใด ๆ ในขณะที่ (1) หรือขณะที่ (2) != 0คอมไพเลอร์จะสร้างการตรวจสอบง่ายสำหรับ


@djechlin - เพราะทั้งคู่ใช้ cpu 1 รอบ
ลุง

คอมไพเลอร์คงที่พับมันดังนั้นจึงไม่ได้ประเมินแม้ในเวลาทำงาน
PaulHK

2

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

และเพื่อใช้เวลากับมันให้มากขึ้น ...

"ในขณะที่ (2)" ไร้สาระเพราะ

"while (1)" และ "while (true)" ใช้เพื่อสร้างวงที่ไม่มีที่สิ้นสุดซึ่งคาดว่าจะมีการเรียก "break" ในบางช่วงของวงตามเงื่อนไขที่จะเกิดขึ้นอย่างแน่นอน

"1" นั้นอยู่ที่นั่นเสมอเพื่อประเมินความจริงเสมอดังนั้นการพูดว่า "ในขณะที่ (2)" เป็นเรื่องโง่เหมือนพูดว่า "ในขณะที่ (1 + 1 == 2)" ซึ่งจะประเมินเป็นจริงเช่นกัน

และถ้าคุณต้องการที่จะโง่อย่างสมบูรณ์เพียงแค่ใช้: -

while (1 + 5 - 2 - (1 * 3) == 0.5 - 4 + ((9 * 2) / 4)) {
    if (succeed())
        break;
}

รหัสต้องการคิดว่า coder ของคุณทำผิดที่ไม่ส่งผลต่อการทำงานของรหัส แต่ถ้าเขาตั้งใจใช้ "2" เพียงเพื่อจะแปลกแล้วก็ไล่ออกเขาก่อนที่เขาจะใส่ sh! t ทั้งหมดผ่านรหัสของคุณทำให้มันยากที่จะ อ่านและทำงานกับ


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

@Palec: ขอบคุณสำหรับความคิดเห็น ฉันพบว่าผู้ชายคนเดียวกันแก้ไขโพสต์ของฉันมากมายส่วนใหญ่จะลบเครื่องหมายออกเท่านั้น ดังนั้นฉันคิดว่าเขาเป็นเพียงโทรลล์ที่มีชื่อเสียงและด้วยเหตุนี้ชื่อเสียงของเขา ฟอรัมออนไลน์หมดภาษา - และความเอื้อเฟื้อทั่วไป - มากจนเราไม่ได้ลงชื่อในงานเขียนของเราอีกต่อไปหรือไม่? บางทีฉันอาจเป็นโรงเรียนเก่าเล็ก ๆ แต่มันก็ดูหยาบคายที่จะละเว้น Hellos และลา เรากำลังใช้แอพ แต่เรายังเป็นมนุษย์อยู่
ekerner

1
บนกองแลกเปลี่ยน , คำนำคำโปรยขอบคุณ ฯลฯ ไม่ต้อนรับ เช่นเดียวกับที่กล่าวถึงในศูนย์ช่วยเหลือโดยเฉพาะภายใต้พฤติกรรมที่คาดหวัง ไม่มีส่วนใดของStack Exchange ที่ไม่ใช่แม้แต่Stack Overflowเป็นฟอรัม - เป็นไซต์ถาม - ตอบ การแก้ไขเป็นหนึ่งในความแตกต่าง ความคิดเห็นควรทำหน้าที่เพียงเพื่อแสดงความคิดเห็นเกี่ยวกับโพสต์ไม่ใช่เพื่อการสนทนา ( Stack Overflow Chat ) และยังมีอีกหลายวิธีที่ถาม & ตอบต่างจากฟอรัม เพิ่มเติมเกี่ยวกับที่อยู่ในศูนย์ช่วยเหลือและMeta กองมากเกิน
Palec

โปรดทราบว่าเมื่อคุณมีตัวแทนมากกว่า 2000 คน (แก้ไขสิทธิ์) คุณจะไม่ได้รับการแก้ไขอีก เมื่อสงสัยว่าทำไมและแก้ไขคุณไม่เห็นด้วยกับที่ได้รับนำไปใช้กับการโพสต์ของคุณหรือคุณเห็นพฤติกรรมที่ไม่เหมาะสมของใครบางคนถามในMeta กองมากเกิน หากบรรณาธิการทำสิ่งผิดพลาดพวกเขาอาจได้รับแจ้งจาก mod (บางทีพวกเขาอาจไม่รู้) หรือแม้แต่ถูกลงโทษ (ถ้าพฤติกรรมนั้นเป็นอันตรายโดยเจตนา) มิฉะนั้นคุณจะได้รับพอยน์เตอร์เพื่ออธิบายว่าทำไมการแก้ไข / พฤติกรรมถูกต้อง กลุ่มย้อนกลับที่ไม่จำเป็นต้องมีประวัติการแก้ไขและสามารถนำคุณเข้าสู่สงครามย้อนกลับได้ ฉันย้อนกลับเฉพาะเมื่อแน่ใจจริงๆว่าการแก้ไขนั้นไม่ถูกต้อง
Palec

ในที่สุดเกี่ยวกับการลงชื่อทักทาย ... : มันอาจดูหยาบคายที่จะไม่รวมพิธีการเหล่านี้ แต่มันเพิ่มอัตราส่วนสัญญาณต่อเสียงรบกวนและไม่มีข้อมูลสูญหาย คุณสามารถเลือกชื่อเล่นที่คุณต้องการได้นั่นคือลายเซ็นของคุณ ในสถานที่ส่วนใหญ่คุณมีสัญลักษณ์ของคุณด้วย
Palec

1

ขึ้นอยู่กับคอมไพเลอร์

หากมันปรับรหัสให้เหมาะสมหรือหากประเมินค่า 1 และ 2 เป็นจริงด้วยจำนวนคำสั่งเท่ากันสำหรับชุดคำสั่งเฉพาะความเร็วในการเรียกใช้จะเท่ากัน

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

ฉันหมายถึง: นี่ไม่ใช่คำถามที่เกี่ยวข้องกับภาษา (C)


0

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

ระมัดระวัง: คุณต้องแน่ใจว่าลูปจะทำงานอย่างน้อยหนึ่งครั้งเสมอเสมอ

การวนซ้ำควรมีเงื่อนไขการแบ่งด้านใน

นอกจากนี้สำหรับประเภทของวงที่ฉันชอบส่วนตัวใช้ {} ขณะที่ (42) ตั้งแต่จำนวนเต็มใด ๆ ยกเว้น 0 จะทำงาน


ทำไมเขาถึงแนะนำให้ทำ {} ขณะที่วนซ้ำ ในขณะที่ (1) (หรือขณะที่ (2)) ทำสิ่งเดียวกัน
Catsunami

ทำในขณะที่ลบกระโดดพิเศษหนึ่งครั้งเพื่อประสิทธิภาพที่ดีขึ้น แน่นอนในกรณีที่คุณรู้ว่าลูปจะถูกประหารอย่างน้อยหนึ่งครั้ง
Antonin GAVREL

0

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

แม้ว่าการกำหนดคำหลัก C ใหม่เป็นแมโครจะมีลักษณะการทำงานที่ไม่ได้กำหนด แต่เป็นวิธีเดียวที่ฉันสามารถคิดได้ว่าจะทำให้ส่วนของโค้ดเร็วขึ้นเลย: คุณสามารถเพิ่มบรรทัดนี้เหนือส่วนที่ 2:

#define while(x) sleep(x);

มันก็จะทำให้while(1)เร็วเป็นสองเท่า (หรือครึ่งหนึ่งเป็นช้า) while(2)เป็น


@ MaxB: จริง ๆ แล้วเคล็ดลับมาโครจะต้องมีการบิดเบี้ยวยิ่งขึ้น ฉันคิดว่าเวอร์ชั่นใหม่ควรใช้งานได้แม้ว่าจะไม่ได้รับการรับรองจาก C Standard
chqrlie

-4

เหตุผลเดียวที่ฉันคิดได้ว่าทำไมถึงwhile(2)ช้ากว่านี้คือ:

  1. รหัสจะปรับลูปให้เหมาะสม

    cmp eax, 2

  2. เมื่อการลบเกิดขึ้นคุณต้องลบออกเป็นหลัก

    00000000 - 00000010 cmp eax, 2

    แทน

    00000000 - 00000001 cmp eax, 1

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


15
cmp จะใช้รอบ cpu 1 รอบโดยไม่คำนึงถึงทั้งสองกรณี
ลุง

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