2013-04-09 26 views
7

Hãy xem xét các chương trình sau đây:Trình biên dịch có được phép tối ưu hóa việc sử dụng bộ nhớ ngăn xếp bằng cách sắp xếp lại các biến cục bộ không?

#include <stdio.h> 

void some_func(char*, int*, char*); 

void stack_alignment(void) { 
    char a = '-'; 
    int i = 1337; 
    char b = '+'; 
    some_func(&a, &i, &b); // to prevent the compiler from removing the local variables 
    printf("%c|%i|%c", a, i, b); 
} 

Nó tạo ra lắp ráp sau (thêm nhận xét của bản thân mình, tôi là một newbie hoàn toàn để lắp ráp):

$ vim stack-alignment.c 
$ gcc -c -S -O3 stack-alignment.c 
$ cat stack-alignment.s 
     .file "stack-alignment.c" 
     .section .rdata,"dr" 
LC0: 
     .ascii "%c|%i|%c\0" 
     .text 
     .p2align 2,,3 
     .globl _stack_alignment 
     .def _stack_alignment;  .scl 2;  .type 32;  .endef 
_stack_alignment: 
LFB7: 
     .cfi_startproc 
     subl $44, %esp 
     .cfi_def_cfa_offset 48 
     movb $45, 26(%esp) // local variable 'a' 
     movl $1337, 28(%esp) // local variable 'i' 
     movb $43, 27(%esp) // local variable 'b' 
     leal 27(%esp), %eax 
     movl %eax, 8(%esp) 
     leal 28(%esp), %eax 
     movl %eax, 4(%esp) 
     leal 26(%esp), %eax 
     movl %eax, (%esp) 
     call _some_func 
     movsbl 27(%esp), %eax 
     movl %eax, 12(%esp) 
     movl 28(%esp), %eax 
     movl %eax, 8(%esp) 
     movsbl 26(%esp), %eax 
     movl %eax, 4(%esp) 
     movl $LC0, (%esp) 
     call _printf 
     addl $44, %esp 
     .cfi_def_cfa_offset 4 
     ret 
     .cfi_endproc 
LFE7: 
     .def _some_func;  .scl 2;  .type 32;  .endef 
     .def _printf;  .scl 2;  .type 32;  .endef 

Như bạn thấy có 3 địa phương biến số (a, ib) với kích thước 1 byte, 4 byte và 1 byte. Bao gồm padding này sẽ là 12 byte (giả sử trình biên dịch căn chỉnh đến 4 byte).

Sẽ không hiệu quả hơn nếu trình biên dịch thay đổi thứ tự của các biến thành (a, b, i)? Sau đó, chỉ cần 8 byte là cần thiết.

Dưới đây là một "đồ họa" đại diện:

3 bytes unused     3 bytes unused 
    vvvvvvvvvvv      vvvvvvvvvvv 
+---+---+---+---+---+---+---+---+---+---+---+---+ 
| a | | | | i    | b | | | | 
+---+---+---+---+---+---+---+---+---+---+---+---+ 

       | 
       v 

+---+---+---+---+---+---+---+---+ 
| a | b | | | i    | 
+---+---+---+---+---+---+---+---+ 
     ^^^^^^^ 
     2 bytes unused 

là trình biên dịch cho phép để làm tối ưu hóa này (theo tiêu chuẩn, vv C)?

  • Nếu không (như tôi nghĩ đầu ra lắp ráp hiển thị), tại sao?
  • Nếu có, tại sao điều đó không xảy ra ở trên?
+0

Giả sử nó được cho phép theo các tiêu chuẩn, vv, thì nó sẽ hoàn toàn phụ thuộc vào việc thực thi trình biên dịch riêng lẻ cho dù chúng có thực hiện hay không. Tôi tưởng tượng nó sẽ được kiểm soát bởi các mức tối ưu hóa tại thời gian biên dịch. – John3136

+0

Trình biên dịch/trình tối ưu hóa là miễn phí để đặt người dân địa phương ở bất cứ nơi nào họ muốn, miễn là nó không phá vỡ chương trình. Hoàn toàn miễn phí để đặt hai biến trong cùng một vị trí nếu chắc chắn chúng không bao giờ được sử dụng cùng một lúc. – mah

+0

Bạn đã cố gắng biên dịch với các tùy chọn tối ưu hóa khác nhau chưa? Có lẽ bạn đã biên dịch với tối ưu hóa tắt. –

Trả lời

4

Trình biên dịch có được phép thực hiện tối ưu hóa này (theo tiêu chuẩn C, v.v.) không?

Có.

Nếu có, tại sao điều đó không xảy ra ở trên?

Điều đó đã xảy ra.

Đọc kỹ đầu ra của bộ lắp ráp.

movb $45, 26(%esp) // local variable 'a' 
    movl $1337, 28(%esp) // local variable 'i' 
    movb $43, 27(%esp) // local variable 'b' 

Biến a là bù đắp 26. Biến b là bù đắp 27. Biến i là bù đắp 28.

Sử dụng những hình ảnh bạn đã thực hiện bố trí hiện nay:

+---+---+---+---+---+---+---+---+ 
| | | a | b | i    | 
+---+---+---+---+---+---+---+---+ 
^^^^^^^ 
2 bytes unused 
+0

Tôi thực sự không chú ý đến số trong đối số thứ hai của lệnh mov (l | b)>. <, Cảm ơn vì điều đó. Vâng, vẫn vui tại sao gcc sắp xếp các lệnh này theo thứ tự này, nó sẽ dễ dàng hơn để chú ý nếu nó là một cách khác. – MarcDefiant

+1

Không biết tại sao các hướng dẫn được đặt hàng theo cách đó. Nếu tôi phải suy đoán hoặc là điều gì đó về hai hướng dẫn 'movb' chạm vào cùng một từ bộ nhớ có thể ngăn chặn (mặc dù chúng ta lưu trữ bộ đệm vì một lý do, vì vậy điều này là không chắc) hoặc nhiều khả năng hơn: nó không quan trọng, vì vậy chúng được tạo ra theo thứ tự trình biên dịch xảy ra để có chúng trong cây cú pháp nội bộ. – Art

7

Trình biên dịch được tự do bố cục các biến cục bộ theo ý muốn. Nó thậm chí không cần sử dụng ngăn xếp.

Nó có thể lưu trữ các biến cục bộ theo thứ tự không liên quan đến thứ tự khai báo trên ngăn xếp nếu nó sử dụng ngăn xếp.

Trình biên dịch có được phép thực hiện tối ưu hóa này (theo tiêu chuẩn C, v.v.) không?

  • Nếu có, tại sao điều đó không xảy ra ở trên?

Vâng, đây có phải là tối ưu hóa không?

Điều đó không rõ ràng. Nó sử dụng một vài byte ít hơn, nhưng điều đó hiếm khi có vấn đề. Nhưng trên một số kiến ​​trúc, có thể đọc nhanh hơn char nếu nó được lưu trữ từ liên kết. Vì vậy, sau đó đặt char s bên cạnh nhau sẽ buộc một trong số chúng ít nhất là không được căn chỉnh từ và làm cho nó đọc chậm hơn.

+1

Điều này không trả lời đầy đủ câu hỏi. Một vài byte trên stack cuộc gọi có thể quan trọng khi thực hiện một thuật toán đệ quy. C là ngôn ngữ lập trình hệ thống, do đó, tiết kiệm bộ nhớ nếu có thể * là * một mối quan tâm. Câu hỏi đặt ra là liệu GCC có bỏ lỡ cơ hội tối ưu hóa hay đang chọn một mặt của một sự cân bằng. Nó gần như chắc chắn đúng là trên một số kiến ​​trúc * nó nhanh hơn để đọc một 'char' từ một vị trí phù hợp; nhưng lắp ráp được tạo ra cho một kiến ​​trúc cụ thể. Nếu kiến ​​trúc này không áp đặt hình phạt cho các lần đọc chưa được ký, thì tối ưu hóa bị thiếu. – user4815162342

0

Nói chung trong các hệ thống bình thường có vấn đề về tốc độ, đọc từ khôn ngoan nhanh hơn đọc ký tự khôn ngoan. Mất trí nhớ so với tăng tốc được bỏ qua. Nhưng trong trường hợp hệ thống có vấn đề về bộ nhớ, giống như trong các trình biên dịch chéo khác nhau, có thể thực thi được (theo nghĩa rất chung) cho một nền tảng đích cụ thể, hình ảnh có thể hoàn toàn khác. Trình biên dịch có thể gói chúng lại với nhau, thậm chí kiểm tra tuổi thọ và sử dụng của chúng, tùy thuộc vào việc giảm bitwidth, vv vv Vì vậy, về cơ bản nó phụ thuộc rất nhiều vào sự cần thiết. Nhưng nói chung tất cả các trình biên dịch mang lại cho bạn sự linh hoạt nếu bạn muốn "pack" chúng chặt chẽ.Bạn có thể nhìn vào hướng dẫn cho rằng

2

Nó sẽ không thể nhớ hiệu quả hơn nếu trình biên dịch sẽ thay đổi thứ tự của các biến

Không có cách nào để nói mà không nói về một CPU cụ thể, một hệ điều hành cụ thể và một trình biên dịch cụ thể. Nói chung, trình biên dịch tối ưu. Để tối ưu hóa mã theo cách có ý nghĩa, bạn cần kiến ​​thức chuyên sâu về hệ thống cụ thể.

Trong trường hợp của bạn, trình biên dịch có thể được đặt để tối ưu hóa cho tốc độ trong trường hợp này. Dường như trình biên dịch đã quyết định rằng các địa chỉ liên kết cho mỗi biến cho mã hiệu quả nhất. Trên một số hệ thống, nó không chỉ nhanh hơn, mà còn bắt buộc phải phân bổ tại các địa chỉ thậm chí, bởi vì một số CPU chỉ có thể xử lý truy cập được căn chỉnh.

Trình biên dịch có được phép thực hiện tối ưu hóa này (theo tiêu chuẩn C, v.v.) không?

Có, tiêu chuẩn C thậm chí không yêu cầu các biến được phân bổ. Trình biên dịch hoàn toàn miễn phí để xử lý điều này theo bất kỳ cách nào nó muốn và nó không cần phải ghi lại cách thức hoặc lý do. Nó có thể phân bổ các biến bất cứ nơi nào, nó có thể tối ưu hóa chúng hoàn toàn, hoặc phân bổ chúng bên trong thanh ghi CPU, hoặc trên ngăn xếp, hoặc trong một hộp gỗ nhỏ bên dưới bàn làm việc của bạn.

0

Trình biên dịch có bảo vệ tràn bộ đệm cho ngăn xếp (/GS cho trình biên dịch của Microsoft) có thể sắp xếp lại các biến làm tính năng bảo mật. Ví dụ, nếu các biến cục bộ của bạn là một mảng char có kích thước không đổi (buffer) và một con trỏ hàm, kẻ tấn công có thể tràn bộ đệm cũng có thể ghi đè lên con trỏ hàm. Do đó, các biến cục bộ được sắp xếp lại sao cho vùng đệm nằm cạnh con chim hoàng yến. Bằng cách này, kẻ tấn công có thể không (trực tiếp) thỏa hiệp con trỏ hàm và tràn bộ đệm (hy vọng) được phát hiện bởi con chim yến bị phá hủy.

Cảnh báo: Các tính năng như vậy không ngăn chặn sự thỏa hiệp, chúng chỉ tăng rào cản cho kẻ tấn công, nhưng kẻ tấn công có kỹ năng thường tìm đường xung quanh.

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