2016-03-21 11 views
5

Trong câu trả lời đầu tiên here, sau đây đã được đề cập về bộ nhớ stack trong C++:C++: Trình biên dịch biết bao nhiêu bộ nhớ để cấp phát cho mỗi khung ngăn xếp?

Khi một hàm được gọi, một khối được dành riêng trên đỉnh của ngăn xếp cho các biến địa phương và một số dữ liệu sổ sách kế toán.

Điều này làm cho cảm giác hoàn hảo trên-cấp cao nhất, và làm cho tôi tò mò về cách trình biên dịch thông minh được khi phân bổ bộ nhớ này trong và của chính nó, trong bối cảnh this question: Kể từ khi niềng răng mình không phải là một stack frame trong C (Tôi cho rằng điều này cũng đúng với C++), tôi muốn kiểm tra xem các trình biên dịch có tối ưu bộ nhớ được dành riêng dựa trên các phạm vi biến trong một hàm duy nhất hay không.

Sau đây tôi giả định rằng chồng trông như thế này trước khi gọi hàm:

-------- 
|main()| 
-------- <- stack pointer: space above it is used for current scope 
|  | 
|  | 
|  | 
|  | 
-------- 

Và rồi sau khi gọi một hàm f():

-------- 
|main()| 
-------- <- old stack pointer (osp) 
| f() | 
-------- <- stack pointer, variables will now be placed between here and osp upon reaching their declarations 
|  | 
|  | 
|  | 
|  | 
-------- 

Ví dụ, đưa ra chức năng này

void f() { 
    int x = 0; 
    int y = 5; 
    int z = x + y; 
} 

Có lẽ, điều này sẽ chỉ phân bổ 3*sizeof(int) + một số chi phí bổ sung để ghi sổ kế toán.

Tuy nhiên, những gì về chức năng này:

void g() { 
    for (int i = 0; i < 100000; i++) { 
    int x = 0; 
    } 
    { 
    MyObject myObject[1000]; 
    } 
    { 
    MyObject myObject[1000]; 
    } 
} 

Bỏ qua tối ưu hóa trình biên dịch có thể bõ mẫu âm chót rất nhiều thứ ở trên kể từ khi thực sự họ không làm gì cả, tôi tò mò về sau trong ví dụ thứ hai:

  • Đối với vòng lặp for: không gian ngăn xếp đủ lớn để phù hợp với tất cả 100000 int không?
  • Trên hết, vùng ngăn xếp có chứa 1000*sizeof(MyObject) hoặc 2000*sizeof(MyObject) không?

Nói chung: trình biên dịch có tính đến phạm vi biến đổi khi xác định dung lượng bộ nhớ cần cho khung ngăn xếp mới, trước khi gọi hàm nhất định không? Nếu đây là trình biên dịch cụ thể, làm thế nào để một số trình biên dịch nổi tiếng làm điều đó?

+3

Một cặp '{}' là một phạm vi. Vòng lặp reuses cùng một bộ nhớ cho 'x', và hai mảng' myObject' không tồn tại cùng một lúc. – LogicStuff

+1

Tại sao cần phân bổ không gian cho '100000' ints, khi nó có thể tái sử dụng cùng một không gian? Cũng vậy với mảng. –

+1

Trình biên dịch kiểm tra từng phạm vi chức năng và không gian dành riêng là tối đa không gian của tất cả các phạm vi có thể tồn tại cùng một lúc. –

Trả lời

4

Trình biên dịch sẽ phân bổ không gian khi cần thiết (thường cho tất cả các mục ở đầu hàm), nhưng không phân bổ cho mỗi lần lặp trong vòng lặp.

Ví dụ, những gì Clang sản xuất, như LLVM-IR

define void @_Z1gv() #0 { 
    %i = alloca i32, align 4 
    %x = alloca i32, align 4 
    %myObject = alloca [1000 x %class.MyObject], align 16 
    %myObject1 = alloca [1000 x %class.MyObject], align 16 
    store i32 0, i32* %i, align 4 
    br label %1 

; <label>:1:          ; preds = %5, %0 
    %2 = load i32, i32* %i, align 4 
    %3 = icmp slt i32 %2, 100000 
    br i1 %3, label %4, label %8 

; <label>:4:          ; preds = %1 
    store i32 0, i32* %x, align 4 
    br label %5 

; <label>:5:          ; preds = %4 
    %6 = load i32, i32* %i, align 4 
    %7 = add nsw i32 %6, 1 
    store i32 %7, i32* %i, align 4 
    br label %1 

; <label>:8:          ; preds = %1 
    ret void 
} 

Đây là kết quả của:

class MyObject 
{ 
public: 
    int x, y; 
}; 

void g() { 
    for (int i = 0; i < 100000; i++) 
    { 
    int x = 0; 
    } 
    { 
    MyObject myObject[1000]; 
    } 
    { 
    MyObject myObject[1000]; 
    } 
} 

Vì vậy, như bạn thấy, x được phân bổ một lần duy nhất, không 100000 lần. Bởi vì chỉ MỘT trong số các biến đó sẽ tồn tại tại bất kỳ thời điểm nào.

(Trình biên dịch có thể tái sử dụng không gian cho myObject[1000] cho x và thứ hai myObject[1000] - và có lẽ sẽ làm như vậy cho một xây dựng được tối ưu hóa, nhưng trong trường hợp đó nó cũng sẽ loại bỏ hoàn toàn các biến này khi họ không được sử dụng, vì vậy nó wouldn Không hiển thị tốt)

+0

Và trong điều kiện của con trỏ ngăn xếp: nó sẽ chỉ được tăng lên bởi 'tối đa (2 * sizeof (int), 1000 * sizeof (MyObject))' khi đạt 'g()'? Vì chỉ những biến đó có thể tồn tại cùng một lúc. Tôi không nghĩ rằng đó là rõ ràng từ hội đồng. – Jimmy

+0

Rất có thể, có, nhưng nó có thể là tổng của tất cả các biến cục bộ - gần như chắc chắn là trong một xây dựng không được tối ưu hóa [đó là mã của tôi hiển thị] –

+0

Tất nhiên, trong một bản dựng tối ưu 'i' và' x 'rất có thể sẽ nằm trong các thanh ghi thay vì trên stack. –

2

Trong trình biên dịch hiện đại, hàm này được chuyển thành biểu đồ luồng đầu tiên. Trong mỗi vòng cung của luồng, trình biên dịch biết có bao nhiêu biến là sống - đó là để nói rằng giữ một giá trị hiển thị. Một số trong số đó sẽ sống trong thanh ghi, và đối với các trình biên dịch khác, trình biên dịch sẽ cần phải dự trữ không gian ngăn xếp.

Mọi thứ trở nên phức tạp hơn một chút vì trình tối ưu hóa được tham gia nhiều hơn, vì nó có thể không muốn di chuyển các biến ngăn xếp xung quanh. Đó không phải là miễn phí.

Tuy nhiên, cuối cùng trình biên dịch có tất cả các hoạt động lắp ráp sẵn sàng và chỉ có thể đếm số lượng địa chỉ ngăn xếp duy nhất được sử dụng.

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