ผู้เขียนควรจ้องมองโนอาห์ และ...เรียนรู้ เหมือนที่พวกเขาทำในเรือโนอาห์ ในการยัดเยียดสิ่งของมากมายให้อยู่ในพื้นที่ที่จำกัด
— SYDNEY SMITH, EDINBURGH REVIEW
Program Space as Cost (พื้นที่โปรแกรมคือต้นทุน)
โปรแกรมใหญ่แค่ไหน? นอกจากเวลาที่ใช้ในการทำงานแล้ว พื้นที่ที่โปรแกรมครอบครองก็เป็นต้นทุนหลักเช่นกัน แม้แต่ในโปรแกรมเชิงพาณิชย์ที่ผู้ใช้จ่ายค่าธรรมเนียมให้กับผู้พัฒนา ซึ่งโดย essence แล้วคือการแบ่งปันต้นทุนการพัฒนา ลองพิจารณาระบบซอฟต์แวร์แบบโต้ตอบ IBM APL มันให้เช่าในราคา $400 ต่อเดือน และเมื่อใช้งาน จะใช้หน่วยความจำอย่างน้อย 160 K bytes บน Model 165 หน่วยความจำให้เช่าในราคาประมาณ $12 ต่อกิโลไบต์ต่อเดือน ถ้าโปรแกรมพร้อมใช้งานตลอดเวลา ผู้ใช้จ่ายค่าเช่าซอฟต์แวร์ $400 และค่าเช่าหน่วยความจำ $1920 ต่อเดือน แต่ถ้าใช้ระบบ APL เพียงสี่ชั่วโมงต่อวัน ต้นทุนจะอยู่ที่ $400 สำหรับค่าเช่าซอฟต์แวร์และ $320 สำหรับค่าเช่าหน่วยความจำต่อเดือน
เรามักได้ยินเสียงวิพากษ์วิจารณ์ว่าเครื่องขนาด 2 M byte อาจมี 400 K ที่ถูกจัดสรรให้กับระบบปฏิบัติการ ซึ่งเป็นการวิจารณ์ที่โง่เขลาพอๆ กับการติว่า Boeing 747 เพราะมันมีราคา $27 ล้าน เราต้องถามด้วยว่า "มันทำอะไรได้บ้าง?" สิ่งที่เราได้กลับมาคือความสะดวกในการใช้งานและประสิทธิภาพ (ผ่านการใช้ทรัพยากรระบบอย่างมีประสิทธิภาพ) สำหรับเงินที่จ่ายไปนั้นคุ้มค่าหรือไม่? เงิน $4800 ต่อเดือนที่ลงทุนไปกับค่าเช่าหน่วยความจำนั้นสามารถนำไปใช้กับฮาร์ดแวร์อื่น โปรแกรมเมอร์ หรือโปรแกรมประยุกต์ได้ดีกว่าหรือไม่?
นักออกแบบระบบจัดสรรส่วนหนึ่งของทรัพยากรฮาร์ดแวร์ทั้งหมดให้กับหน่วยความจำสำหรับโปรแกรมประจำ (resident-program memory) เมื่อเขาคิดว่ามันจะเป็นประโยชน์ต่อผู้ใช้มากกว่าในรูปแบบนั้น มากกว่าที่จะใช้เป็น adder, disk ฯลฯ การทำอย่างอื่นจะถือเป็นการ irresponsible อย่างร้ายแรง และผลลัพธ์ต้องถูกตัดสินโดยรวม ไม่มีใครสามารถวิพากษ์วิจารณ์ระบบซอฟต์แวร์เรื่องขนาด per se และในเวลาเดียวกันก็สนับสนุนการผสานการออกแบบฮาร์ดแวร์และซอฟต์แวร์ให้ใกล้ชิดยิ่งขึ้น
เนื่องจากขนาดเป็นส่วนสำคัญของต้นทุนที่ผู้ใช้จ่ายสำหรับผลิตภัณฑ์ระบบซอฟต์แวร์ ผู้สร้างต้องกำหนดเป้าหมายด้านขนาด ควบคุมขนาด และคิดค้นเทคนิคการลดขนาด เช่นเดียวกับที่ผู้สร้างฮาร์ดแวร์กำหนดเป้าหมายจำนวน component ควบคุมจำนวน component และคิดค้นเทคนิคการลดจำนวน เช่นเดียวกับต้นทุนอื่นๆ ขนาดในตัวมันเองไม่ใช่สิ่งไม่ดี แต่ขนาดที่ไม่จำเป็นต่างหากที่ไม่ดี
Size Control (การควบคุมขนาด)
สำหรับผู้จัดการโครงการ การควบคุมขนาดเป็นทั้งงานด้านเทคนิคและงานด้านการจัดการ เราต้องศึกษาผู้ใช้และแอปพลิเคชันของพวกเขาเพื่อกำหนดขนาดของระบบที่จะนำเสนอ จากนั้นระบบเหล่านี้ต้องถูกแบ่งย่อย และแต่ละ component ได้รับเป้าหมายขนาด เนื่องจากการ trade-off ระหว่างขนาดและความเร็วมักมาในรูปแบบ quantum jump ที่ค่อนข้างใหญ่ การกำหนดเป้าหมายขนาดจึงเป็นเรื่องที่ต้องอาศัยความชำนาญ ซึ่งต้องมีความรู้เกี่ยวกับ trade-offs ที่มีอยู่ในแต่ละส่วน ผู้จัดการที่ชาญฉลาดยังเก็บงบประมาณสำรองไว้สำหรับจัดสรรเมื่องานดำเนินไป
ใน OS/360 แม้ว่าทั้งหมดนี้จะทำอย่างระมัดระวัง แต่ก็ยังมีบทเรียนอื่นๆ ที่ต้องเรียนรู้อย่างเจ็บปวด
แรกเลย การกำหนดเป้าหมายขนาดสำหรับ core อย่างเดียวนั้นไม่พอ เราต้องจัดสรรงบประมาณสำหรับทุกมิติของขนาด ในระบบปฏิบัติการส่วนใหญ่ก่อนหน้านี้ ระบบ residence อยู่บน tape และเวลาในการค้นหาที่ยาวนานของ tape ทำให้ไม่มีใครอยากใช้มันอย่างไม่ระมัดระวังเพื่อนำโปรแกรม segment เข้ามา OS/360 ใช้ disk เป็นที่เก็บ residence เช่นเดียวกับรุ่นก่อนหน้าโดยตรงอย่าง Stretch Operating System และ 1410-7010 Disk Operating System ผู้สร้างรู้สึกยินดีกับอิสระของการเข้าถึง disk ที่ราคาถูก แต่ผลลัพธ์แรกเริ่มกลับเป็นหายนะต่อประสิทธิภาพ
ในการกำหนดขนาด core สำหรับแต่ละ component เราไม่ได้กำหนดงบประมาณการเข้าถึง (access budgets) ไปพร้อมกัน อย่างที่ใครก็ตามที่มองย้อนกลับไปคงคาดไว้ โปรแกรมเมอร์ที่พบว่าโปรแกรมของเขาเกินเป้าหมาย core ก็จะแบ่งมันเป็น overlays กระบวนการนี้เพิ่มขนาดโดยรวมและทำให้การทำงานช้าลง ที่ร้ายแรงที่สุดคือระบบการจัดการของเราไม่ได้วัดหรือตรวจจับสิ่งนี้ แต่ละคนรายงานว่าเขาใช้ core ไปเท่าไหร่ และเนื่องจากอยู่ในเป้าหมาย ก็ไม่มีใครกังวล
โชคดีที่มีวันหนึ่งในช่วงต้นของความพยายามที่ OS/360 performance simulator เริ่มทำงาน ผลลัพธ์แรกบ่งชี้ถึงปัญหาใหญ่ Fortran H บน Model 65 ที่มี drums จำลองการ compile ได้เพียงห้า statements ต่อนาที! การขุดลึกลงไปพบว่า control program modules แต่ละตัวทำการเข้าถึง disk หลายครั้งมาก แม้แต่ supervisor modules ที่ถูกเรียกใช้บ่อยก็ยังต้องเข้าถึง disk บ่อยครั้ง และผลลัพธ์ก็คล้ายคลึงกับ page thrashing อย่างมาก
บทเรียนแรกชัดเจน: กำหนดงบประมาณขนาด total (โดยรวม) ควบคู่กับงบประมาณพื้นที่ resident; กำหนดงบประมาณการเข้าถึง backing-store ควบคู่กับงบประมาณขนาด
บทเรียนถัดไปคล้ายกันมาก งบประมาณพื้นที่ถูกกำหนดก่อนที่จะมีการจัดสรรหน้าที่อย่างชัดเจนให้กับแต่ละ module ผลลัพธ์คือโปรแกรมเมอร์ที่มีปัญหาเรื่องขนาดจะตรวจสอบโค้ดของตนเพื่อดูว่าสามารถโยนอะไรข้ามรั้วไปยังพื้นที่ของเพื่อนบ้านได้บ้าง ดังนั้น buffers ที่ถูกจัดการโดย control program กลายเป็นส่วนหนึ่งของพื้นที่ผู้ใช้ ที่ร้ายแรงกว่านั้น control blocks ทุกประเภทก็กลายเป็นเช่นเดียวกัน และผลกระทบนั้นส่งผลเสียอย่างร้ายแรงต่อความปลอดภัยและการปกป้องของระบบ
ดังนั้นบทเรียนที่สองก็ชัดเจนเช่นกัน: กำหนดให้ชัดเจนว่า module ต้องทำอะไรเมื่อคุณกำหนดว่ามันต้องใหญ่แค่ไหน
บทเรียนที่สามและลึกซึ้งยิ่งขึ้นปรากฏจากประสบการณ์เหล่านี้ โครงการมีขนาดใหญ่พอและการสื่อสารในการจัดการแย่พอที่จะทำให้สมาชิกในทีมจำนวนมากมองตัวเองเป็นผู้แข่งขันที่ทำเพื่อสร้างคะแนนความดี (brownie points) มากกว่าเป็นผู้สร้างที่ทำผลิตภัณฑ์ซอฟต์แวร์ แต่ละคนปรับแต่งชิ้นส่วนของตนให้ดีที่สุดเพื่อให้ถึงเป้าหมาย มีน้อยคนที่หยุดคิดถึงผลกระทบโดยรวมต่อลูกค้า การแตกสลายของทิศทางและการสื่อสารนี้เป็นอันตรายสำคัญสำหรับโครงการขนาดใหญ่ ตลอดระยะเวลาการ implement สถาปนิกระบบต้องเฝ้าระวังอย่างต่อเนื่องเพื่อรักษาความสมบูรณ์ของระบบ อย่างไรก็ตาม นอกเหนือจากกลไกการควบคุมนี้แล้ว ยังมีเรื่องของทัศนคติของผู้ implement เอง การปลูกฝังทัศนคติที่มองทั้งระบบและเน้นผู้ใช้น่าจะเป็นหน้าที่ที่สำคัญที่สุดของผู้จัดการฝ่ายโปรแกรม
Space Techniques (เทคนิคการจัดการพื้นที่)
ไม่ว่าการจัดสรรงบประมาณและการควบคุมพื้นที่มากแค่ไหนก็ไม่สามารถทำให้โปรแกรมเล็กได้ สิ่งนั้นต้องการการคิดค้น (invention) และฝีมือเชิงช่าง (craftsmanship)
แน่นอนว่า ฟังก์ชันที่มากขึ้นหมายถึงพื้นที่ที่มากขึ้น ถ้าความเร็วคงที่ ดังนั้นศาสตร์แรกของ craftsmanship คือการ trade ฟังก์ชันกับขนาด ที่นี่มีคำถามเชิงนโยบายที่ลึกซึ้งตั้งแต่เนิ่นๆ: ผู้ใช้ควรมีสิทธิ์เลือกมากน้อยแค่ไหน? เราสามารถออกแบบโปรแกรมที่มีคุณสมบัติเสริมมากมาย ซึ่งแต่ละอย่างใช้พื้นที่เล็กน้อย เราสามารถออกแบบ generator ที่รับ option list และปรับแต่งโปรแกรมตามนั้น แต่สำหรับชุด option ใดๆ ที่กำหนดไว้ โปรแกรมแบบ monolithic จะใช้พื้นที่น้อยกว่า มันคล้ายกับรถยนต์ ถ้า map light, cigarette lighter และ clock ถูกคิดราคารวมกันเป็น option เดียว ชุดรวมจะมีราคาถูกกว่าการให้เลือกแต่ละอย่างแยกกัน ดังนั้นนักออกแบบต้องตัดสินใจว่าผู้ใช้จะเลือก option ได้ละเอียดแค่ไหน
ในการออกแบบระบบสำหรับช่วงขนาดหน่วยความจำที่หลากหลาย มีคำถามพื้นฐานอีกข้อเกิดขึ้น ผลกระทบที่เป็นข้อจำกัดทำให้ช่วงความเหมาะสมไม่สามารถกว้างตามต้องการได้ แม้จะมีการ modularize ฟังก์ชันอย่างละเอียดก็ตาม ในระบบที่เล็กที่สุด module ส่วนใหญ่จะถูก overlaid ส่วนสำคัญของพื้นที่ resident ของระบบที่เล็กที่สุดต้องถูกกันไว้เป็น transient area หรือ paging area สำหรับดึงส่วนอื่นๆ เข้ามา ขนาดของพื้นที่นี้กำหนดขนาดของ module ทั้งหมด และการแบ่งฟังก์ชันเป็น module ย่อยๆ ทำให้เสียทั้งประสิทธิภาพและพื้นที่ ดังนั้นระบบขนาดใหญ่ที่สามารถมี transient area ที่ใหญ่กว่า 20 เท่าได้ เพียงแค่ช่วยลดจำนวนการเข้าถึงเท่านั้น มันยังคงมีปัญหาทั้งด้านความเร็วและพื้นที่เพราะขนาด module นั้นเล็กเกินไป ผลกระทบนี้จำกัดขนาดระบบที่มีประสิทธิภาพสูงสุดที่สามารถสร้างจาก module ของระบบเล็ก
ศาสตร์ที่สองของ craftsmanship คือการ trade-off ระหว่างพื้นที่และเวลา (space-time trade-offs) สำหรับฟังก์ชันที่กำหนด ยิ่งใช้พื้นที่มาก ก็ยิ่งทำงานเร็ว ซึ่งเป็นจริงในช่วงที่กว้างอย่างน่าทึ่ง ข้อเท็จจริงนี้เองที่ทำให้การกำหนดงบประมาณพื้นที่เป็นไปได้
ผู้จัดการสามารถทำสองสิ่งเพื่อช่วยให้ทีมของเขาทำ space-time trade-offs ได้ดี หนึ่งคือทำให้แน่ใจว่าพวกเขาได้รับการฝึกฝนในเทคนิคการเขียนโปรแกรม ไม่ใช่ปล่อยให้พึ่งพาแค่ความสามารถตามธรรมชาติและประสบการณ์เดิม สำหรับภาษาใหม่หรือเครื่องจักรใหม่ นี่เป็นสิ่งสำคัญเป็นพิเศษ ลักษณะเฉพาะของการใช้งานอย่างชำนาญต้องเรียนรู้อย่างรวดเร็วและแบ่งปันอย่างกว้างขวาง อาจมีการให้รางวัลพิเศษหรือคำชมสำหรับเทคนิคใหม่ๆ
สองคือการตระหนักว่าการเขียนโปรแกรมมี technology และ components ต้องการถูกสร้างขึ้น (fabricated) ทุกโครงการต้องการสมุดบันทึกที่เต็มไปด้วย subroutines หรือ macros ที่ดีสำหรับ queuing, searching, hashing และ sorting สำหรับแต่ละฟังก์ชันดังกล่าว สมุดบันทึกควรมีอย่างน้อยสองโปรแกรม: แบบเร็ว (quick) และแบบประหยัด (squeezed) การพัฒนา technology ดังกล่าวเป็นงาน realization ที่สำคัญซึ่งสามารถทำควบคู่ไปกับสถาปัตยกรรมระบบได้
Representation Is the Essence of Programming (การแทนข้อมูลคือหัวใจของการเขียนโปรแกรม)
นอกเหนือจาก craftsmanship แล้วคือการคิดค้น (invention) และนี่คือที่มาของโปรแกรมที่เล็ก กระชับ และเร็ว เกือบทุกครั้งสิ่งเหล่านี้เป็นผลมาจากการ breakthrough เชิงกลยุทธ์ (strategic breakthrough) มากกว่าความฉลาดเชิงกลวิธี (tactical cleverness) บางครั้ง strategic breakthrough จะเป็น algorithm ใหม่ เช่น Cooley-Tukey Fast Fourier Transform หรือการแทนที่การเปรียบเทียบแบบ n 2 ด้วยการ sort แบบ n log n
บ่อยครั้งกว่านั้นมาก strategic breakthrough จะมาจากการปรับปรุงการแทนข้อมูล (representation) ของ data หรือ tables ใหม่ นี่คือที่ที่หัวใจของโปรแกรมอยู่ ให้ฉันดู flowcharts ของคุณแต่ซ่อน tables ของคุณ แล้วฉันจะยังคงสับสน ให้ฉันดู tables ของคุณ แล้วฉันจะไม่ต้องการ flowcharts ของคุณ เพราะมันจะชัดเจนในตัว
เป็นเรื่องง่ายที่จะยกตัวอย่างเพิ่มเติมเกี่ยวกับพลังของ representation ผมจำได้ว่าชายหนุ่มคนหนึ่งรับหน้าที่สร้าง console interpreter ที่ซับซ้อนสำหรับ IBM 650 เขาลงเอยด้วยการยัดมันลงในพื้นที่ที่เล็กอย่างไม่น่าเชื่อโดยการสร้าง interpreter สำหรับ interpreter โดยตระหนักว่าการปฏิสัมพันธ์ของมนุษย์นั้นช้าและไม่บ่อย แต่พื้นที่นั้นมีค่า Digitek's Fortran compiler ขนาดเล็กที่สง่างามใช้ representation ที่หนาแน่นและเฉพาะทางสำหรับโค้ดของ compiler เอง ทำให้ไม่จำเป็นต้องใช้ external storage เวลาที่เสียไปในการถอดรหัส representation นี้ถูกชดเชยคืนมาเป็นสิบเท่าจากการหลีกเลี่ยง input-output (แบบฝึกหัดท้ายบทที่ 6 ใน Brooks และ Iverson, Automatic Data Processing [1] รวมถึงตัวอย่างดังกล่าวไว้มากมาย เช่นเดียวกับแบบฝึกหัดหลายข้อของ Knuth [2] )
โปรแกรมเมอร์ที่หมดหนทางเพราะพื้นที่ไม่พอ มักจะทำได้ดีที่สุดโดยการถอนตัวออกจากโค้ด ถอยกลับมา และพิจารณาข้อมูลของตน Representation (การแทนข้อมูล) คือ หัวใจสำคัญของการเขียนโปรแกรม