มีการเรียกฟังก์ชันซ้อนกันมากเกินไปจำนวนเท่าใด?


15

อ้างจากMSDN เกี่ยวกับ StackOverflowException :

ข้อยกเว้นที่ถูกส่งออกมาเมื่อการดำเนินการสแต็คล้นเพราะมันมีการเรียกวิธีการซ้อนกันมากเกินไป

Too manyที่นี่ค่อนข้างคลุมเครือ ฉันจะรู้ได้อย่างไรว่าเมื่อใดมากเกินไปมากเกินไปจริง ๆ ? การเรียกใช้ฟังก์ชันหลายพันครั้ง? ล้าน? ฉันคิดว่ามันต้องเกี่ยวข้องกับปริมาณของหน่วยความจำในคอมพิวเตอร์ แต่เป็นไปได้ไหมที่จะมีขนาดของลำดับความแม่นยำที่ถูกต้อง?

ฉันกังวลเกี่ยวกับเรื่องนี้เพราะฉันกำลังพัฒนาโครงการที่เกี่ยวข้องกับการใช้โครงสร้างแบบเรียกซ้ำและการเรียกใช้ฟังก์ชันแบบเรียกซ้ำ ฉันไม่ต้องการให้แอปพลิเคชันล้มเหลวเมื่อฉันเริ่มใช้งานมากกว่าการทดสอบเล็กน้อย


4
มันค่อนข้างง่ายที่จะแปลงฟังก์ชั่นเวียนเกิดเป็นฟังก์ชั่นที่ไม่เกิดซ้ำ Stack<T>เพียงแค่ติดข้อมูลของคุณบน
Brian

1
ขนาด "ใหญ่เกินไป" นั้นใหญ่แค่ไหนขึ้นอยู่กับคุณที่จะกำหนด สำหรับ NET editbin /stack:WHATEVER-NUMBER-YOU-LIKE yourexefile.exeใช้
SK-logic

คำตอบ:


28

ฉันกังวลเกี่ยวกับเรื่องนี้เพราะฉันกำลังพัฒนาโครงการที่เกี่ยวข้องกับการใช้โครงสร้างแบบเรียกซ้ำและการเรียกใช้ฟังก์ชันแบบเรียกซ้ำ ฉันไม่ต้องการให้แอปพลิเคชันล้มเหลวเมื่อฉันเริ่มใช้งานมากกว่าการทดสอบเล็กน้อย

หากสภาพแวดล้อมภาษาของคุณรองรับการปรับแต่งtail call (และการเรียกซ้ำของคุณคือ tail call) กฎพื้นฐานของหัวแม่มือคือ: รับประกันความลึกของการเรียกซ้ำเป็น O (log n) เช่นการใช้อัลกอริทึมหรือโครงสร้างข้อมูลตามการหารและ พิชิต (เช่นต้นไม้ alogorithms การเรียงลำดับส่วนใหญ่ ฯลฯ ) ก็โอเค แต่สิ่งที่เป็นเส้นตรง (เช่นการใช้งานแบบซ้ำของการจัดการรายการที่เชื่อมโยง) ไม่ได้


3
+1 คำตอบที่ดีมากฉันใช้กฎนี้โดยปริยาย แต่ฉันไม่ได้ตระหนักถึงมัน
Giorgio

ในยุคนี้ผู้เรียบเรียงคุณภาพการผลิตที่เหมาะสมกับวลีของคำคุณศัพท์จะสนับสนุนการเพิ่มประสิทธิภาพการโทรหาง
John R. Strohm

1
@ JohnR.Strohm อย่างไรก็ตาม OP ได้ติดแท็กคำถาม. NET ดังนั้น AFAIK เฉพาะjitter 64 บิตเท่านั้นที่จะทำการปรับการโทรให้เหมาะสมที่สุดในขณะนี้
Mark Hurd

1
ดังนั้นจึงไม่มีความสับสนทั้ง x86 และ x64 ต่างก็ให้เกียรติหาง "CIL" เสมอ คำนำหน้าคำแนะนำ ดังนั้นเพื่อสรุปใน. NET land, F # จะทำการปรับการโทรให้สมบูรณ์แบบสมบูรณ์, C # ไม่ได้และ x64 jitter จะทำถ้ามันเป็น "ง่าย" (ไม่สามารถขึ้นอยู่กับมันได้) ดูblogs.msdn.com/b/clrcodegeneration/archive/2009/05/11/…
Stephen Swensen

1
จริง แต่ในบางกรณีเช่นใน IIS โฮสติ้ง ASP.NET ที่น่าอับอายแม้แต่ความลึกการเรียกซ้ำเพียงแค่ O (log n) อาจล้มเหลวเนื่องจากข้อ จำกัด เชิงลึกที่น่าสมเพชไม่ยุติธรรมและน่าหัวเราะซึ่งทำให้การเรียกซ้ำ (หรือ) แม้แต่สายยาวของการโทรซ้อนซ้อนที่ไม่เกิดซ้ำ) เป็นไปไม่ได้ วิธีเดียวที่จะแก้ไขคือ bin ไบนารีของตัวเอง
SK-logic

17

ตามค่าเริ่มต้น CLR จะจัดสรร 1 MB ให้กับสแต็กสำหรับแต่ละเธรด (ดูบทความนี้ ) ดังนั้นการโทรหลายครั้งจึงใช้เวลาเกินจำนวนเงินนี้ ซึ่งจะแตกต่างกันไปขึ้นอยู่กับจำนวนพื้นที่บนสแต็กแต่ละครั้งที่ใช้สำหรับสิ่งต่าง ๆ เช่นพารามิเตอร์และตัวแปรโลคัล

คุณสามารถStackOverflowExceptionโทรออกด้วยการโทรเพียงครั้งเดียวหากคุณเต็มใจที่จะทำสิ่งที่แหวกแนว:

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

น่าเสียดายที่ค่าเริ่มต้นที่สมเหตุสมผลนี้มักถูกละเมิด (เช่นใน asp.net)
SK-logic

2

ตั้งแต่Cole Campbellบันทึกขนาดหน่วยความจำและMichael Borgwardtบันทึกการเพิ่มประสิทธิภาพการโทรหางฉันจะไม่ครอบคลุมสิ่งเหล่านั้น

สิ่งที่ควรระวังอีกอย่างคือCPSซึ่งสามารถใช้เพื่อปรับฟังก์ชั่น interwoven หลายฟังก์ชั่นให้เหมาะที่สุดสำหรับฟังก์ชั่นเดี่ยว

คุณสามารถเพิ่มขนาดของสแต็กตามที่เราทำที่นี่และโปรดทราบว่ารหัส 64 บิตกินสแต็กเร็วกว่ารหัส 32 บิต

สิ่งที่ควรทราบคือเรารันหนึ่งในตัวอย่างภายใต้ F # แบบโต้ตอบมานานกว่า 40 ชั่วโมงโดยไม่ทำให้เกิดกองซ้อน ใช่มันเป็นการเรียกฟังก์ชั่นเดียวที่วิ่งด้วยตัวเองอย่างต่อเนื่องเพื่อความสำเร็จ

นอกจากนี้หากคุณจำเป็นต้องทำการครอบคลุมโค้ดเพื่อพิจารณาว่าปัญหาเกิดขึ้นที่ใดและไม่มีการครอบคลุมโค้ดด้วย VS คุณสามารถใช้TestDriven.NET

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.