2010-04-15 29 views
9

Có lẽ đây là một điều cụ thể của trình biên dịch. Nếu vậy, làm thế nào về gcc (g ++)? Nếu bạn sử dụng tham chiếu/bí danh biến như sau:Tham chiếu biến (bí danh) có phát sinh chi phí thời gian chạy không?

int x = 5; 
int& y = x; 
y += 10; 

Thực sự cần chu kỳ nhiều hơn nếu chúng tôi không sử dụng tham chiếu.

int x = 5; 
x += 10; 

Nói cách khác, mã máy có thay đổi hay "bí danh" chỉ xảy ra ở cấp trình biên dịch không?

Điều này có vẻ như một câu hỏi ngớ ngẩn, nhưng tôi tò mò. Đặc biệt trong trường hợp có thể thuận tiện để tạm thời đổi tên một số biến thành viên để mã toán dễ đọc hơn một chút. Chắc chắn, chúng tôi không nói chính xác về một nút cổ chai ở đây ... nhưng đó là một cái gì đó mà tôi đang làm và vì vậy tôi chỉ tự hỏi nếu có bất kỳ sự khác biệt 'thực tế' ... hoặc nếu nó chỉ là mỹ phẩm.

Trả lời

9

Nó có thể được coi là bí danh, nhưng không phải là về hiệu quả. Bên dưới mui xe, một tham chiếu là một con trỏ với một cú pháp đẹp hơn và đảm bảo an toàn cao hơn. Do đó bạn đã có một hình phạt thời gian hoạt động "dereference". UNLESS trình biên dịch tối ưu hóa nó, nhưng tôi sẽ không dựa vào đó thường.

Trong trường hợp có câu hỏi liệu trình biên dịch có tối ưu hóa hay không, không có cách nào khác ngoài việc xem hội đồng được tạo ra.

+0

Câu trả lời hay. Cảm ơn – cheshirekow

+4

Câu trả lời không chính xác. Một trong những ý định đằng sau các tham chiếu là thực hiện khái niệm về một bí danh, tên thay thế cho một đối tượng hiện có. Tôi tin rằng điều này được nêu rõ trong TC++ PL. Trong khi điều này không phải luôn luôn như vậy, "bí danh" vẫn là một mô tả chính xác về những tham chiếu trong nhiều trường hợp. – AnT

+0

@AndreyT, tôi chưa bao giờ nghe ý tưởng về các tham chiếu là bí danh, bạn có thể cho tôi biết đoạn văn của tiêu chuẩn được hàm ý không? –

4

Cách duy nhất để biết chắc chắn là biên dịch và kiểm tra đầu ra của trình biên dịch. Nói chung, chi phí cho một tham chiếu tương tự như chi phí cho một con trỏ, bởi vì con trỏ thường là cách các tham chiếu được thực hiện. Tuy nhiên, với trường hợp đơn giản mà bạn đang hiển thị, tôi tin rằng tham chiếu sẽ được tối ưu hóa.

+0

Vâng, một ngày nào đó tôi có thể làm một số xét nghiệm đơn giản và xem liệu nó có được tối ưu hóa không ... hiện tại, nó không quá quan trọng. – cheshirekow

0

Có, dereferencing con trỏ phía sau tham chiếu chịu chi phí thời gian chạy bổ sung, nhưng có khả năng không đáng kể. Viết mã theo bất kỳ cách rõ ràng nhất để đọc và rõ ràng nhất rõ ràng ngữ nghĩa bạn đang hướng tới, và sau đó chạy trong một profiler nếu hiệu suất là một vấn đề (nút cổ chai hiếm khi những gì bạn đoán nó là). Nếu bạn đang sử dụng hệ điều hành MacOS, Shark thật tuyệt vời.

+0

Xong. Như tôi đã nói, chỉ tò mò thôi. Phát triển trên máy Mac? ew ...;) – cheshirekow

+0

Đừng snub những gì bạn không biết! :-) Các công cụ gỡ lỗi và lược tả là đỉnh cao và không chi phí $$$. – metasim

+0

Câu trả lời này sai. –

4

Đúng là trong hầu hết các trường hợp, tham chiếu sẽ triển khai khái niệm "bí danh", một tên thay thế cho đối tượng mà chúng bị ràng buộc.

Tuy nhiên, trong trường hợp tham chiếu trường hợp chung được thực hiện thông qua con trỏ. Tuy nhiên, một trình biên dịch tốt sẽ chỉ sử dụng một con trỏ thực tế để thực hiện tham chiếu trong các tình huống khi liên kết thực sự được xác định tại thời gian chạy. Nếu ràng buộc được biết tại thời gian biên dịch (và các kiểu khớp), trình biên dịch sẽ thường thực hiện tham chiếu như một tên thay thế cho cùng một đối tượng, trong trường hợp đó sẽ không có hiệu suất nào để truy cập đối tượng thông qua tham chiếu (so với truy cập nó thông qua tên ban đầu của nó).

Ví dụ của bạn là một trong những ví dụ khi bạn sẽ không nhận được hình phạt về hiệu suất từ ​​tham chiếu.

4

Cả hai chức năng này đều biên dịch chính xác cùng một mã trong g++, thậm chí chỉ cần sử dụng -O1. (Tôi đã thêm tuyên bố return để đảm bảo rằng tính toán không được loại bỏ hoàn toàn.)

Không có con trỏ, chỉ tham chiếu. Trong ví dụ tầm thường này, không có sự khác biệt về hiệu suất. Đó là không đảm bảo rằng điều này sẽ luôn luôn là trường hợp (không có sự khác biệt hiệu suất) cho tất cả các sử dụng tài liệu tham khảo, mặc dù.

int f() 
{ 
    int x = 5; 
    x += 10; 
    return x; 
} 

.

int f() 
{ 
    int x = 5; 
    int & y = x; 
    y += 10; 
    return y; 
} 

Assembler:

movl $15, %eax 
ret 
+1

Không chắc chắn đây là một thử nghiệm hợp lý, bởi vì khi kết quả của bạn hiển thị, toàn bộ phần thân của hàm có thể được đánh giá tại thời gian biên dịch. –

+1

@Daniel Pryden: Nhưng đó là mã được hỏi về. Bạn có thể đề xuất một thử nghiệm công bằng hơn về mã không? –

+0

Tôi nghĩ rằng thử nghiệm của tôi là một chút công bằng hơn, bởi vì trình biên dịch không đánh giá cơ thể tại thời gian biên dịch. (Nhưng tôi đã không kiểm tra cùng một mã). –

2

tôi so 2 chương trình trên GNU/Linux. Chỉ có đầu ra GCC được hiển thị dưới đây, nhưng kết quả kêu vang dẫn đến kết luận giống hệt nhau.

phiên bản GCC:4.9.2

Clang phiên bản:3.4.2

Các chương trình

1.cpp

#include <stdio.h> 
int main() 
{ 
    int x = 3; 
    printf("%d\n", x); 
    return 0; 
} 

2.cpp

#include <stdio.h> 
int main() 
{ 
    int x = 3; 
    int & y = x; 
    printf("%d\n", y); 
    return 0; 
} 

Các thử nghiệm

Cố gắng 1: Không tối ưu hóa

gcc -S --std=c++11 1.cpp

gcc -S --std=c++11 2.cpp

lắp ráp kết quả 1.cpp là ngắn hơn.

Cố gắng 2: tối ưu hóa trên

gcc -S -O2 --std=c++11 1.cpp

gcc -S -O2 --std=c++11 2.cpp

Việc lắp ráp kết quả là hoàn toàn giống hệt nhau.

Sản lượng lắp ráp

1.cpp, không tối ưu hóa

.file "1.cpp" 
    .section .rodata 
.LC0: 
    .string "%d\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    subq $16, %rsp 
    movl $3, -4(%rbp) 
    movl -4(%rbp), %eax 
    movl %eax, %esi 
    movl $.LC0, %edi 
    movl $0, %eax 
    call printf 
    movl $0, %eax 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (Debian 4.9.2-10) 4.9.2" 
    .section .note.GNU-stack,"",@progbits 

2.cpp, không tối ưu hóa

.file "2.cpp" 
    .section .rodata 
.LC0: 
    .string "%d\n" 
    .text 
    .globl main 
    .type main, @function 
main: 
.LFB0: 
    .cfi_startproc 
    pushq %rbp 
    .cfi_def_cfa_offset 16 
    .cfi_offset 6, -16 
    movq %rsp, %rbp 
    .cfi_def_cfa_register 6 
    subq $16, %rsp 
    movl $3, -12(%rbp) 
    leaq -12(%rbp), %rax 
    movq %rax, -8(%rbp) 
    movq -8(%rbp), %rax 
    movl (%rax), %eax 
    movl %eax, %esi 
    movl $.LC0, %edi 
    movl $0, %eax 
    call printf 
    movl $0, %eax 
    leave 
    .cfi_def_cfa 7, 8 
    ret 
    .cfi_endproc 
.LFE0: 
    .size main, .-main 
    .ident "GCC: (Debian 4.9.2-10) 4.9.2" 
    .section .note.GNU-stack,"",@progbits 

1.cpp, với tối ưu hóa

.file "1.cpp" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "%d\n" 
    .section .text.unlikely,"ax",@progbits 
.LCOLDB1: 
    .section .text.startup,"ax",@progbits 
.LHOTB1: 
    .p2align 4,,15 
    .globl main 
    .type main, @function 
main: 
.LFB12: 
    .cfi_startproc 
    subq $8, %rsp 
    .cfi_def_cfa_offset 16 
    movl $3, %esi 
    movl $.LC0, %edi 
    xorl %eax, %eax 
    call printf 
    xorl %eax, %eax 
    addq $8, %rsp 
    .cfi_def_cfa_offset 8 
    ret 
    .cfi_endproc 
.LFE12: 
    .size main, .-main 
    .section .text.unlikely 
.LCOLDE1: 
    .section .text.startup 
.LHOTE1: 
    .ident "GCC: (Debian 4.9.2-10) 4.9.2" 
    .section .note.GNU-stack,"",@progbits 

2.cpp, với tối ưu hóa

.file "1.cpp" 
    .section .rodata.str1.1,"aMS",@progbits,1 
.LC0: 
    .string "%d\n" 
    .section .text.unlikely,"ax",@progbits 
.LCOLDB1: 
    .section .text.startup,"ax",@progbits 
.LHOTB1: 
    .p2align 4,,15 
    .globl main 
    .type main, @function 
main: 
.LFB12: 
    .cfi_startproc 
    subq $8, %rsp 
    .cfi_def_cfa_offset 16 
    movl $3, %esi 
    movl $.LC0, %edi 
    xorl %eax, %eax 
    call printf 
    xorl %eax, %eax 
    addq $8, %rsp 
    .cfi_def_cfa_offset 8 
    ret 
    .cfi_endproc 
.LFE12: 
    .size main, .-main 
    .section .text.unlikely 
.LCOLDE1: 
    .section .text.startup 
.LHOTE1: 
    .ident "GCC: (Debian 4.9.2-10) 4.9.2" 
    .section .note.GNU-stack,"",@progbits 

Kết luận

Không có chi phí thời gian chạy khi nói đến sản lượng GCC được tối ưu hóa. Tương tự với clang (thử nghiệm với phiên bản 3.4.2): khi tối ưu hóa được bật, mã assembly được tạo giống hệt nhau trong cả hai chương trình.

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