2010-03-30 54 views
7

Nếu chúng ta có 2 đoạn mã sau đây trong C++ mà làm nhiệm vụ giống nhau:nhanh hơn là gì?

int a, b=somenumber; 
while(b > 0) 
{ 
a = b % 3; 
b /= 3; 
} 

hoặc

int b=somenumber; 
while(b > 0) 
{ 
int a=b%3; 
b /= 3; 
} 

Tôi không biết nhiều về kiến ​​trúc/C++ thiết kế máy tính, nhưng tôi nghĩ rằng rằng mã đầu tiên nhanh hơn vì nó khai báo số nguyên ở đầu và chỉ sử dụng nó trong vòng lặp while, và trong mã thứ hai, số nguyên a được khai báo mỗi khi vòng lặp while bắt đầu lại. Có thể một số giúp tôi với điều này, tôi là chính xác hoặc những gì và tại sao?

+4

Tại sao bạn không định thời gian cho cả hai giải pháp và tự mình xem? –

+35

Nếu có bất kỳ sự khác biệt nào cả, thì bạn cần một trình biên dịch mới. –

+2

chúng không tương đương. a là scoped đến vòng lặp while trong lần thứ hai nhưng với phạm vi kèm theo trong lần đầu tiên. –

Trả lời

11

Khai báo int là thông tin cho trình biên dịch và không dịch sang hướng dẫn phải được mã hóa. Vì vậy, nó làm cho không có sự khác biệt. Khai báo int bên trong vòng lặp sẽ không slop vòng lặp xuống. Tại sao không thử biên dịch cả hai cho chính mình và nhận được trình biên dịch để mã lắp ráp đầu ra để bạn có thể xem cho chính mình.

-6

Người đầu tiên NÊN nên nhanh hơn; tuy nhiên trình biên dịch thường đủ thông minh để tối ưu hóa chính nó vì vậy có lẽ sẽ không thành vấn đề.

Đối với độ tinh khiết vì lợi ích tuy nhiên, câu trả lời là lần đầu tiên một

EDIT: Nó là nhanh hơn bởi vì nó đòi hỏi chỉ có một phân bổ như trái ngược với N (N là số lần lặp vòng lặp while sẽ thực hiện).

+1

AFAIK điều này là sai. Tất cả các biến cục bộ chỉ được cấp phát một lần cho mỗi cuộc gọi hàm. –

+2

Tôi không chắc chắn điều đó là chính xác. Địa phương 'a' không được sử dụng trong ví dụ thứ hai, do đó trình biên dịch có thể loại bỏ nó một cách an toàn. Và trong mọi trường hợp, tôi mong đợi nó sẽ tái sử dụng cùng một không gian ngăn xếp cho mỗi lần lặp lại. –

+1

Không có phân bổ trong vòng lặp và "khởi tạo" (nhiệm vụ) được thực hiện trong cả hai trường hợp (hoặc không phải là một, do trình biên dịch được thông minh). –

1

Không, nó không thể được "khai báo" trong vòng lặp, vì nó được khai báo tại thời gian biên dịch. Tôi muốn nói rằng họ đang bình đẳng, nhưng thứ hai có thể đã được nhanh hơn nếu các loại biến là một cái gì đó phức tạp hơn, có constructor và destructor.

5

Nghiêm túc, điều đó thực sự quan trọng? Đây là loại tối ưu hóa vi mô mà bạn nên tránh. Viết mã dễ đọc hơn mà IMHO là vòng lặp thứ hai. Trình biên dịch đủ tốt để thực hiện tối ưu hóa cho những thứ này và tôi sẽ để nó làm điều đó.

+0

Tôi không ngại bỏ phiếu xuống, nhưng một lý do sẽ là tốt :-) – Naveen

+1

Không phải là nó rõ ràng? Bạn đã từng trả lời câu hỏi chưa? ;-) –

+0

Quan điểm của ông là; nó "nhanh hơn" không bận tâm với việc tối ưu hóa vi mô này hơn là thực sự cố gắng tối ưu hóa nó: vì vậy chắc chắn mã được đề xuất đầu tiên nhanh hơn, bởi vì OP nghĩ đó là nhanh nhất. – Pindatjuh

2

Không có "nhanh hơn" trong tiêu chuẩn C++, ngoại trừ đảm bảo hiệu suất trong thư viện chuẩn. Trình biên dịch tối ưu hóa có khả năng sẽ loại bỏ a, vì nó không được sử dụng. Ngoài ra, nó có thể phân bổ tất cả bộ nhớ chức năng cần thiết cho tất cả các biến cục bộ cùng một lúc, và sau đó nó sẽ không tạo ra bất kỳ sự khác biệt nào.

Câu hỏi hợp pháp duy nhất về cấu trúc ngôn ngữ cấp thấp như thế này là liệu triển khai cụ thể của bạn có chạy nhanh hay chậm hơn và cách tốt nhất để tìm ra nó là tự mình thực hiện. Bạn sẽ thấy rằng rất nhiều thứ trong số này chỉ đơn giản là không quan trọng, và nếu bạn kiểm tra mã được tạo ra, bạn thường sẽ thấy rằng các trình biên dịch làm điều tương tự với các cách viết mã khác nhau.

Thông thường, tìm kiếm tối ưu hóa vi mô là một ý tưởng tồi, nhưng nếu bạn đang cố gắng thiết lập một kiểu chung, nó có thể đáng giá (ví dụ: sử dụng ++i thay vì i++). Tuy nhiên, nếu bạn đang thiết lập một phong cách cho bất kỳ mục đích nào khác ngoài khả năng đọc, bạn nên có lý do chính đáng để thực hiện nó. Trong trường hợp này, điều đó có nghĩa là kiểm tra hiệu suất.

+0

Những gì tôi đã nói, nhưng hùng hồn hơn tôi từng có thể. –

1

Về mặt lý thuyết, tùy chọn đầu tiên có thể nhanh hơn. Trong thực tế, tôi mong đợi a và b được đưa vào sổ đăng ký theo cách sao cho assembly được tạo ra giống hệt nhau (mà bạn có thể xác minh trong binary đã biên dịch). Nếu bạn đang thực hiện vòng lặp đủ thời gian mà bạn nghĩ rằng có thể có một sự khác biệt, cách duy nhất để biết là để đo lường. Nếu hồ sơ của bạn không thể nói khác, hãy viết mã theo cách làm cho mã rõ ràng nhất đối với những người bảo trì trong tương lai.

Nói chung (như đã đề cập), các loại tối ưu hóa này sẽ không cung cấp bất kỳ cải tiến có ý nghĩa nào về hiệu suất chương trình. Thay vào đó, bạn nên tìm kiếm tối ưu hóa thuật toán và thiết kế.

14

Không có sự khác biệt, nhưng để có thêm kinh nghiệm (hậu môn?) Tôi đã thử nghiệm điều này với g ++, tạo ra một hàm cho mỗi đoạn mã. Cả hai có và không có tối ưu hóa, nó tạo ra giống hệt nhau mã bất kể nơi khai báo là int a.

#include <iostream> 

int variant_a(int b) 
{ 
     int a; 
     while(b > 0) 
     { 
       a = b % 3; 
       b /= 3; 
     } 
     return b; 
} 

int variant_b(int b) 
{ 
     while(b > 0) 
     { 
       int a = b % 3; 
       b /= 3; 
     } 
     return b; 
} 

int main() 
{ 
     std::cout << variant_a(42) << std::endl; 
     std::cout << variant_b(42) << std::endl; 
} 

Đây là vòng lặp được tối ưu hóa:

_Z9variant_ai: 
.LFB952: 
     pushl %ebp 
.LCFI0: 
     movl %esp, %ebp 
.LCFI1: 
     subl $24, %esp 
.LCFI2: 
     jmp  .L2 
.L3: 
     movl 8(%ebp), %eax 
     movl %eax, -20(%ebp) 
     movl $1431655766, -24(%ebp) 
     movl -24(%ebp), %eax 
     imull -20(%ebp) 
     movl %edx, %ecx 
     movl -20(%ebp), %eax 
     sarl $31, %eax 
     subl %eax, %ecx 
     movl %ecx, %eax 
     addl %eax, %eax 
     addl %ecx, %eax 
     movl -20(%ebp), %edx 
     subl %eax, %edx 
     movl %edx, %eax 
     movl %eax, -4(%ebp) 
     movl 8(%ebp), %eax 
     movl %eax, -20(%ebp) 
     movl $1431655766, -24(%ebp) 
     movl -24(%ebp), %eax 
     imull -20(%ebp) 
     movl %edx, %ecx 
     movl -20(%ebp), %eax 
     sarl $31, %eax 
     movl %ecx, %edx 
     subl %eax, %edx 
     movl %edx, %eax 
     movl %eax, 8(%ebp) 
.L2: 
     cmpl $0, 8(%ebp) 
     jg  .L3 
     movl 8(%ebp), %eax 
     leave 
     ret 

và tối ưu hóa một:

_Z9variant_ai: 
.LFB968: 
     pushl %ebp 
.LCFI0: 
     movl %esp, %ebp 
.LCFI1: 
     pushl %ebx 
.LCFI2: 
     movl 8(%ebp), %ebx 
     testl %ebx, %ebx 
     jle  .L2 
     movl $1431655766, %ecx 
     .p2align 4,,7 
     .p2align 3 
.L5: 
     movl %ebx, %eax 
     imull %ecx 
     movl %ebx, %eax 
     sarl $31, %eax 
     movl %edx, %ebx 
     subl %eax, %ebx 
     jne  .L5 
.L2: 
     movl %ebx, %eax 
     popl %ebx 
     popl %ebp 
     ret 
+0

Ít nhất một người nào đó thực sự đã sao lưu xác nhận quyền sở hữu bằng dữ liệu. Không phải là tôi thực sự nghi ngờ, nhưng tôi thích sự thật để giả định. –

+3

Errr ... Tôi hy vọng bạn đã chỉnh sửa bài đăng này sau khi tôi nhấn nút "Thêm nhận xét". (Gợi ý; biến thể_a và biến thể_b giống hệt nhau?) – Pindatjuh

+3

Hai hàm a và b của bạn giống nhau, chúng có khác nhau không? –

0

Tôi không tin rằng sẽ có bất kỳ sự khác biệt trong thực tế. Không có sự phân bổ bộ nhớ nào liên quan, bởi vì bộ nhớ cho các biến tự động được cấp phát hoặc được đặt sang một bên tại thời gian biên dịch.

Về mặt lý thuyết, tôi nghĩ rằng thứ hai có thể dễ dàng nhanh hơn: trình biên dịch có thêm thông tin về vị trí và cách sử dụng biến (ví dụ: bạn có thể sử dụng lại biến tương tự cho một thứ hoàn toàn không liên quan sau này).

Bạn có thể bắt đầu lo lắng về những điều như vậy khi bạn đang xử lý các loại tốn kém để xây dựng. Ví dụ: tôi nên tuyên bố một std :: vector trong vòng lặp bên trong, hoặc tôi nên khai báo nó trước khi vòng lặp và clear() nó ở đầu của cơ thể vòng lặp (tái sử dụng bộ nhớ được phân bổ).

Các vấn đề liên quan