ผู้มาสายในการตอบคำถามและคำตอบที่ยอดเยี่ยมนี้แล้ว แต่ฉันต้องการที่จะก้าวก่ายในฐานะชาวต่างชาติที่เคยมองสิ่งต่าง ๆ จากจุดยืนระดับล่างของบิตและไบต์ในหน่วยความจำ
ฉันตื่นเต้นมากกับการออกแบบที่ไม่เปลี่ยนรูปแม้กระทั่งมาจากมุมมอง C และจากมุมมองของการหาวิธีใหม่ในการเขียนโปรแกรมฮาร์ดแวร์สัตว์ร้ายที่เรามีอยู่ในปัจจุบันอย่างมีประสิทธิภาพ
ช้า / เร็วขึ้น
สำหรับคำถามที่ว่ามันทำให้ช้าลงหรือไม่คำตอบของหุ่นยนต์ก็yes
คือ ในระดับแนวความคิดทางเทคนิคแบบนี้ความไม่เปลี่ยนรูปจะทำให้สิ่งต่าง ๆ ช้าลง ฮาร์ดแวร์ทำได้ดีที่สุดเมื่อไม่จัดสรรหน่วยความจำเป็นระยะและสามารถปรับเปลี่ยนหน่วยความจำที่มีอยู่แทนได้ (ทำไมเราจึงมีแนวคิดเหมือนท้องถิ่นชั่วคราว)
maybe
แต่คำตอบที่ปฏิบัติคือ ประสิทธิภาพการทำงานยังคงเป็นตัวชี้วัดประสิทธิภาพการผลิตส่วนใหญ่ใน codebase ที่ไม่สำคัญ โดยทั่วไปแล้วเราไม่พบว่ามีโค้ดฐานที่น่ากลัวที่จะรักษาสภาพการแข่งขันที่มีประสิทธิภาพมากที่สุดแม้ว่าเราจะไม่สนใจข้อบกพร่องก็ตาม ประสิทธิภาพมักเป็นหน้าที่ของความสง่างามและความเรียบง่าย จุดสูงสุดของการเพิ่มประสิทธิภาพขนาดเล็กอาจขัดแย้งกันบ้าง แต่ส่วนใหญ่จะถูกสงวนไว้สำหรับส่วนที่เล็กที่สุดและสำคัญที่สุดของรหัส
การแปลงบิตและไบต์ที่ไม่เปลี่ยนรูป
มาจากจุดยืนระดับต่ำหากเรามีแนวคิดเอ็กซ์เรย์เช่นobjects
และstrings
อื่น ๆ หัวใจของมันคือบิตและไบต์ในหน่วยความจำรูปแบบต่าง ๆ ที่มีลักษณะความเร็ว / ขนาดต่างกัน (ความเร็วและขนาดของฮาร์ดแวร์หน่วยความจำโดยทั่วไป พิเศษร่วมกัน)
ลำดับชั้นหน่วยความจำของคอมพิวเตอร์ชอบเมื่อเราเข้าถึงหน่วยความจำอันเดียวกันซ้ำหลายครั้งเช่นในแผนภาพด้านบนเนื่องจากมันจะเก็บกลุ่มของหน่วยความจำที่เข้าถึงบ่อยๆในรูปแบบหน่วยความจำที่เร็วที่สุด (L1 แคชเช่นซึ่ง เกือบจะเร็วเท่ากับการลงทะเบียน) เราอาจเข้าถึงหน่วยความจำเดียวกันซ้ำหลายครั้ง (นำมาใช้ซ้ำหลายครั้ง) หรือเข้าถึงส่วนต่าง ๆ ของก้อน (ซ้ำ ๆ : วนลูปผ่านองค์ประกอบในก้อนที่ต่อเนื่องกันซึ่งเข้าถึงหน่วยความจำของหน่วยความจำนั้น ๆ ซ้ำ ๆ
เราสิ้นสุดการขว้างประแจในกระบวนการนั้นหากการแก้ไขหน่วยความจำนี้สิ้นสุดลงซึ่งต้องการสร้างบล็อกหน่วยความจำใหม่ทั้งหมดที่ด้านข้างดังนี้:
... ในกรณีนี้การเข้าถึงบล็อกหน่วยความจำใหม่อาจต้องใช้ความผิดพลาดของเพจที่บังคับและแคชคิดถึงเพื่อย้ายมันกลับไปยังหน่วยความจำรูปแบบที่เร็วที่สุด (ตลอดการลงทะเบียน) ที่สามารถเป็นนักฆ่าประสิทธิภาพที่แท้จริง
อย่างไรก็ตามมีวิธีการลดขนาดนี้อย่างไรก็ตามการใช้พูลสำรองของหน่วยความจำที่จัดสรรล่วงหน้าได้ถูกแตะไปแล้ว
มวลรวมขนาดใหญ่
อีกประเด็นทางความคิดที่เกิดขึ้นจากมุมมองระดับสูงขึ้นเล็กน้อยคือการทำสำเนาที่ไม่จำเป็นของมวลรวมที่มีขนาดใหญ่จริงๆ
เพื่อหลีกเลี่ยงไดอะแกรมที่ซับซ้อนเกินไปลองนึกภาพว่าบล็อกหน่วยความจำง่าย ๆ นี้มีราคาแพง (อาจเป็นอักขระ UTF-32 บนฮาร์ดแวร์ที่ จำกัด อย่างไม่น่าเชื่อ)
ในกรณีนี้ถ้าเราต้องการแทนที่ "ช่วยเหลือ" ด้วย "KILL" และบล็อกหน่วยความจำนี้ไม่เปลี่ยนรูปเราจะต้องสร้างบล็อกใหม่ทั้งหมดให้ครบถ้วนเพื่อสร้างวัตถุใหม่ที่ไม่ซ้ำกันแม้ว่าจะเปลี่ยนเพียงบางส่วนเท่านั้น :
การยืดจินตนาการของเราออกไปเล็กน้อยสำเนาแบบลึก ๆ ของทุกอย่างอื่นที่จะทำให้ชิ้นส่วนเล็ก ๆ น้อย ๆ ที่ไม่เหมือนใครอาจมีราคาแพงมาก (ในกรณีของโลกแห่งความจริงบล็อกหน่วยความจำนี้จะใหญ่กว่ามาก ๆ
อย่างไรก็ตามแม้จะมีค่าใช้จ่ายเช่นนี้การออกแบบแบบนี้จะมีแนวโน้มที่จะเกิดข้อผิดพลาดน้อยกว่ามาก ใครก็ตามที่ทำงานในภาษาที่ใช้งานได้กับฟังก์ชั่นที่บริสุทธิ์สามารถชื่นชมสิ่งนี้ได้และโดยเฉพาะอย่างยิ่งในกรณีที่มีหลายเธรดซึ่งเราสามารถมัลติเธรดโค้ดดังกล่าวได้โดยไม่ต้องสนใจในโลกนี้ โดยทั่วไปโปรแกรมเมอร์มนุษย์มักจะเดินทางข้ามการเปลี่ยนแปลงสถานะโดยเฉพาะอย่างยิ่งสิ่งที่ทำให้เกิดผลข้างเคียงภายนอกเพื่อระบุสถานะภายนอกขอบเขตของฟังก์ชันปัจจุบัน แม้การกู้คืนจากข้อผิดพลาดภายนอก (ข้อยกเว้น) ในกรณีเช่นนี้อาจเป็นเรื่องยากอย่างไม่น่าเชื่อกับการเปลี่ยนแปลงสถานะภายนอกที่ไม่แน่นอนในการผสม
วิธีหนึ่งในการลดงานคัดลอกซ้ำซ้อนนี้คือการทำให้บล็อกหน่วยความจำเหล่านี้เป็นชุดของพอยน์เตอร์ (หรือการอ้างอิง) กับตัวอักษรเช่น:
ขอโทษฉันไม่ได้ตระหนักว่าเราไม่จำเป็นต้องสร้างความL
แปลกใหม่ในขณะที่ทำแผนภาพ
สีน้ำเงินหมายถึงข้อมูลที่ถูกคัดลอกตื้น
... น่าเสียดายที่มันมีราคาแพงอย่างไม่น่าเชื่อที่จะจ่ายค่าตัวชี้ / การอ้างอิงต่อตัวละคร นอกจากนี้เราอาจกระจายเนื้อหาของตัวละครไปทั่วพื้นที่ที่อยู่และจบลงด้วยการจ่ายในรูปแบบของการโหลดของความผิดพลาดของหน้าและแคชที่ขาดหายไปทำให้การแก้ปัญหานี้เลวร้ายยิ่งกว่าการคัดลอกสิ่งทั้งปวง
แม้ว่าเราจะระมัดระวังในการจัดสรรอักขระเหล่านี้อย่างต่อเนื่องบอกว่าเครื่องสามารถโหลด 8 อักขระและ 8 พอยน์เตอร์เป็นอักขระในบรรทัดแคช เราจะโหลดหน่วยความจำแบบนี้เพื่อสำรวจสตริงใหม่:
ในกรณีนี้เราจะต้องโหลดแคชของหน่วยความจำที่ต่อเนื่องกัน 7 เส้นเพื่อโหลดสตริงนี้เมื่อเราต้องการเพียง 3
รับรู้ข้อมูล
เพื่อบรรเทาปัญหาดังกล่าวข้างต้นเราสามารถใช้กลยุทธ์พื้นฐานเดียวกัน แต่ในระดับที่หยาบกว่า 8 ตัวอักษรเช่น
ผลลัพธ์ต้องมีแคช 4 บรรทัดที่มีมูลค่าของข้อมูล (1 สำหรับ 3 พอยน์เตอร์และ 3 สำหรับตัวอักษร) เพื่อโหลดผ่านสายอักขระนี้ซึ่งสั้นเพียง 1 ของทฤษฎีที่เหมาะสมที่สุด
นั่นไม่เลวเลย มีหน่วยความจำเหลืออยู่บ้าง แต่หน่วยความจำมีมากมายและการใช้งานที่มากขึ้นไม่ทำให้ช้าลงหากหน่วยความจำเสริมกำลังจะเป็นข้อมูลที่ไม่ได้รับการเข้าถึงบ่อยครั้ง เป็นข้อมูลที่ร้อนแรงและต่อเนื่องกันซึ่งการใช้หน่วยความจำลดลงและความเร็วมักจะไปจับมือกันซึ่งเราต้องการเพิ่มหน่วยความจำให้มากขึ้นในหน้าเดียวหรือแคชไลน์และเข้าถึงข้อมูลทั้งหมดก่อนที่จะถูกไล่ออก การแสดงนี้ค่อนข้างเป็นมิตรกับแคช
ความเร็ว
ดังนั้นการใช้การแสดงเช่นด้านบนจึงสามารถสร้างความสมดุลของประสิทธิภาพได้ค่อนข้างดี อาจเป็นการใช้โครงสร้างข้อมูลที่ไม่เปลี่ยนรูปแบบซึ่งมีความสำคัญที่สุดในลักษณะของการแก้ไขข้อมูลที่เป็นก้อนและทำให้พวกมันไม่เหมือนใครในกระบวนการนี้ในขณะที่การคัดลอกชิ้นส่วนที่ไม่มีการแก้ไขก็ตื้นเขิน นอกจากนี้ยังแสดงถึงค่าใช้จ่ายในการปฏิบัติการปรมาณูเพื่ออ้างอิงชิ้นงานที่คัดลอกมาอย่างปลอดภัยในบริบทแบบมัลติเธรด (อาจเป็นไปได้เมื่อมีการนับเลขอ้างอิงอะตอม)
แต่ตราบใดที่ชิ้นส่วนของข้อมูลเหล่านี้ถูกแสดงในระดับที่ค่อนข้างหยาบค่าใช้จ่ายจำนวนมากนี้จะลดน้อยลงและอาจเป็นเรื่องเล็กน้อยในขณะที่ยังให้ความปลอดภัยและความสะดวกในการเขียนโค้ดและมัลติฟังก์ชันเพิ่มเติมในรูปแบบบริสุทธิ์ ผลกระทบ
การเก็บข้อมูลทั้งเก่าและใหม่
ที่ฉันเห็นความไม่สามารถเปลี่ยนแปลงได้ซึ่งเป็นประโยชน์มากที่สุดจากจุดยืนประสิทธิภาพ (ในแง่ปฏิบัติ) คือเมื่อเราถูกล่อลวงให้ทำสำเนาข้อมูลขนาดใหญ่ทั้งหมดเพื่อทำให้เป็นเอกลักษณ์ในบริบทที่ไม่แน่นอนซึ่งเป้าหมายคือการสร้างสิ่งใหม่จาก บางสิ่งที่มีอยู่แล้วในแบบที่เราต้องการเก็บทั้งเก่าและใหม่เมื่อเราสามารถทำให้ชิ้นเล็ก ๆ น้อย ๆ และมันเป็นเอกลักษณ์ด้วยการออกแบบที่ไม่เปลี่ยนรูปแบบอย่างระมัดระวัง
ตัวอย่าง: เลิกทำระบบ
ตัวอย่างนี้เป็นระบบเลิกทำ เราอาจเปลี่ยนโครงสร้างข้อมูลเพียงเล็กน้อยและต้องการรักษาทั้งรูปแบบดั้งเดิมที่เราสามารถยกเลิกและรูปแบบใหม่ได้ ด้วยการออกแบบที่ไม่เปลี่ยนรูปแบบซึ่งทำให้ส่วนเล็ก ๆ ที่ถูกดัดแปลงของโครงสร้างข้อมูลไม่ซ้ำกันเราสามารถเก็บสำเนาของข้อมูลเก่าไว้ในรายการเลิกทำในขณะที่จ่ายเฉพาะค่าหน่วยความจำของข้อมูลส่วนที่เพิ่มเข้ามาเท่านั้น สิ่งนี้ให้ความสมดุลที่มีประสิทธิภาพอย่างมากในการผลิต
อินเทอร์เฟซระดับสูง
แต่มีบางอย่างที่น่าอึดอัดใจเกิดขึ้นกับคดีข้างต้น ในบริบทของฟังก์ชันในท้องถิ่นข้อมูลที่ไม่แน่นอนมักจะเป็นวิธีที่ง่ายที่สุดและตรงไปตรงมาที่สุดในการแก้ไข ท้ายที่สุดแล้ววิธีที่ง่ายที่สุดในการแก้ไขอาเรย์มักจะวนซ้ำไปเรื่อย ๆ และแก้ไขทีละองค์ประกอบ เราสามารถเพิ่มค่าใช้จ่ายทางปัญญาได้หากเรามีอัลกอริธึมระดับสูงจำนวนมากให้เลือกที่จะแปลงอาเรย์และต้องเลือกอันที่เหมาะสมเพื่อให้แน่ใจว่าสำเนาตื้น ๆ เหล่านี้จะถูกสร้างขึ้นในขณะที่ชิ้นส่วนที่ถูกดัดแปลงนั้น ทำที่ไม่ซ้ำกัน
อาจเป็นวิธีที่ง่ายที่สุดในกรณีเหล่านี้คือการใช้บัฟเฟอร์ที่ไม่แน่นอนในบริบทของฟังก์ชั่น (ซึ่งโดยทั่วไปจะไม่เดินทางไปหาเรา) ซึ่งกระทำการเปลี่ยนแปลงแบบอะตอมในโครงสร้างข้อมูลเพื่อรับสำเนาที่ไม่เปลี่ยนรูปแบบใหม่ "ชั่วคราว" เหล่านี้)) ...
... หรือเราอาจเพียงแค่จำลองฟังก์ชันการแปลงระดับสูงขึ้นและสูงขึ้นเหนือข้อมูลเพื่อให้เราสามารถซ่อนกระบวนการปรับเปลี่ยนบัฟเฟอร์ที่ไม่แน่นอนและส่งไปยังโครงสร้างโดยไม่เกี่ยวข้องกับตรรกะที่ไม่แน่นอน ไม่ว่าในกรณีใด ๆ นี่ยังไม่ได้เป็นอาณาเขตที่มีการสำรวจอย่างกว้างขวางและเราได้ทำการตัดออกหากเราใช้การออกแบบที่ไม่เปลี่ยนรูปแบบมากขึ้นเพื่อสร้างอินเตอร์เฟสที่มีความหมายสำหรับวิธีการแปลงโครงสร้างข้อมูลเหล่านี้
โครงสร้างข้อมูล
อีกสิ่งหนึ่งที่เกิดขึ้นที่นี่คือความผันแปรไม่ได้ที่ใช้ในบริบทที่มีประสิทธิภาพที่สำคัญอาจต้องการให้โครงสร้างข้อมูลแบ่งเป็นข้อมูลที่เป็นก้อนที่ชิ้นไม่เล็กเกินไป แต่ก็ไม่ใหญ่เกินไป
รายการที่เชื่อมโยงอาจต้องการเปลี่ยนเล็กน้อยเพื่อรองรับสิ่งนี้และเปลี่ยนเป็นรายการที่ไม่ได้ควบคุม อาร์เรย์ขนาดใหญ่ที่ต่อเนื่องกันอาจเปลี่ยนเป็นอาเรย์ของพอยน์เตอร์ให้เป็นส่วนต่อเนื่องที่มีการทำดัชนีโมดูโลสำหรับการเข้าถึงแบบสุ่ม
มันอาจเปลี่ยนแปลงวิธีที่เราดูโครงสร้างข้อมูลในวิธีที่น่าสนใจในขณะที่ผลักดันฟังก์ชันการปรับเปลี่ยนของโครงสร้างข้อมูลเหล่านี้ให้มีลักษณะคล้ายกันมากขึ้นเพื่อซ่อนความซับซ้อนเป็นพิเศษในการคัดลอกบิตที่ตื้นที่นี่
ประสิทธิภาพ
อย่างไรก็ตามนี่คือมุมมองระดับล่างเล็กน้อยของฉันในหัวข้อ ในทางทฤษฎีการเปลี่ยนแปลงไม่ได้อาจมีค่าใช้จ่ายตั้งแต่ขนาดใหญ่ถึงเล็ก แต่วิธีการทางทฤษฎีไม่ได้ทำให้แอพพลิเคชั่นทำงานได้อย่างรวดเร็ว มันอาจทำให้มันปรับขนาดได้ แต่ความเร็วในโลกแห่งความเป็นจริงมักจะต้องใช้ความคิดที่เป็นประโยชน์มากขึ้น
จากมุมมองในทางปฏิบัติคุณภาพเช่นประสิทธิภาพความสามารถในการบำรุงรักษาและความปลอดภัยมีแนวโน้มที่จะกลายเป็นพร่ามัวขนาดใหญ่โดยเฉพาะอย่างยิ่งสำหรับโค้ดเบสขนาดใหญ่มาก ในขณะที่ประสิทธิภาพการทำงานในความรู้สึกที่แน่นอนบางอย่างลดลงด้วยความไม่สามารถเปลี่ยนได้ แต่ก็ยากที่จะโต้แย้งถึงประโยชน์ที่มีต่อประสิทธิภาพและความปลอดภัย ด้วยการเพิ่มขึ้นของสิ่งเหล่านี้มักจะสามารถเพิ่มประสิทธิภาพการปฏิบัติถ้าเพียงเพราะนักพัฒนามีเวลามากขึ้นในการปรับแต่งและเพิ่มประสิทธิภาพรหัสของพวกเขาโดยไม่ถูกฝูงโดยข้อบกพร่อง
ดังนั้นฉันจึงคิดว่าจากความจริงแล้วโครงสร้างข้อมูลที่ไม่เปลี่ยนรูปอาจจะช่วยประสิทธิภาพในหลายกรณี โลกอุดมคติอาจมองหาการผสมผสานระหว่างสองสิ่งนี้: โครงสร้างข้อมูลที่ไม่เปลี่ยนรูปแบบและสิ่งที่เปลี่ยนแปลงไม่ได้โดยทั่วไปจะมีความปลอดภัยมากที่จะใช้ในขอบเขตท้องถิ่น (เช่น: ภายในฟังก์ชัน) ในขณะที่วัตถุที่ไม่เปลี่ยนรูปสามารถหลีกเลี่ยงด้านนอก ผลทันทีและเปลี่ยนการเปลี่ยนแปลงโครงสร้างข้อมูลทั้งหมดเป็นการดำเนินการปรมาณูสร้างรุ่นใหม่โดยไม่มีความเสี่ยงของสภาพการแข่งขัน