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?
if(flag = 0)
với cái giá là dễ đọc.
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?
if(flag = 0)
với cái giá là dễ đọc.
Câu trả lời:
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 flag
thự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 flag
là 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 Jxx
hướng dẫn cho if
câ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.
flag
phải là số nguyên hoặc boolean. OTOH, có một biến có tên flag
củ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
#include
chỉ thị nào. Để đơn giản, nó thường số tiền int
, char
, bool
và 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 :)
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
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 type
cờ 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!
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>
.
Sẽ hoàn toàn không có sự khác biệt về tốc độ. Tại sao phải có?
x == 0
có thể sử dụng lệnh đó nhưng 0 == x
có 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.
virtual operator==(int)
trong một loại do người dùng xác định?
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.
Khi nghi ngờ hãy đánh giá nó và tìm hiểu sự thật.
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
{
}
Như những người khác đã nói, không có sự khác biệt.
0
phải được đánh giá. flag
phả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)
và 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 ...
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);
}
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 == 0
có thể biên dịch thành một lệnh hợp ngữ gốc jump if zero
, trong khi 0 == x
có 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 ...
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.
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 độ.
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.
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 ...