คำถามสัมภาษณ์: อันไหนจะดำเนินการเร็วกว่าif (flag==0)
หรือif (0==flag)
? ทำไม?
if(flag = 0)
ในราคาที่อ่านได้เล็กน้อย
คำถามสัมภาษณ์: อันไหนจะดำเนินการเร็วกว่าif (flag==0)
หรือif (0==flag)
? ทำไม?
if(flag = 0)
ในราคาที่อ่านได้เล็กน้อย
คำตอบ:
ฉันยังไม่เห็นคำตอบที่ถูกต้อง (และมีข้อแม้อยู่แล้ว) : นาวาซชี้ให้เห็นกับดักที่ผู้ใช้กำหนดเอง และฉันเสียใจที่รีบโหวต "คำถามที่โง่ที่สุด" เพราะดูเหมือนว่ามีหลายคนทำไม่ถูกและทำให้มีพื้นที่สำหรับการอภิปรายที่ดีเกี่ยวกับการเพิ่มประสิทธิภาพคอมไพเลอร์ :)
คำตอบคือ:
คืออะไร
flag
ชนิด 's?
ในกรณีที่flag
เป็นประเภทที่ผู้ใช้กำหนดเอง จากนั้นขึ้นอยู่กับว่าเลือกโอเวอร์โหลดoperator==
ใด แน่นอนว่ามันอาจดูโง่ที่มันจะไม่สมมาตร แต่มันก็อนุญาตอย่างแน่นอนและฉันก็เคยเห็นการละเมิดอื่น ๆ มาแล้ว
หากflag
เป็นแบบบิวท์อินทั้งคู่ควรใช้ความเร็วเท่ากัน
จากบทความวิกิพีเดียในx86
, ฉันเดิมพันสำหรับJxx
การเรียนการสอนสำหรับif
คำสั่ง: บางทีอาจจะเป็นJNZ
(Jump ถ้าไม่เป็นศูนย์) หรือเทียบเท่าบาง
ฉันสงสัยว่าคอมไพเลอร์พลาดการเพิ่มประสิทธิภาพที่ชัดเจนเช่นนี้แม้ว่าจะปิดการปรับให้เหมาะสมแล้วก็ตาม นี่คือประเภทของสิ่งต่าง ๆ ที่Peephole Optimizationออกแบบมาสำหรับ
แก้ไข:ผุดขึ้นมาอีกแล้วเรามาเพิ่มแอสเซมบลีกันเถอะ (LLVM 2.7 IR)
int regular(int c) {
if (c == 0) { return 0; }
return 1;
}
int yoda(int c) {
if (0 == c) { return 0; }
return 1;
}
define i32 @regular(i32 %c) nounwind readnone {
entry:
%not. = icmp ne i32 %c, 0 ; <i1> [#uses=1]
%.0 = zext i1 %not. to i32 ; <i32> [#uses=1]
ret i32 %.0
}
define i32 @yoda(i32 %c) nounwind readnone {
entry:
%not. = icmp ne i32 %c, 0 ; <i1> [#uses=1]
%.0 = zext i1 %not. to i32 ; <i32> [#uses=1]
ret i32 %.0
}
แม้ว่าใครจะไม่รู้วิธีอ่าน IR แต่ฉันก็คิดว่ามันอธิบายได้ด้วยตนเอง
flag
ต้องเป็นจำนวนเต็มหรือบูลีนโดยอัตโนมัติ OTOH การมีตัวแปรชื่อflag
ประเภทที่ผู้ใช้กำหนดนั้นค่อนข้างผิดในตัวเอง IMHO
#include
คำสั่งเดียว เพื่อประโยชน์ของความเรียบง่ายก็มักจะมีจำนวนint
, char
, bool
และชอบ ทุกประเภทอื่น ๆ จะกล่าวว่าเป็นผู้ใช้กำหนดนั่นคือพวกเขามีอยู่เพราะพวกเขาเป็นผลมาจากผู้ใช้บางพวกเขาประกาศ: typedef
, enum
, ,struct
class
ตัวอย่างเช่นstd::string
ผู้ใช้กำหนดแม้ว่าคุณจะไม่ได้กำหนดเองอย่างแน่นอน :)
รหัสเดียวกันสำหรับ amd64 กับ GCC 4.1.2:
.loc 1 4 0 # int f = argc;
movl -20(%rbp), %eax
movl %eax, -4(%rbp)
.loc 1 6 0 # if( f == 0 ) {
cmpl $0, -4(%rbp)
jne .L2
.loc 1 7 0 # return 0;
movl $0, -36(%rbp)
jmp .L4
.loc 1 8 0 # }
.L2:
.loc 1 10 0 # if( 0 == f ) {
cmpl $0, -4(%rbp)
jne .L5
.loc 1 11 0 # return 1;
movl $1, -36(%rbp)
jmp .L4
.loc 1 12 0 # }
.L5:
.loc 1 14 0 # return 2;
movl $2, -36(%rbp)
.L4:
movl -36(%rbp), %eax
.loc 1 15 0 # }
leave
ret
จะไม่มีความแตกต่างในเวอร์ชันของคุณ
ฉันสมมติว่าtype
ธงไม่ใช่ประเภทที่ผู้ใช้กำหนด แต่เป็นประเภทที่มีอยู่แล้วภายใน Enum เป็นข้อยกเว้น! . คุณสามารถรักษา enum ได้เหมือนในตัว ในความเป็นจริงค่าของมันเป็นหนึ่งในประเภทในตัว!
ในกรณีถ้ามันของผู้ใช้กำหนดชนิด (ยกเว้นenum
) ==
แล้วคำตอบทั้งหมดขึ้นอยู่กับวิธีการที่คุณได้มากเกินไปผู้ประกอบการ โปรดทราบว่าคุณต้องทำงานหนักเกินไป==
โดยกำหนดสองฟังก์ชันหนึ่งฟังก์ชันสำหรับแต่ละเวอร์ชันของคุณ!
ไม่มีความแตกต่างอย่างแน่นอน
คุณอาจได้รับคะแนนในการตอบคำถามสัมภาษณ์นั้นโดยอ้างถึงการกำจัดความผิดพลาดในการมอบหมายงาน / การเปรียบเทียบแม้ว่า:
if (flag = 0) // typo here
{
// code never executes
}
if (0 = flag) // typo and syntactic error -> compiler complains
{
// ...
}
แม้ว่าจะเป็นเรื่องจริง แต่เช่น C-compiler เตือนในกรณีของอดีต ( flag = 0
) ไม่มีคำเตือนดังกล่าวใน PHP, Perl หรือ Javascript หรือ<insert language here>
.
จะไม่มีความเร็วที่แตกต่างกันอย่างแน่นอน ทำไมต้องมี?
x == 0
อาจใช้ แต่0 == x
อาจใช้การเปรียบเทียบแบบปกติ ฉันไม่ได้บอกว่ามันจะต้องปัญญาอ่อน
virtual operator==(int)
ประเภทที่ผู้ใช้กำหนดเอง?
มีความแตกต่างเมื่อแฟล็กเป็นประเภทที่ผู้ใช้กำหนด
struct sInt
{
sInt( int i ) : wrappedInt(i)
{
std::cout << "ctor called" << std::endl;
}
operator int()
{
std::cout << "operator int()" << std::endl;
return wrappedInt;
}
bool operator==(int nComp)
{
std::cout << "bool operator==(int nComp)" << std::endl;
return (nComp == wrappedInt);
}
int wrappedInt;
};
int
_tmain(int argc, _TCHAR* argv[])
{
sInt s(0);
//in this case this will probably be faster
if ( 0 == s )
{
std::cout << "equal" << std::endl;
}
if ( s == 0 )
{
std::cout << "equal" << std::endl;
}
}
ในกรณีแรก (0 == s) ตัวดำเนินการแปลงถูกเรียกจากนั้นผลลัพธ์ที่ส่งกลับจะถูกเปรียบเทียบกับ 0 ในกรณีที่สองตัวดำเนินการ == ถูกเรียก
เมื่อมีข้อสงสัยจงเปรียบเทียบและเรียนรู้ความจริง
พวกเขาควรจะเหมือนกันทุกประการในแง่ของความเร็ว
อย่างไรก็ตามสังเกตว่าบางคนใช้ค่าคงที่ทางซ้ายในการเปรียบเทียบความเท่าเทียมกัน (ที่เรียกว่า "Yoda conditionals") เพื่อหลีกเลี่ยงข้อผิดพลาดทั้งหมดที่อาจเกิดขึ้นหากคุณเขียน=
(ตัวดำเนินการกำหนด) แทน==
(ตัวดำเนินการเปรียบเทียบความเท่าเทียมกัน) เนื่องจากการกำหนดให้ตัวอักษรทำให้เกิดข้อผิดพลาดในการคอมไพล์จึงหลีกเลี่ยงข้อผิดพลาดประเภทนี้
if(flag=0) // <--- typo: = instead of ==; flag is now set to 0
{
// this is never executed
}
if(0=flag) // <--- compiler error, cannot assign value to literal
{
}
ในทางกลับกันคนส่วนใหญ่พบว่า "Yoda conditionals" ดูแปลกและน่ารำคาญโดยเฉพาะอย่างยิ่งเนื่องจากระดับของข้อผิดพลาดที่พวกเขาป้องกันสามารถถูกตรวจพบได้โดยใช้คำเตือนของคอมไพเลอร์ที่เหมาะสม
if(flag=0) // <--- warning: assignment in conditional expression
{
}
อย่างที่คนอื่นบอกไม่มีความแตกต่าง
0
จะต้องได้รับการประเมิน flag
จะต้องได้รับการประเมิน กระบวนการนี้ใช้เวลาเท่ากันไม่ว่าจะวางไว้ด้านใดก็ตาม
คำตอบที่ถูกต้องคือทั้งคู่มีความเร็วเท่ากัน
แม้แต่นิพจน์if(flag==0)
และif(0==flag)
มีจำนวนอักขระเท่ากัน! หากหนึ่งในนั้นถูกเขียนเป็นif(flag== 0)
คอมไพเลอร์จะมีพื้นที่พิเศษสำหรับแยกวิเคราะห์ดังนั้นคุณจะมีเหตุผลที่ถูกต้องในการระบุเวลาในการคอมไพล์
แต่เนื่องจากไม่มีสิ่งนั้นจึงไม่มีเหตุผลอย่างแน่นอนว่าทำไมเราจึงควรเร็วกว่าอย่างอื่น หากมีเหตุผลแสดงว่าคอมไพเลอร์กำลังทำสิ่งที่แปลกประหลาดมากในการสร้างโค้ด ...
อันไหนเร็วขึ้นอยู่กับรุ่นของ == ที่คุณใช้ นี่คือตัวอย่างข้อมูลที่ใช้ 2 การใช้งานที่เป็นไปได้ของ == และขึ้นอยู่กับว่าคุณเลือกที่จะเรียก x == 0 หรือ 0 == x หนึ่งใน 2 ตัวเลือก
หากคุณเพิ่งใช้ POD สิ่งนี้ไม่ควรสำคัญเมื่อต้องใช้ความเร็ว
#include <iostream>
using namespace std;
class x {
public:
bool operator==(int x) { cout << "hello\n"; return 0; }
friend bool operator==(int x, const x& a) { cout << "world\n"; return 0; }
};
int main()
{
x x1;
//int m = 0;
int k = (x1 == 0);
int j = (0 == x1);
}
ฉันเห็นด้วยอย่างสมบูรณ์กับสิ่งที่กล่าวไว้ในความคิดเห็นต่อ OP เพื่อประโยชน์ในการออกกำลังกาย:
หากคอมไพเลอร์ไม่ฉลาดพอ (คุณไม่ควรใช้) หรือปิดใช้งานการเพิ่มประสิทธิภาพx == 0
สามารถคอมไพล์ตามjump if zero
คำสั่งแอสเซมบลีเนทีฟได้ในขณะที่0 == x
อาจเป็นการเปรียบเทียบค่าตัวเลขทั่วไป (และเสียค่าใช้จ่าย) มากกว่า
ถึงกระนั้นฉันก็ไม่อยากทำงานให้กับเจ้านายที่คิดในแง่เหล่านี้ ...
แน่นอนว่าไม่มีความแตกต่างในแง่ของความเร็วในการดำเนินการ เงื่อนไขจะต้องได้รับการประเมินในทั้งสองกรณีในลักษณะเดียวกัน
ฉันคิดว่าคำตอบที่ดีที่สุดคือ "ตัวอย่างนี้เป็นภาษาอะไร"
คำถามไม่ได้ระบุภาษาและมีการติดแท็กทั้ง 'C' และ 'C ++' คำตอบที่แม่นยำต้องการข้อมูลเพิ่มเติม
มันเป็นคำถามเกี่ยวกับการเขียนโปรแกรมที่น่าเบื่อ แต่มันอาจจะดีสำหรับแผนก "ขอให้ผู้ให้สัมภาษณ์มีเชือกเพียงพอที่จะแขวนคอตัวเองหรือสร้างชิงช้าบนต้นไม้" ปัญหาของคำถามประเภทนี้คือพวกเขามักจะเขียนและส่งต่อจากผู้สัมภาษณ์ไปยังผู้สัมภาษณ์จนกว่าจะถึงคนที่ไม่เข้าใจจริงๆจากทุกมุม
สร้างโปรแกรมง่ายๆสองโปรแกรมโดยใช้วิธีที่แนะนำ
รวบรวมรหัส ดูการชุมนุมแล้วคุณสามารถตัดสินได้ แต่ฉันสงสัยว่ามีความแตกต่าง!
การสัมภาษณ์ต่ำลงกว่าที่เคย
เช่นกัน (ฉันคิดว่าคอมไพเลอร์ที่เหมาะสมจะทำให้คำถามนี้สงสัยเพราะมันจะปรับให้เหมาะสม) การใช้ 0 == flag over flag == 0 จะป้องกันการพิมพ์ผิดที่คุณลืมหนึ่งใน = (เช่นถ้าคุณพิมพ์โดยไม่ได้ตั้งใจ flag = 0 มันจะคอมไพล์ แต่ 0 = flag จะไม่) ซึ่งฉันคิดว่ามันเป็นข้อผิดพลาดที่ทุกคนทำในจุดใดจุดหนึ่ง ...
หากมีความแตกต่างกันสิ่งที่หยุดคอมไพเลอร์เพื่อเลือกเร็วกว่าครั้งเดียว? เหตุผลไม่มีความแตกต่างใด ๆ อาจเป็นสิ่งที่ผู้สัมภาษณ์คาดหวัง เป็นคำถามที่ยอดเยี่ยมจริงๆ