Có bao nhiêu cuộc gọi hàm quá nhiều lồng nhau?


15

Trích dẫn từ MSDN về StackOverflowException :

Ngoại lệ được ném khi ngăn xếp thực thi tràn vì nó chứa quá nhiều lệnh gọi phương thức lồng nhau.

Too manyở đây khá mơ hồ. Làm thế nào để tôi biết khi quá nhiều thực sự là quá nhiều? Hàng ngàn chức năng gọi? Hàng triệu? Tôi giả định rằng nó phải liên quan theo một cách nào đó đến dung lượng bộ nhớ trong máy tính nhưng liệu có thể đưa ra một thứ tự độ lớn gần như chính xác?

Tôi lo ngại về điều này bởi vì tôi đang phát triển một dự án liên quan đến việc sử dụng nhiều cấu trúc đệ quy và các lệnh gọi hàm đệ quy. Tôi không muốn ứng dụng bị lỗi khi tôi bắt đầu sử dụng nó không chỉ là các thử nghiệm nhỏ.


4
Việc chuyển đổi một hàm đệ quy thành một hàm không đệ quy tương đối dễ dàng. Chỉ cần dán dữ liệu của bạn vào a Stack<T>.
Brian

1
Làm thế nào lớn là "quá nhiều" là hoàn toàn tùy thuộc vào bạn để xác định. Đối với .NET, sử dụng editbin /stack:WHATEVER-NUMBER-YOU-LIKE yourexefile.exe.
SK-logic

Câu trả lời:


28

Tôi lo ngại về điều này bởi vì tôi đang phát triển một dự án liên quan đến việc sử dụng nhiều cấu trúc đệ quy và các lệnh gọi hàm đệ quy. Tôi không muốn ứng dụng bị lỗi khi tôi bắt đầu sử dụng nó không chỉ là các thử nghiệm nhỏ.

Trừ khi môi trường ngôn ngữ của bạn hỗ trợ tối ưu hóa cuộc gọi đuôi (và đệ quy của bạn cuộc gọi đuôi), một nguyên tắc cơ bản là: độ sâu đệ quy phải được đảm bảo là O (log n), tức là sử dụng thuật toán hoặc cấu trúc dữ liệu dựa trên phân chia và chinh phục (như cây, hầu hết các trường hợp sắp xếp, v.v.) là OK, nhưng bất cứ điều gì tuyến tính (như triển khai đệ quy của xử lý danh sách liên kết) thì không.


3
+1. Câu trả lời rất hay, tôi đã ngầm sử dụng quy tắc này nhưng tôi không nhận thức được nó.
Giorgio

Trong thời đại ngày nay, bất kỳ trình biên dịch chất lượng sản xuất nào xứng đáng với cụm tính từ sẽ hỗ trợ tối ưu hóa cuộc gọi đuôi.
John R. Strohm

1
@ JohnR.Strohm Tuy nhiên, OP đã gắn thẻ câu hỏi .NET, vì vậy AFAIK chỉ có jitter 64 bit thực hiện tối ưu hóa cuộc gọi đuôi vào lúc này.
Đánh dấu Hurd

1
Không có gì nhầm lẫn, cả x86 và x64 luôn tôn vinh "đuôi" của CIL. tiền tố hướng dẫn. Vì vậy, để tóm tắt, trong vùng đất .NET, F # hoàn thành tối ưu hóa cuộc gọi đuôi, C # không và jitter x64 sẽ làm nếu nó "dễ dàng" (không thể phụ thuộc vào). Xem blog.msdn.com/b/clrcodegeneration/archive/2009/05/11/ Kẻ
Stephen Swensen

1
Đúng, nhưng trong một số trường hợp, như trong một máy chủ lưu trữ IIS khét tiếng, ngay cả độ sâu đệ quy O (log n) đơn thuần cũng có thể thất bại vì giới hạn độ sâu ngăn xếp nhỏ xíu, không chính đáng, gây cười của chúng, điều này thể hiện khá nhiều bất kỳ đệ quy nào (hoặc thậm chí một chuỗi dài các cuộc gọi lồng nhau không đệ quy) không thể. Cách duy nhất là chỉnh sửa bản nhị phân iis.
SK-logic

17

Theo mặc định, CLR phân bổ 1 MB cho ngăn xếp cho mỗi luồng (xem bài viết này ). Vì vậy, tuy nhiên nhiều cuộc gọi cần phải vượt quá số tiền này. Điều đó sẽ thay đổi tùy thuộc vào lượng không gian trên ngăn xếp mà mỗi cuộc gọi đang sử dụng cho những thứ như tham số và biến cục bộ.

Bạn thậm chí có thể thực hiện StackOverflowExceptioncuộc gọi bằng một cuộc gọi duy nhất nếu bạn muốn có một chút không chính thống:

private static unsafe void BlowUpTheStack()
{
    var x = stackalloc byte[1000000000];
    Console.WriteLine("Oh no, I've blown up the stack!");
}

Thật không may, mặc định hợp lý này thường bị vi phạm (ví dụ, trong asp.net).
SK-logic

2

Cole Campbell lưu ý kích thước bộ nhớMichael Borgwardt lưu ý tối ưu hóa cuộc gọi đuôi, tôi sẽ không bao gồm những điều đó.

Một điều khác cần lưu ý là CPS có thể được sử dụng để tối ưu hóa một số chức năng đan xen trong đó tối ưu hóa cuộc gọi đuôi là cho các chức năng đơn lẻ.

Bạn có thể tăng kích thước của ngăn xếp như chúng tôi đã làm ở đây và lưu ý rằng mã 64 bit ăn ngăn xếp nhanh hơn mã 32 bit.

Đáng chú ý là chúng tôi đã chạy một trong các ví dụ trong tương tác F # trong hơn 40 giờ mà không thổi tung ngăn xếp. Vâng, đó là một cuộc gọi chức năng chạy liên tục để hoàn thành thành công.

Ngoài ra, nếu bạn cần thực hiện bảo hiểm mã để tìm ra nơi xảy ra sự cố và không có phạm vi bảo hiểm mã với VS bạn có thể sử dụng, TestDriven.NET

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.