Cái nào sẽ thực thi nhanh hơn, if (flag == 0) hoặc if (0 == flag)?


111

Câu hỏi phỏng vấn: Cái nào sẽ thực thi nhanh hơn, if (flag==0)hoặc if (0==flag)? Tại sao?


330
Được đề cử cho câu hỏi phỏng vấn ngu ngốc nhất từ ​​trước đến nay. Và có một sự cạnh tranh gay gắt.
Konrad Rudolph

119
Bạn: Kể tên một tình huống mà sự khác biệt giữa hai điều này có thể đáng bận tâm. Người phỏng vấn: Được rồi, bạn được thuê.
Chris Lutz

37
Sự khác biệt duy nhất giữa hai điều này là với quy ước sau này, bạn được bảo hiểm chống lại các lỗi giống như if(flag = 0)với cái giá là dễ đọc.
Amarghosh

22
@Amarghosh: Với cái giá phải trả là làm cho mã của bạn khó đọc và không trực quan. Sử dụng cái trước để bật cảnh báo trình biên dịch của bạn, đôi bên cùng có lợi.
GManNickG

129
Một khi một nhà văn biên dịch đã nhận được điều này trong cuộc phỏng vấn của mình. Anh ta thì thầm đáp lại, " bạn muốn cái nào nhanh hơn?".

Câu trả lời:


236

Tôi chưa thấy bất kỳ câu trả lời chính xác nào (và đã có một số) cảnh báo: Nawaz đã chỉ ra cái bẫy do người dùng xác định . Và tôi rất tiếc vì đã vội vàng phản đối "câu hỏi ngu ngốc nhất" bởi vì có vẻ như nhiều người đã không hiểu đúng và điều đó cho phép một cuộc thảo luận tốt đẹp về tối ưu hóa trình biên dịch :)

Câu trả lời là:

Là gì flag's loại?

Trong trường hợp flagthực sự là một loại do người dùng xác định. Sau đó, nó phụ thuộc vào quá tải của operator==được chọn. Tất nhiên có vẻ ngu ngốc khi chúng không đối xứng, nhưng chắc chắn nó được phép, và tôi đã thấy những hành vi lạm dụng khác rồi.

Nếu flaglà một cài sẵn, thì cả hai sẽ có cùng tốc độ.

Từ bài viết trên Wikipediax86 , tôi đặt cược cho một Jxxhướng dẫn cho ifcâu lệnh: có lẽ là a JNZ(Nhảy nếu không phải là 0) hoặc một số tương đương.

Tôi nghi ngờ trình biên dịch bỏ lỡ một tối ưu hóa rõ ràng như vậy, ngay cả khi tối ưu hóa bị tắt. Đây là loại thứ mà Tối ưu hóa lỗ nhìn trộm được thiết kế cho.

EDIT: Sprang up một lần nữa, vì vậy hãy thêm một số lắp ráp (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
}

Ngay cả khi một người không biết cách đọc IR, tôi nghĩ rằng nó là tự giải thích.


4
@Matthieu: bạn nói tôi đã không nhìn thấy bất kỳ câu trả lời đúng chưa .. nhưng tôi là đúng, tôi nghĩ rằng: P
Nawaz

7
tốt! câu trả lời của bạn có thể biến "câu hỏi ngu ngốc nhất" thành "những trò gian trá / ít ỏi". "chúng ta hãy đào một cái lỗ cho ứng cử viên và xem liệu anh ta có rơi vào đó không ..." :) Tôi đoán tất cả chúng ta đều tự động cho rằng đó flagphải là số nguyên hoặc boolean. OTOH, có một biến có tên flagcủa một loại người dùng định nghĩa là hoàn toàn sai lầm về bản thân, IMHO
davka

@Nawaz: Tôi có thể đã bỏ qua đoạn cuối cùng trong câu trả lời của bạn: p
Matthieu M.

1
@Nawaz: Tôi không thực sự chạy đua, tôi thường đọc các câu hỏi rất lâu sau khi chúng được trả lời và mọi người có xu hướng chỉ đọc những câu trả lời được ủng hộ nhiều nhất đầu tiên :) Nhưng tôi thực sự đang đọc những thứ về tối ưu hóa trình biên dịch, và điều này khiến tôi cảm thấy trường hợp điển hình của việc tối ưu hóa tầm thường vì vậy tôi nghĩ tôi sẽ chỉ ra nó cho những độc giả thực sự bận tâm ... Tôi thực sự khá ngạc nhiên khi tôi nhận được nhiều lượt ủng hộ đến vậy. Bây giờ là nhất của tôi bình chọn câu trả lời mặc dù nó chắc chắn không phải là người mà tôi đặt nhiều công sức nhất: / Dù sao tôi sửa câu trả lời của tôi và điều chỉnh tuyên bố của tôi :)
Matthieu M.

2
@mr_eclair: kiểu tích hợp là kiểu (như tên ngụ ý) được tích hợp sẵn trong ngôn ngữ. Có nghĩa là, nó có sẵn ngay cả khi không có một #includechỉ thị nào. Để đơn giản, nó thường số tiền int, char, boolvà những thứ tương tự. Tất cả các loại khác được cho là người dùng định nghĩa, đó là chúng tồn tại bởi vì họ là kết quả của một số người dùng khai báo: typedef, enum, struct, class. Ví dụ, std::stringđược người sử dụng xác định, ngay cả khi bạn chắc chắn không phải định nghĩa nó cho mình :)
Matthieu M.

56

Mã tương tự cho amd64 với 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

18
+1 để đi xa hơn để chứng minh rằng việc tối ưu hóa trình biên dịch là như nhau.
k rey

56

Sẽ không có sự khác biệt trong các phiên bản của bạn.

Tôi giả định rằng typecờ không phải là loại do người dùng xác định, thay vì đó là một số loại tích hợp sẵn. Enum là ngoại lệ! . Bạn có thể coi enum như thể nó được tích hợp sẵn. Trên thực tế, giá trị của nó là một trong những kiểu tích hợp sẵn!

Trong trường hợp, nếu đó là kiểu do người dùng xác định (ngoại trừ enum), thì câu trả lời hoàn toàn phụ thuộc vào cách bạn đã nạp chồng toán tử ==. Lưu ý rằng bạn đã quá tải ==bằng cách xác định hai chức năng, một cho mỗi phiên bản của bạn!


8
đây có thể là lý do duy nhất có thể để hỏi câu hỏi này, IMHO
davka

15
Tôi sẽ cực kỳ ngạc nhiên nếu các trình biên dịch hiện đại bỏ qua một sự tối ưu hóa rõ ràng như vậy.
Pedro d'Aquino

3
Theo hiểu biết của tôi! không phải là một hoạt động Bitwise
Xavier Combelle

8
@Nawaz: không phản đối nhưng câu trả lời của bạn thực tế là sai và thật kinh khủng khi nó vẫn nhận được rất nhiều ủng hộ. Đối với bản ghi, so sánh một số nguyên với 0 là một lệnh hợp ngữ đơn lẻ , hoàn toàn ngang bằng với phủ định. Trên thực tế, nếu trình biên dịch hơi ngu ngốc thì điều này thậm chí có thể nhanh hơn phủ định (mặc dù không có khả năng xảy ra).
Konrad Rudolph

6
@Nawaz: vẫn sai khi nói rằng nó có thể, sẽ hoặc thường sẽ nhanh hơn. Nếu có sự khác biệt, thì phiên bản "so sánh với số không" sẽ nhanh hơn, vì phép phủ định thực sự chuyển thành hai phép toán: "phủ định toán hạng; kiểm tra kết quả đó có phải là khác không". Tất nhiên, trong thực tế, trình biên dịch tối ưu hóa nó để mang lại mã giống như phiên bản đơn giản "so sánh với số không", nhưng tối ưu hóa đang được áp dụng cho phiên bản phủ định, để làm cho nó bắt kịp, chứ không phải ngược lại. Konrad nói đúng.
jalf

27

Hoàn toàn không có sự khác biệt.

Tuy nhiên, bạn có thể đạt được điểm khi trả lời câu hỏi phỏng vấn đó bằng cách đề cập đến việc loại bỏ lỗi chính tả phân công / so sánh:

if (flag = 0)  // typo here
   {
   // code never executes
   }

if (0 = flag) // typo and syntactic error -> compiler complains
   {
   // ...
   }

Trong khi đó là sự thật, ví dụ như trình biên dịch C không cảnh báo trong trường hợp trước đây ( flag = 0), không có cảnh báo nào như vậy trong PHP, Perl hoặc Javascript hoặc <insert language here>.


@Matthieu Hả. Tôi hẳn đã bỏ lỡ bài đăng trên meta mô tả phong cách giằng "thích hợp".
Linus Kleen

7
Tôi chưa bỏ phiếu lần nào, nhưng vì điều đó đáng giá: tại sao mọi người phải giải thích về bản thân bất cứ khi nào họ bỏ phiếu lại quan trọng đến vậy? Các phiếu bầu được ẩn danh theo thiết kế. Tôi hoàn toàn phản đối ý kiến ​​rằng những người phản đối nên luôn bình luận, bởi vì cá nhân tôi không bao giờ muốn bị cho là người phản đối chỉ vì tôi đã để lại một bình luận chỉ ra một vấn đề. Có lẽ người phản đối nghĩ rằng phần lớn câu trả lời không liên quan đến câu hỏi tốc độ? Có lẽ anh ấy nghĩ rằng nó khuyến khích một phong cách mã hóa mà anh ấy không chấp nhận? Có lẽ anh ta là một kẻ ranh mãnh, và muốn câu trả lời của riêng mình có xếp hạng cao nhất?
David Hedlund

3
Mọi người nên được tự do bỏ phiếu tùy ý, bất kể lý do là gì. Về danh tiếng, điều này hầu như luôn luôn là một điều tốt vì nó thường kích động người khác ủng hộ, để chống lại sự phản đối không đáng có, trong khi thực tế, một phiếu ủng hộ duy nhất sẽ hủy bỏ năm phiếu phản đối không được phục vụ.
David Hedlund

26
@David: Những người phản đối nên tự giải thích vì trang web này không nói về các cuộc bỏ phiếu phổ biến bí mật, bỏ phiếu ẩn danh hoặc những thứ tương tự. Trang web này là về học tập. Nếu ai đó nói rằng phản hồi không chính xác bằng cách từ chối nó, thì người phản đối đang ích kỷ với kiến ​​thức của họ nếu họ không giải thích tại sao. Họ sẵn sàng nhận mọi công lao khi họ đúng, nhưng không sẵn sàng chia sẻ kiến ​​thức khi người khác sai.
John Dibling

1
Chỉ để giải quyết vấn đề về phong cách niềng răng, tôi thực sự nghĩ Matthieu định đó như một trò đùa. Tôi sẽ ngạc nhiên khi thấy rằng bất kỳ ai cũng bỏ phiếu của họ tùy thuộc vào những vấn đề như vậy. Phải nói rằng, không phải ai cũng sử dụng phiếu bầu theo cùng một cách. Tôi có thể thấy lý do cho việc phản đối vì bài đăng dường như ủng hộ phong cách viết mã mà người bỏ phiếu có thể không chấp nhận (lưu ý sự khác biệt giữa việc ủng hộ phong cách viết mã - "nếu bạn viết mã của mình như thế này, bạn sẽ gặp lỗi trình biên dịch khi thực hiện typo này" - và chỉ cần sử dụng một phong cách mã hóa, chẳng hạn như niềng răng) Trong đó ...
David Hedlund

16

Sẽ hoàn toàn không có sự khác biệt về tốc độ. Tại sao phải có?


7
nếu trình biên dịch bị chậm hoàn toàn. Đó là lý do duy nhất.
JeremyP

@JeremyP: Tôi không thể tưởng tượng được sự khác biệt ngay cả khi trình biên dịch bị chậm lại. Người viết trình biên dịch sẽ phải làm điều đó có chủ đích trong chừng mực tôi có thể nói.
Jon

2
Giả sử bộ xử lý có lệnh "kiểm tra nếu 0", x == 0có thể sử dụng lệnh đó nhưng 0 == xcó thể sử dụng phép so sánh bình thường. Tôi đã nói rằng nó sẽ phải chậm phát triển.
JeremyP

8
Nếu cờ là một loại người dùng định nghĩa với một tình trạng quá tải đối xứng của toán tử == ()
OrangeDog

Bởi vì chúng tôi có thểvirtual operator==(int)trong một loại do người dùng xác định?
lorro

12

Có một sự khác biệt khi cờ là loại do người dùng xác định

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

Trong trường hợp đầu tiên (0 == s) toán tử chuyển đổi được gọi và sau đó kết quả trả về được so sánh với 0. Trong trường hợp thứ hai, toán tử == được gọi.


3
+1 vì đã đề cập rằng toán tử chuyển đổi có thể có liên quan như toán tử ==.
Tony Delroy

11

Khi nghi ngờ hãy đánh giá nó và tìm hiểu sự thật.


2
có chuyện gì với điểm chuẩn? đôi khi việc thực hành là nói cho bạn nhiều hơn lý thuyết
Elzo Valugi

1
Đó là câu trả lời tôi đã tìm kiếm khi tôi bắt đầu đọc chủ đề này. Dường như lý thuyết được nhiều hấp dẫn hơn so với thực tế, tìm câu trả lời và upvotes :)
Samuel Rivas

Làm thế nào anh ta có thể chuẩn mực khi phỏng vấn? cộng với tôi nghĩ người phỏng vấn thậm chí không biết điểm chuẩn nghĩa là gì, vì vậy anh ta có thể đã bị xúc phạm.
IAdapter

Họ trả lời đúng cho câu hỏi (IMO) là "Điều đó khá nhiều phụ thuộc vào trình biên dịch và phần còn lại của chương trình tôi muốn viết một chuẩn mực và thử nghiệm nó trong 5 phút."
Samuel Rivas

7

Chúng phải hoàn toàn giống nhau về tốc độ.

Tuy nhiên, lưu ý rằng một số người sử dụng để đặt hằng số ở bên trái trong phép so sánh bình đẳng (cái gọi là "điều kiện Yoda") để tránh tất cả các lỗi có thể phát sinh nếu bạn viết =(toán tử gán) thay vì ==(toán tử so sánh bình đẳng); vì việc gán cho một ký tự sẽ gây ra lỗi biên dịch, nên tránh được loại lỗi này.

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
{

}

Mặt khác, hầu hết mọi người thấy "điều kiện Yoda" trông kỳ lạ và khó chịu, đặc biệt là vì lớp lỗi mà họ ngăn chặn cũng có thể được phát hiện bằng cách sử dụng các cảnh báo trình biên dịch thích hợp.

if(flag=0) // <--- warning: assignment in conditional expression
{

}

Cảm ơn vì đã vang vọng. Tuy nhiên, lưu ý rằng PHP chẳng hạn sẽ không cảnh báo trong trường hợp gán trong điều kiện.
Linus Kleen

5

Như những người khác đã nói, không có sự khác biệt.

0phải được đánh giá. flagphải được đánh giá. Quá trình này mất cùng một thời gian, bất kể chúng được đặt ở phía nào.

Câu trả lời đúng sẽ là: cả hai đều có cùng tốc độ.

Ngay cả các biểu thức if(flag==0)if(0==flag)có cùng một lượng ký tự! Nếu một trong số chúng được viết là if(flag== 0), thì trình biên dịch sẽ có thêm một không gian để phân tích cú pháp, vì vậy bạn sẽ có lý do chính đáng khi chỉ ra thời gian biên dịch.

Nhưng vì không có điều đó, hoàn toàn không có lý do gì tại sao người này phải nhanh hơn người khác. Nếu có một lý do nào đó, thì trình biên dịch đang thực hiện một số điều rất, rất kỳ lạ đối với mã được tạo ...


5

Tốc độ của cái nào phụ thuộc vào phiên bản == bạn đang sử dụng. Đây là đoạn mã sử dụng 2 cách triển khai có thể có của == và tùy thuộc vào việc bạn chọn gọi x == 0 hay 0 == x, một trong 2 được chọn.

Nếu bạn chỉ đang sử dụng POD, điều này thực sự không quan trọng khi nói đến tốc độ.

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

5

Vâng, tôi hoàn toàn đồng ý với tất cả những gì đã nói trong các bình luận với OP, vì lợi ích của bài tập:

Nếu trình biên dịch không đủ thông minh (thực sự là bạn không nên sử dụng nó) hoặc tối ưu hóa bị vô hiệu hóa, x == 0có thể biên dịch thành một lệnh hợp ngữ gốc jump if zero, trong khi 0 == xcó thể là một so sánh chung chung hơn (và tốn kém) các giá trị số.

Tuy nhiên, tôi không muốn làm việc cho một ông chủ nghĩ theo những điều này ...


4

Chắc chắn không có sự khác biệt về tốc độ thực thi. Điều kiện cần được đánh giá trong cả hai trường hợp theo cùng một cách.


3

Tôi nghĩ câu trả lời tốt nhất là "ví dụ này bằng ngôn ngữ nào"?

Câu hỏi không chỉ định ngôn ngữ và nó được gắn thẻ cả 'C' và 'C ++'. Một câu trả lời chính xác cần thêm thông tin.

Đó là một câu hỏi lập trình tệ hại, nhưng nó có thể là một câu hỏi hay ở chỗ "hãy cung cấp cho người được phỏng vấn đủ dây để treo cổ tự tử hoặc dựng cây đu". Vấn đề với những loại câu hỏi đó là chúng thường được viết ra và truyền lại từ người phỏng vấn này sang người phỏng vấn khác cho đến khi nó đến tay những người không thực sự hiểu nó từ mọi góc độ.


3

Xây dựng hai chương trình đơn giản bằng các cách được gợi ý.

Lắp ráp các mã. Nhìn vào cách lắp ráp và bạn có thể đánh giá, nhưng tôi nghi ngờ có sự khác biệt!

Các cuộc phỏng vấn ngày càng thấp hơn bao giờ hết.


2

Cũng giống như một điều sang một bên (tôi thực sự nghĩ rằng bất kỳ trình biên dịch tốt nào sẽ làm cho câu hỏi này tranh luận, vì nó sẽ tối ưu hóa nó) bằng cách sử dụng 0 == cờ trên cờ == 0 sẽ ngăn lỗi đánh máy mà bạn quên một trong các dấu = (tức là nếu bạn vô tình nhập Flag = 0 nó sẽ biên dịch, nhưng 0 = flag thì không), mà tôi nghĩ là một sai lầm mà mọi người đã mắc phải ở điểm này hay lúc khác ...


0

Nếu có sự khác biệt, điều gì sẽ dừng trình biên dịch để chọn nhanh hơn một lần? Vì vậy, về mặt logic, không thể có bất kỳ sự khác biệt nào. Có lẽ đây là điều mà người phỏng vấn mong đợi. Nó thực sự là một câu hỏi tuyệt vời.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.