กองซ้อนเป็นวิธีเดียวที่สมเหตุสมผลในการจัดโครงสร้างโปรแกรมหรือไม่


74

สถาปัตยกรรมส่วนใหญ่ที่ฉันเคยเห็นต้องใช้ call stack เพื่อบันทึก / เรียกคืนบริบทก่อนการเรียกใช้ฟังก์ชัน มันเป็นกระบวนทัศน์ทั่วไปที่การดำเนินงานแบบพุชและป๊อปถูกติดตั้งไว้ในโปรเซสเซอร์ส่วนใหญ่ มีระบบที่ทำงานโดยไม่มี stack หรือไม่? ถ้าเป็นเช่นนั้นทำงานอย่างไรและใช้ทำอะไร


5
กำหนดวิธีการฟังก์ชั่นที่คาดว่าจะประพฤติตนอยู่ในภาษา C-ชอบ (เช่นคุณรังสามารถเรียกร้องให้ลึกที่สุดเท่าที่คุณต้องการและสามารถกลับออกมาในลำดับที่กลับ) ก็ไม่ชัดเจนให้ฉันวิธีอื่นอย่างใดอย่างหนึ่งสามารถใช้ฟังก์ชั่นการโทรโดยไม่ได้ถูกอย่างไม่น่าเชื่อ ไม่มีประสิทธิภาพ คุณสามารถบังคับให้โปรแกรมเมอร์ใช้รูปแบบการส่งต่อหรือรูปแบบการเขียนโปรแกรมที่แปลกประหลาด แต่ดูเหมือนว่าไม่มีใครทำงานกับ CPS ได้จริงในระดับต่ำมากด้วยเหตุผลบางประการ
Kevin

5
GLSL ทำงานได้โดยไม่มีสแต็ก (เช่นภาษาอื่น ๆ ในวงเล็บเฉพาะนั้น) มันไม่อนุญาตให้โทรซ้ำ
Leushenko

3
คุณอาจต้องการดูในหน้าต่างการลงทะเบียนซึ่งถูกใช้โดยสถาปัตยกรรม RISC บางตัว
Mark Booth

2
@Kevin: "คอมไพเลอร์ FORTRAN รุ่นแรกไม่สนับสนุนการเรียกซ้ำในรูทีนย่อยสถาปัตยกรรมคอมพิวเตอร์ยุคแรกไม่สนับสนุนแนวคิดของสแต็กและเมื่อพวกเขาสนับสนุนการเรียกรูทีนย่อยโดยตรงตำแหน่งที่ส่งคืนมักจะถูกเก็บไว้ ไม่อนุญาตให้เรียกใช้รูทีนย่อยอีกครั้งก่อนที่จะเรียกรูทีนย่อยก่อนกลับมาแม้ว่าจะไม่ได้ระบุใน Fortran 77 คอมไพเลอร์ F77 จำนวนมากสนับสนุนการเรียกซ้ำเป็นตัวเลือก en.wikipedia.org/wiki/Fortran#FORTRAN_II
Mooing Duck

3
ไมโครคอนโทรลเลอร์ P8X32A ("ใบพัด") ไม่มีแนวคิดของสแต็กในภาษาแอสเซมบลีมาตรฐาน (PASM) คำแนะนำที่รับผิดชอบในการกระโดดยังแก้ไขคำสั่งการส่งคืนใน RAM ด้วยตนเองเพื่อกำหนดตำแหน่งที่จะกลับไป - ซึ่งสามารถเลือกได้เอง น่าสนใจภาษา "สปิน" (ภาษาระดับสูงตีความที่ทำงานบนชิปเดียวกัน) จะมีความหมายสแต็คแบบดั้งเดิม
Wossname

คำตอบ:


50

A (ค่อนข้าง) ทางเลือกที่นิยมสแต็คโทรต

ตัวอย่างเช่น Parrot VM นั้นเป็นแบบต่อเนื่อง มันไม่มีข้อมูลที่สมบูรณ์: ข้อมูลถูกเก็บไว้ในรีจิสเตอร์ (เช่น Dalvik หรือ LuaVM, Parrot คือการลงทะเบียน) และการควบคุมการไหลจะแสดงด้วยการดำเนินการต่อเนื่อง (ต่างจาก Dalvik หรือ LuaVM ซึ่งมี call stack)

โครงสร้างข้อมูลที่ได้รับความนิยมอีกอย่างหนึ่งที่ใช้โดย Smalltalk และ Lisp VM คือสปาเก็ตตี้ซึ่งมีลักษณะคล้ายกับเครือข่ายสแต็ก

ตามที่@ rwong ชี้ให้เห็นว่าลักษณะการส่งต่อความต่อเนื่องเป็นทางเลือกหนึ่งของ call stack โปรแกรมที่เขียนใน (หรือแปลงเป็น) รูปแบบการส่งผ่านความต่อเนื่องจะไม่ส่งคืนดังนั้นจึงไม่จำเป็นต้องใช้สแต็ก

ตอบคำถามของคุณจากมุมมองที่แตกต่าง: เป็นไปได้ที่จะมีสแต็กการโทรโดยไม่มีสแต็กแยกต่างหากโดยจัดสรรเฟรมสแต็กบนฮีป การใช้ Lisp และ Scheme บางอย่างทำเช่นนี้


4
ขึ้นอยู่กับคำจำกัดความของสแต็กของคุณ ฉันไม่แน่ใจว่าการมีรายการที่เชื่อมโยง (หรืออาเรย์ของตัวชี้ไปยังหรือ ... ) ของสแต็กเฟรมนั้นมาก "ไม่ใช่สแต็ก" เป็น "การแสดงสแต็กที่แตกต่างกัน"; และโปรแกรมในภาษา CPS (ในทางปฏิบัติ) มีแนวโน้มที่จะสร้างรายการที่เชื่อมโยงอย่างมีประสิทธิภาพซึ่งมีลักษณะคล้ายกับสแต็คอย่างมาก (ถ้าคุณยังไม่มีคุณอาจตรวจสอบ GHC ซึ่งจะผลักดันสิ่งที่เรียกว่า เพื่อประสิทธิภาพ)
Jonathan Cast Cast

6
" โปรแกรมที่เขียนใน (หรือแปลงเป็น) รูปแบบการส่งผ่านแบบต่อเนื่องจะไม่กลับมาอีก ...
Rob Penridge

5
@ RobPenridge: มันเป็นความลับเล็กน้อยฉันเห็นด้วย CPS หมายถึงแทนที่จะส่งกลับฟังก์ชันจะใช้เป็นอาร์กิวเมนต์พิเศษเพื่อเรียกใช้เมื่องานเสร็จ ดังนั้นเมื่อคุณเรียกฟังก์ชั่นและคุณมีงานอื่นที่คุณต้องทำหลังจากที่คุณเรียกใช้ฟังก์ชั่นแทนที่จะรอฟังก์ชั่นที่จะกลับมาแล้วดำเนินการต่อกับงานของคุณคุณห่องานที่เหลือ ("ความต่อเนื่อง" ) เป็นฟังก์ชันและส่งผ่านฟังก์ชันนั้นเป็นอาร์กิวเมนต์เพิ่มเติม ฟังก์ชั่นที่คุณโทรแล้วเรียกใช้ฟังก์ชันนั้นแทนการส่งคืนและต่อ ๆ ไปเรื่อย ๆ ไม่มีฟังก์ชั่นที่เคยส่งกลับก็แค่
Jörg W Mittag

3
…เรียกใช้ฟังก์ชันถัดไป ดังนั้นคุณไม่จำเป็นต้องมี call stack เพราะคุณไม่จำเป็นต้องกลับมาและเรียกคืนสถานะการเชื่อมโยงของฟังก์ชันที่เรียกว่าก่อนหน้านี้ แทนที่จะถือครองรัฐที่ผ่านมาเพื่อที่คุณจะได้กลับมาอีกครั้งคุณจะต้องดำเนินการในอนาคตหากคุณต้องการ
Jörg W Mittag

1
@jcast: คุณสมบัติการกำหนดของสแต็กคือ IMO ที่คุณสามารถเข้าถึงองค์ประกอบสูงสุดเท่านั้น รายการของการต่อเนื่อง OTOH จะให้คุณเข้าถึงการต่อเนื่องทั้งหมดไม่ใช่เฉพาะสแต็กเฟรมสูงสุด หากคุณมีข้อยกเว้นที่ทำงานต่อได้ในสไตล์ Smalltalk คุณต้องสามารถสำรวจสแต็กได้โดยไม่ต้องเปิดมัน และมีความต่อเนื่องในภาษาในขณะที่ยังคงต้องการความคิดที่คุ้นเคยของ call stack จะนำไปสู่สปาเก็ตตี้สแต็คซึ่งโดยทั่วไปแล้วจะเป็นต้นไม้ของสแต็ค
Jörg W Mittag

36

ในสมัยก่อนตัวประมวลผลไม่มีคำสั่งสแต็กและภาษาการเขียนโปรแกรมไม่รองรับการเรียกซ้ำ เมื่อเวลาผ่านไปภาษามากขึ้นเลือกที่จะสนับสนุนการเรียกซ้ำและฮาร์ดแวร์ตามด้วยชุดที่มีความสามารถในการจัดสรรสแต็กเฟรม การสนับสนุนนี้มีความหลากหลายในช่วงหลายปีที่ผ่านมาด้วยโปรเซสเซอร์ที่แตกต่างกัน โปรเซสเซอร์บางตัวใช้เฟรมสแต็กและ / หรือตัวชี้สแต็ก คำแนะนำที่นำมาใช้ซึ่งจะทำให้การจัดสรรเฟรมสแต็กสำเร็จในคำสั่งเดียว

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

ในระยะสั้นการใช้งานของสแต็คพัฒนาทั้งในฮาร์ดแวร์และซอฟต์แวร์ มีโมเดลอื่น ๆ (ตัวอย่างเช่นการคำนวณการไหลของข้อมูลถูกลองเป็นระยะเวลานาน) อย่างไรก็ตามตำแหน่งของสแต็กทำให้ทำงานได้ดีจริงๆ นอกจากนี้โค้ดขั้นตอนเป็นสิ่งที่โปรเซสเซอร์ต้องการเพื่อประสิทธิภาพ: คำสั่งหนึ่งบอกว่าจะต้องทำอะไรหลังจากนั้น เมื่อคำแนะนำไม่ตรงตามลำดับตัวประมวลผลจะช้าลงอย่างมากอย่างน้อยก็เนื่องจากเราไม่ได้หาวิธีการเข้าถึงแบบสุ่มอย่างรวดเร็วเท่ากับการเข้าถึงแบบลำดับ (Btw มีปัญหาคล้ายกันในแต่ละระดับหน่วยความจำตั้งแต่แคชจนถึงหน่วยความจำหลักไปจนถึงดิสก์ ... )

ระหว่างประสิทธิภาพการทำงานที่แสดงให้เห็นของคำแนะนำการเข้าถึงตามลำดับและพฤติกรรมการแคชที่เป็นประโยชน์ของ call stack เรามีอย่างน้อยในปัจจุบันรูปแบบประสิทธิภาพการทำงานที่ชนะ

(เราอาจแสดงความไม่แน่นอนของโครงสร้างข้อมูลลงในงานด้วย ... )

นี่ไม่ได้หมายความว่ารูปแบบการเขียนโปรแกรมอื่นไม่สามารถทำงานได้โดยเฉพาะอย่างยิ่งเมื่อสามารถแปลเป็นคำสั่งตามลำดับและ call stack model ของฮาร์ดแวร์ในปัจจุบัน แต่มีข้อได้เปรียบที่แตกต่างกันสำหรับรุ่นที่รองรับตำแหน่งของฮาร์ดแวร์ อย่างไรก็ตามสิ่งต่าง ๆ ไม่เหมือนเดิมเสมอไปดังนั้นเราสามารถเห็นการเปลี่ยนแปลงในอนาคตเนื่องจากเทคโนโลยีหน่วยความจำและทรานซิสเตอร์ที่แตกต่างกันทำให้เกิดความขนานมากขึ้น มันเป็นสิ่งที่ล้อเลียนระหว่างภาษาการเขียนโปรแกรมและความสามารถของฮาร์ดแวร์ดังนั้นเราจะเห็น!


9
ในความเป็นจริง GPUs ยังคงไม่มีอยู่เลย คุณถูกห้ามไม่ให้เรียกใช้ซ้ำใน GLSL / SPIR-V / OpenCL (ไม่แน่ใจเกี่ยวกับ HLSL แต่อาจเป็นไปได้ว่าฉันไม่มีเหตุผลว่าทำไมถึงแตกต่างกัน) วิธีที่พวกเขาจัดการกับฟังก์ชั่น "สแต็ค" จริง ๆ แล้วก็คือการใช้รีจิสเตอร์จำนวนมากอย่างไร้เหตุผล
LinearZoetrope

@Jsor: นั่นคือรายละเอียดการใช้งานระดับใหญ่ดังที่เห็นได้จากสถาปัตยกรรม SPARC เช่นเดียวกับ GPU ของคุณ SPARC มีชุดลงทะเบียนขนาดใหญ่ แต่มันมีความพิเศษตรงที่มันมีหน้าต่างแบบเลื่อนซึ่งจะทำให้การลงทะเบียนที่เก่าแก่ของสแต็กใน RAM มีจำนวนมาก ดังนั้นมันจึงเป็นลูกผสมระหว่างทั้งสองรุ่น และ SPARC ไม่ได้ระบุจำนวนการลงทะเบียนทางกายภาพที่แน่นอนเพียงเท่านี้หน้าต่างการลงทะเบียนขนาดใหญ่เท่านั้นดังนั้นการใช้งานที่แตกต่างกันอาจอยู่ที่ใดก็ได้ในระดับของ "การลงทะเบียนจำนวนมาก" ถึง "เพียงพอสำหรับหน้าต่างเดียวทุกครั้ง หกลงในสแต็กโดยตรง "
MSalters

ด้านล่างของ call stack model คือ array และ / หรือ address overflow ที่ต้องคอยเฝ้าดูอย่างระมัดระวังเนื่องจากโปรแกรมการแก้ไขตัวเองในฐานะที่เป็นช่องโหว่นั้นเป็นไปได้
BenPen

14

TL; DR

  • Call stack เป็นกลไกการเรียกใช้ฟังก์ชัน:
    1. โดยทั่วไปแล้วจะจำลองด้วยฮาร์ดแวร์ แต่ไม่ได้เป็นพื้นฐานในการสร้างฮาร์ดแวร์
    2. เป็นพื้นฐานในการเขียนโปรแกรมที่จำเป็น
    3. ไม่ใช่พื้นฐานของการโปรแกรมเชิงฟังก์ชัน
  • สแต็คเป็นนามธรรมของ "last-in, first-out" (LIFO) เป็นพื้นฐานของวิทยาศาสตร์คอมพิวเตอร์อัลกอริทึมและแม้กระทั่งบางโดเมนที่ไม่ใช่ด้านเทคนิค
  • ตัวอย่างบางส่วนของการจัดโปรแกรมที่ไม่ได้ใช้ call stack:
    • รูปแบบการส่งต่อ (CPS)
    • เครื่องรัฐ - วงยักษ์ที่มีทุกอย่างอยู่ในบรรทัด (อ้างว่าได้รับแรงบันดาลใจจากสถาปัตยกรรมเฟิร์มแวร์ของ Saab Gripen และเนื่องมาจากการสื่อสารโดย Henry Spencer และทำซ้ำโดย John Carmack) (หมายเหตุ # 1)
    • สถาปัตยกรรม Dataflow - เครือข่ายของนักแสดงที่เชื่อมต่อด้วยคิว (FIFO) คิวบางครั้งเรียกว่าแชนเนล

ส่วนที่เหลือของคำตอบนี้คือการรวบรวมความคิดและเกร็ดเล็กเกร็ดน้อยแบบสุ่ม


สแต็กที่คุณอธิบายไว้ (เป็นกลไกการเรียกใช้ฟังก์ชัน) นั้นมีลักษณะเฉพาะสำหรับการเขียนโปรแกรมที่จำเป็น

ด้านล่างการเขียนโปรแกรมที่จำเป็นคุณจะพบรหัสเครื่อง รหัสเครื่องสามารถเลียนแบบ call stack ได้โดยเรียกใช้คำสั่งตามลำดับเล็ก ๆ

ด้านล่างรหัสเครื่องคุณจะพบฮาร์ดแวร์ที่รับผิดชอบการใช้งานซอฟต์แวร์ ในขณะที่ไมโครโปรเซสเซอร์สมัยใหม่นั้นซับซ้อนเกินกว่าจะอธิบายได้ที่นี่ใคร ๆ ก็สามารถจินตนาการได้ว่าการออกแบบที่เรียบง่ายมีอยู่ที่ช้า แต่ก็ยังสามารถเรียกใช้รหัสเครื่องเดียวกันได้ การออกแบบที่เรียบง่ายเช่นนี้จะใช้ประโยชน์จากองค์ประกอบพื้นฐานของตรรกะดิจิทัล:

  1. ตรรกะเชิงผสมคือการเชื่อมต่อของลอจิกเกต (และ, หรือไม่ใช่, ... ) โปรดทราบว่า "ตรรกะเชิงผสม" ไม่รวมการตอบกลับ
  2. หน่วยความจำเช่น flip-flop latches รีจิสเตอร์ SRAM, DRAM เป็นต้น
  3. เครื่องสถานะที่ประกอบด้วยตรรกะเชิงผสมบางอย่างและหน่วยความจำบางส่วนเพียงพอที่จะใช้ "ตัวควบคุม" ที่จัดการส่วนที่เหลือของฮาร์ดแวร์

การสนทนาต่อไปนี้มีตัวอย่างมากมายของวิธีทางเลือกในการสร้างโปรแกรมที่จำเป็น

โครงสร้างของโปรแกรมเช่นนี้จะเป็นดังนี้:

void main(void)
{
    do
    {
        // validate inputs for task 1
        // execute task 1, inlined, 
        // must complete in a deterministically short amount of time
        // and limited to a statically allocated amount of memory
        // ...
        // validate inputs for task 2
        // execute task 2, inlined
        // ...
        // validate inputs for task N
        // execute task N, inlined
    }
    while (true);
    // if this line is reached, tell the programmers to prepare
    // themselves to appear before an accident investigation board.
    return 0; 
}

ลักษณะนี้เหมาะสำหรับไมโครคอนโทรลเลอร์เช่นสำหรับผู้ที่เห็นว่าซอฟต์แวร์เป็นคู่หูกับการทำงานของฮาร์ดแวร์



@Peteris: Stacks คือโครงสร้างข้อมูล LIFO
Christopher Creutzig

1
น่าสนใจ ฉันจะคิดว่ามันเป็นอีกทางหนึ่ง ตัวอย่างเช่น FORTRAN เป็นภาษาการเขียนโปรแกรมที่จำเป็นและเวอร์ชันก่อนหน้านี้ไม่ได้ใช้ call stack อย่างไรก็ตามการเรียกซ้ำเป็นพื้นฐานของการเขียนโปรแกรมการทำงานและฉันไม่คิดว่ามันเป็นไปได้ที่จะใช้ในกรณีทั่วไปโดยไม่ต้องใช้สแต็ค
TED

@TED ​​- ในการใช้งานฟังก์ชั่นการใช้งานมีโครงสร้างข้อมูลแบบสแต็ค (หรือโดยทั่วไปเป็นต้นไม้) ที่แสดงถึงการคำนวณที่รอดำเนินการ แต่คุณไม่จำเป็นต้องเรียกใช้งานพร้อมคำแนะนำโดยใช้โหมดการกำหนดแอดเดรสสแต็ก (ในแบบซ้อน / แบบเรียกซ้ำ - อาจเป็นเพียงส่วนหนึ่งของลูปเครื่องรัฐ)
davidbak

@davidbak - IIRC อัลกอริทึมแบบเรียกซ้ำเกือบจะต้องเรียกซ้ำเพื่อให้สามารถกำจัดสแต็กได้ อาจมีบางกรณีที่คุณสามารถปรับให้เหมาะสม แต่ในกรณีทั่วไปคุณต้องมีสแต็ก ในความเป็นจริงฉันบอกว่ามีหลักฐานทางคณิตศาสตร์ของเรื่องนี้ลอยอยู่ที่ไหนสักแห่ง คำตอบนี้อ้างว่าเป็นทฤษฎีบท Church-Turing (ฉันคิดว่าตามความจริงที่ว่าเครื่องจักรทัวริงใช้กองซ้อนหรือไม่?)
TED

1
@TED ​​- ฉันเห็นด้วยกับคุณ ผมเชื่อว่าการสื่อสารผิดพลาดที่นี่คือที่ผมอ่านโพสต์ของ OP ที่จะพูดคุยเกี่ยวกับสถาปัตยกรรมของระบบซึ่งหมายถึงฉันสถาปัตยกรรมเครื่อง ฉันคิดว่าคนอื่น ๆ ที่ตอบที่นี่มีความเข้าใจเหมือนกัน ดังนั้นพวกเราที่เข้าใจว่าเป็นบริบทได้ตอบโดยตอบว่าคุณไม่จำเป็นต้องสแต็คในระดับคำสั่งของเครื่อง / ระดับที่อยู่ แต่ฉันสามารถเห็นคำถามที่สามารถตีความได้ว่าหมายถึงเพียงระบบภาษาโดยทั่วไปต้องมี call stack คำตอบนั้นก็ไม่ใช่ แต่ด้วยเหตุผลที่ต่างกัน
davidbak

11

ไม่ไม่จำเป็น

อ่านขยะกระดาษของ Appel ได้เร็วกว่าการจัดสรรแบบแยกส่วน มันใช้รูปแบบการส่งผ่านความต่อเนื่องและแสดงการใช้งานแบบไม่ซ้อน

โปรดสังเกตว่าสถาปัตยกรรมคอมพิวเตอร์เก่า (เช่นIBM / 360 ) ไม่มีการลงทะเบียนสแต็กของฮาร์ดแวร์ แต่ OS และเรียบเรียงลิขสิทธิ์ทะเบียนตัวชี้สแต็คโดยการประชุม (ที่เกี่ยวข้องกับการประชุมโทร ) เพื่อที่พวกเขาจะมีซอฟต์แวร์ที่เรียกกอง

โดยหลักการแล้วโปรแกรมคอมไพเลอร์ C และออพติไมเซอร์ทั้งหมดสามารถตรวจจับเคส (โดยทั่วไปสำหรับระบบฝังตัว) ซึ่งกราฟการโทรนั้นเป็นที่รู้จักกันในแบบคงที่และไม่มีการเรียกซ้ำ (หรือตัวชี้ฟังก์ชั่น) ในระบบดังกล่าวแต่ละฟังก์ชั่นสามารถเก็บที่อยู่ผู้ส่งในตำแหน่งคงที่ (และเป็นวิธีที่Fortran77ทำงานในคอมพิวเตอร์ยุค 1970)

วันนี้ยังมีหน่วยประมวลผลสแต็คโทร (และโทรกลับและคำแนะนำเครื่อง) ตระหนักถึงแคช CPU


1
ค่อนข้างมั่นใจว่า FORTRAN หยุดใช้สถานที่ผลตอบแทนคงที่เมื่อ FORTRAN-66 ออกมาและต้องได้รับการสนับสนุนและSUBROUTINE FUNCTIONคุณถูกต้องสำหรับเวอร์ชันก่อนหน้านี้แม้ว่า (FORTRAN-IV และอาจเป็น WATFIV)
TMN

และ COBOL แน่นอน และจุดที่ยอดเยี่ยมเกี่ยวกับ IBM / 360 - มีการใช้งานค่อนข้างมากแม้ว่าจะไม่มีโหมดการกำหนดแอดเดรสของสแต็กฮาร์ดแวร์ (R14 ฉันเชื่อว่ามันคืออะไร) และมันก็มีคอมไพเลอร์สำหรับภาษาสแต็กเช่น PL / I, Ada, Algol, C.
davidbak

อันที่จริงฉันศึกษา 360 ในวิทยาลัยและพบว่ามันทำให้สับสนในตอนแรก
JDługosz

1
@ JDługoszวิธีที่ดีที่สุดสำหรับนักเรียนที่ทันสมัยของสถาปัตยกรรมคอมพิวเตอร์ที่จะต้องพิจารณา 360 เป็นเครื่อง RISC ง่ายมาก ... แม้จะมีรูปแบบการเรียนการสอนมากกว่าหนึ่ง ... และความผิดปกติบางอย่างและTR TRT
davidbak

วิธีการเกี่ยวกับ "ศูนย์และเพิ่มบรรจุ" เพื่อย้ายการลงทะเบียน? แต่ "branch and link" แทนที่จะเป็น stack สำหรับที่อยู่ผู้ส่งได้กลับมาแล้ว
JDługosz

10

คุณได้รับคำตอบที่ดีมาแล้ว ให้ฉันเป็นตัวอย่างที่ทำไม่ได้ แต่ให้ความรู้สูงเกี่ยวกับวิธีที่คุณสามารถออกแบบภาษาโดยปราศจากความคิดของสแต็คหรือ "การควบคุมการไหล" ได้เลย นี่คือโปรแกรมที่กำหนดแฟคทอเรียล:

function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = f(3)

เราใส่โปรแกรมนี้ในสตริงและเราประเมินโปรแกรมโดยการทดแทนข้อความ ดังนั้นเมื่อเราประเมินf(3)เราทำการค้นหาและแทนที่ด้วย 3 สำหรับ i ดังนี้:

function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = if 3 == 0 then 1 else 3 * f(3 - 1)

ยิ่งใหญ่ ตอนนี้เราทำการทดแทนตัวบทอื่น: เราเห็นว่าเงื่อนไขของ "if" เป็นเท็จและทำการแทนที่สายอักขระอื่นโดยสร้างโปรแกรมขึ้นมา:

function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = 3 * f(3 - 1)

ตอนนี้เราทำสายอักขระอื่นแทนที่นิพจน์ย่อยทั้งหมดที่เกี่ยวข้องกับค่าคงที่:

function f(i) => if i == 0 then 1 else i * f(i - 1)
let x = 3 * f(2)

และคุณจะเห็นว่ามันเป็นอย่างไร ฉันจะไม่ใช้แรงงานจุดต่อไป เราสามารถทำชุดของการแทนที่สตริงจนกว่าเราจะลงไปlet x = 6และเราจะทำ

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

ในโมเดลการแทนที่สตริงของการเขียนโปรแกรมไม่มี "ตัวแปรโลคัล" บนสแต็ก พารามิเตอร์ที่เป็นทางการจะถูกแทนที่ด้วยค่าของพวกเขาเมื่อฟังก์ชั่นถูกนำไปใช้กับการโต้แย้งมากกว่าใส่ลงในตารางการค้นหาบนสแต็ค และไม่มี "ไปที่ไหนสักแห่งต่อไป" เพราะการประเมินผลของโปรแกรมเป็นเพียงการใช้กฎง่ายๆสำหรับการทดแทนสตริงเพื่อสร้างโปรแกรมที่แตกต่างกัน แต่เทียบเท่า

ตอนนี้แน่นอนว่าการใช้การแทนที่สตริงอาจไม่ใช่วิธีที่จะไป แต่ภาษาการเขียนโปรแกรมที่รองรับ "การให้เหตุผลเชิงเหตุผล" (เช่น Haskell) นั้นใช้เหตุผลเชิงตรรกะโดยใช้เทคนิคนี้


3
Retinaเป็นตัวอย่างของภาษาการเขียนโปรแกรมที่ใช้ Regex ซึ่งใช้การดำเนินงานสตริงสำหรับการคำนวณ
Andrew Piliser

2
@AndrewPiliser ออกแบบและดำเนินการโดยเพื่อนสุดเจ๋งนี้
แมว

3

ตั้งแต่การตีพิมพ์โดย Parnas ในปี 1972 ของOn ถึงเกณฑ์ที่จะใช้ในการย่อยสลายระบบเป็นโมดูลมันได้รับการยอมรับอย่างสมเหตุสมผลว่าการซ่อนข้อมูลในซอฟต์แวร์เป็นสิ่งที่ดี สิ่งนี้ตามมาด้วยการถกเถียงกันมานานในช่วงทศวรรษที่ 60 ในเรื่องโครงสร้างการสลายตัวและการเขียนโปรแกรมแบบแยกส่วน

modularity

ผลลัพธ์ที่จำเป็นของความสัมพันธ์ของกล่องดำระหว่างโมดูลที่นำมาใช้โดยกลุ่มต่าง ๆ ในระบบมัลติเธรดใด ๆ จำเป็นต้องมีกลไกในการอนุญาตให้มีการ reentrancy และวิธีการติดตามกราฟการโทรแบบไดนามิกของระบบ โฟลว์การดำเนินการควบคุมต้องผ่านทั้งเข้าและออกหลายโมดูล

การกำหนดขอบเขตแบบไดนามิก

ทันทีที่การกำหนดขอบเขตศัพท์ไม่เพียงพอที่จะติดตามพฤติกรรมแบบไดนามิกดังนั้นการทำบัญชีรันไทม์บางอย่างจำเป็นต้องมีเพื่อติดตามความแตกต่าง

กำหนดเธรดใด ๆ (ตามคำนิยาม) มีเพียงตัวชี้คำสั่งปัจจุบันเพียงอย่างเดียว LIFO-stack เหมาะสมสำหรับการติดตามการร้องขอแต่ละครั้ง

ข้อยกเว้น

ดังนั้นในขณะที่โมเดลความต่อเนื่องไม่ได้รักษาโครงสร้างข้อมูลไว้อย่างชัดเจนสำหรับสแต็ก แต่ก็ยังคงมีการเรียกใช้โมดูลที่ซ้อนกันที่ต้องได้รับการบำรุงรักษาที่อื่น!

แม้แต่ภาษาที่ประกาศก็ยังคงรักษาประวัติการประเมินหรือทำให้แผนการดำเนินการแบนราบในทางกลับกันเพื่อเหตุผลด้านประสิทธิภาพและรักษาความคืบหน้าในทางอื่น

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

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


1
คุณวาดภาพตัวเองในมุมโดยสมมติว่า " ระบบแบบมัลติเธรดใด ๆ " เครื่อง finite-state ที่จับคู่กันอาจมีหลายเธรดในการปรับใช้ แต่ไม่ต้องการ LIFO stack ไม่มีข้อ จำกัด ใน FSM ที่คุณกลับสู่สถานะก่อนหน้านี้เพียงอย่างเดียวตามลำดับ LIFO นั่นเป็นระบบมัลติเธรดที่แท้จริงซึ่งมันไม่ถือ และถ้าคุณ จำกัด ตัวเองเป็นคำจำกัดความของมัลติเธรดในฐานะ "สแต็คการเรียกฟังก์ชันอิสระแบบขนาน" คุณจะได้คำจำกัดความแบบวงกลม
MSalters

ฉันไม่ได้อ่านคำถามแบบนั้น OP คุ้นเคยกับการเรียกใช้ฟังก์ชัน แต่ถามเกี่ยวกับระบบอื่น
MSalters

@MSalters อัปเดตเพื่อรวมลูปแบบไม่มีที่สิ้นสุดพร้อมกัน โมเดลถูกต้อง แต่ จำกัด ขีดความสามารถในการขยาย ฉันขอแนะนำว่าแม้เครื่องระดับปานกลางจะรวมการเรียกใช้ฟังก์ชันเพื่ออนุญาตการใช้รหัสซ้ำ
Pekka

2

เมนเฟรมเก่าทั้งหมด (IBM System / 360) ไม่มีแนวคิดเรื่องสแต็กเลย ตัวอย่างเช่นใน 260 พารามิเตอร์ถูกสร้างขึ้นในตำแหน่งคงที่ในหน่วยความจำและเมื่อเรียกรูทีนย่อยมันจะถูกเรียกโดยR1ชี้ไปที่บล็อกพารามิเตอร์และR14มีที่อยู่ผู้ส่ง รูทีนที่เรียกว่าถ้าต้องการเรียกรูทีนย่อยอื่นจะต้องเก็บไว้R14ในตำแหน่งที่ทราบก่อนทำการเรียก

สิ่งนี้มีความน่าเชื่อถือมากกว่าสแต็กเพราะทุกอย่างสามารถเก็บไว้ในตำแหน่งหน่วยความจำคงที่ที่สร้างขึ้นในเวลารวบรวมและสามารถรับประกันได้ 100% ว่ากระบวนการจะไม่หมดสแต็ก ไม่มี "จัดสรร 1MB และข้ามนิ้วของคุณ" ที่เราต้องทำในปัจจุบัน

โทร subroutine ซ้ำได้รับอนุญาตใน PL / I RECURSIVEโดยการระบุคำหลัก หมายความว่าหน่วยความจำที่ใช้โดยรูทีนย่อยนั้นเป็นแบบไดนามิกมากกว่าการจัดสรรแบบคงที่ แต่การโทรแบบเรียกซ้ำนั้นหายากแล้วในขณะนี้

การทำงานแบบไม่มีสแต็กยังทำให้การมัลติเธรดหลายครั้งง่ายขึ้นมากซึ่งเป็นสาเหตุที่ทำให้ความพยายามในการทำให้ภาษาสมัยใหม่ไม่มีความหมาย ไม่มีเหตุผลอะไรเลยตัวอย่างเช่นเหตุใดคอมไพเลอร์ C ++ จึงไม่สามารถทำการดัดแปลงแบ็คเอนด์เพื่อใช้หน่วยความจำที่จัดสรรแบบไดนามิกแทนที่จะเป็นสแต็ค

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