ชุดข้อมูลที่มุ่งเน้นข้อมูล
การออกแบบที่เน้นข้อมูลไม่ได้หมายถึงการใช้ SoAs ได้ทุกที่ มันหมายถึงการออกแบบสถาปัตยกรรมที่เน้นการแสดงข้อมูลเป็นพิเศษโดยเน้นที่การจัดวางหน่วยความจำที่มีประสิทธิภาพและการเข้าถึงหน่วยความจำ
ซึ่งอาจนำไปสู่ตัวแทนของ SOA เมื่อเหมาะสมเช่นนั้น:
struct BallSoa
{
vector<float> x; // size n
vector<float> y; // size n
vector<float> z; // size n
vector<float> r; // size n
};
... นี่มักจะเหมาะสำหรับตรรกะวงวนแนวตั้งที่ไม่ได้ประมวลผลองค์ประกอบเวกเตอร์ของศูนย์ทรงกลมและรัศมีพร้อมกัน (ทั้งสี่เขตข้อมูลไม่ร้อนพร้อมกัน) แต่แทนที่ทีละครั้ง (วนผ่านรัศมีอีก 3 ลูป ผ่านแต่ละองค์ประกอบของศูนย์ทรงกลม)
ในกรณีอื่น ๆ อาจเป็นการเหมาะสมที่จะใช้ AoS หากมีการเข้าถึงฟิลด์ด้วยกันบ่อยครั้ง (ถ้าตรรกะการวนรอบของคุณวนซ้ำทุกสนามของลูกแทนที่จะเป็นรายบุคคล) และ / หรือถ้าต้องการการเข้าถึงแบบสุ่มของลูกบอล:
struct BallAoS
{
float x;
float y;
float z;
float r;
};
vector<BallAoS> balls; // size n
... ในกรณีอื่น ๆ มันอาจจะเหมาะสมที่จะใช้ไฮบริดซึ่งยอดคงเหลือทั้งสองประโยชน์:
struct BallAoSoA
{
float x[8];
float y[8];
float z[8];
float r[8];
};
vector<BallAoSoA> balls; // size n/8
... คุณอาจบีบอัดขนาดของลูกให้เหลือครึ่งโดยใช้ลูกลอยเพื่อเพิ่มขนาดของลูกให้มากขึ้นในบรรทัดแคช / หน้า
struct BallAoSoA16
{
Float16 x2[16];
Float16 y2[16];
Float16 z2[16];
Float16 r2[16];
};
vector<BallAoSoA16> balls; // size n/16
... บางทีแม้แต่รัศมีก็ไม่สามารถเข้าถึงได้เกือบเท่าศูนย์สเฟียร์ (บางทีโค้ดเบสของคุณมักจะถือว่าพวกมันเหมือนจุดและไม่ค่อยเป็นทรงกลมเช่น) ในกรณีดังกล่าวคุณอาจใช้เทคนิคการแยกฟิลด์แบบร้อน / เย็นต่อไป
struct BallAoSoA16Hot
{
Float16 x2[16];
Float16 y2[16];
Float16 z2[16];
};
vector<BallAoSoA16Hot> balls; // size n/16: hot fields
vector<Float16> ball_radiuses; // size n: cold fields
กุญแจสำคัญในการออกแบบที่มุ่งเน้นข้อมูลคือการพิจารณาการเป็นตัวแทนทุกประเภทเหล่านี้ในช่วงต้นของการตัดสินใจออกแบบของคุณเพื่อที่จะไม่ดักจับตัวคุณเองให้เป็นตัวแทนย่อยที่ดีที่สุดด้วยอินเทอร์เฟซสาธารณะที่อยู่ด้านหลัง
มันให้ความสำคัญกับรูปแบบการเข้าถึงหน่วยความจำและรูปแบบประกอบทำให้พวกเขากังวลมากขึ้นกว่าปกติ ในความรู้สึกมันก็อาจจะทำลาย abstractions ฉันพบโดยใช้ความคิดนี้มากกว่าที่ฉันไม่ได้ดูอีกต่อไปstd::deque
เช่นในแง่ของความต้องการอัลกอริทึมของมันค่อนข้างมากเท่ากับการแสดงบล็อกที่ต่อเนื่องกันที่รวบรวมไว้และการเข้าถึงแบบสุ่มของมันทำงานในระดับหน่วยความจำ มันค่อนข้างให้ความสำคัญกับรายละเอียดการใช้งาน แต่รายละเอียดการใช้งานซึ่งมีแนวโน้มที่จะมีผลกระทบต่อประสิทธิภาพเพียงเล็กน้อยหรือมากกว่านั้นตามความซับซ้อนของอัลกอริธึมที่อธิบายการขยายขีดความสามารถ
การเพิ่มประสิทธิภาพก่อนวัยอันควร
จุดสนใจที่เด่นชัดจำนวนมากของการออกแบบที่มุ่งเน้นข้อมูลจะปรากฏขึ้นอย่างน้อยในทันทีที่ใกล้ถึงการปรับให้เหมาะสมก่อนที่จะเกิดอันตราย ประสบการณ์มักสอนเราว่าการเพิ่มประสิทธิภาพขนาดเล็กนั้นถูกนำไปใช้ในการเข้าใจย้อนหลังที่สุดและมีผู้สร้างโปรไฟล์ในมือ
แต่บางทีข้อความที่แข็งแกร่งที่จะรับจากการออกแบบเชิงข้อมูลคือการออกจากพื้นที่สำหรับการเพิ่มประสิทธิภาพดังกล่าว นั่นคือความคิดเชิงข้อมูลที่สามารถช่วยให้:
การออกแบบที่เน้นข้อมูลสามารถออกจากห้องหายใจเพื่อสำรวจการนำเสนอที่มีประสิทธิภาพมากขึ้น ไม่จำเป็นต้องเกี่ยวกับการบรรลุความสมบูรณ์แบบของหน่วยความจำในครั้งเดียว แต่เพิ่มเติมเกี่ยวกับการพิจารณาที่เหมาะสมล่วงหน้าเพื่อให้การเป็นตัวแทนที่ดีที่สุดเพิ่มขึ้น
การออกแบบเชิงวัตถุที่ละเอียด
การอภิปรายเกี่ยวกับการออกแบบเชิงข้อมูลจำนวนมากจะทำให้ตนเองแตกต่างจากแนวคิดดั้งเดิมของการเขียนโปรแกรมเชิงวัตถุ ถึงกระนั้นฉันก็จะเสนอวิธีการดูสิ่งนี้ซึ่งไม่ง่ายนักที่จะยกเลิก OOP ทั้งหมด
ความยากลำบากในการออกแบบเชิงวัตถุคือมันมักจะดึงดูดเราให้ทำโมเดลอินเตอร์เฟสในระดับย่อย ๆ ทำให้เราติดกับ scalar ความคิดแบบครั้งต่อครั้งแทนที่จะเป็นความคิดแบบกลุ่มจำนวนมาก
เป็นตัวอย่างที่พูดเกินจริงลองจินตนาการถึงแนวคิดการออกแบบเชิงวัตถุที่นำไปใช้กับพิกเซลเดียวของภาพ
class Pixel
{
public:
// Pixel operations to blend, multiply, add, blur, etc.
private:
Image* image; // back pointer to access adjacent pixels
unsigned char rgba[4];
};
หวังว่าจะไม่มีใครทำเช่นนี้ ในการทำให้ตัวอย่างรวมจริงๆฉันเก็บตัวชี้หลังไปยังรูปภาพที่มีพิกเซลเพื่อให้สามารถเข้าถึงพิกเซลข้างเคียงสำหรับอัลกอริทึมการประมวลผลภาพอย่างเบลอ
ตัวชี้ด้านหลังของรูปภาพเพิ่มค่าใช้จ่ายที่จ้องมองทันทีแม้ว่าเราจะแยกออก (ทำให้อินเทอร์เฟซสาธารณะของพิกเซลให้การดำเนินการที่ใช้กับพิกเซลเดียว) แต่เราก็จบลงด้วยคลาสเพื่อเป็นตัวแทนของพิกเซล
ตอนนี้ไม่มีอะไรผิดปกติกับคลาสในค่าใช้จ่ายทันทีในบริบท C ++ นอกเหนือจากตัวชี้หลังนี้ การเพิ่มประสิทธิภาพคอมไพเลอร์ C ++ นั้นยอดเยี่ยมในการใช้โครงสร้างทั้งหมดที่เราสร้างขึ้นและกำจัดมันให้เป็นโรงหลอมขนาดเล็ก
ความยากของที่นี่คือเรากำลังสร้างโมเดลอินเทอร์เฟซแบบแค็ปซูลที่ระดับพิกเซลเกินไป นั่นทำให้เราติดกับการออกแบบที่ละเอียดและข้อมูลแบบนี้โดยอาจมีการพึ่งพาลูกค้าจำนวนมากที่เชื่อมต่อกับPixel
อินเทอร์เฟซนี้
วิธีแก้ปัญหา: กำจัดโครงสร้างเชิงวัตถุของพิกเซลเม็ดเล็กออกและเริ่มสร้างโมเดลอินเทอร์เฟซของคุณที่ระดับ coarser ที่เกี่ยวข้องกับจำนวนพิกเซลจำนวนมาก (ที่ระดับภาพ)
ด้วยการสร้างแบบจำลองที่ระดับรูปภาพจำนวนมากเรามีพื้นที่เพิ่มประสิทธิภาพมากขึ้นอย่างมีนัยสำคัญ ยกตัวอย่างเช่นเราสามารถแสดงภาพขนาดใหญ่เป็นภาพรวมที่มีขนาด 16x16 พิกเซลซึ่งพอดีกับแคชแคชแบบ 64 ไบต์ แต่อนุญาตการเข้าถึงพิกเซลแนวตั้งที่มีประสิทธิภาพซึ่งอยู่ใกล้เคียงกับการก้าวเล็ก ๆ (ถ้าเรามีอัลกอริธึมการประมวลผลภาพจำนวนมาก จำเป็นต้องเข้าถึงพิกเซลข้างเคียงในแนวตั้ง) เป็นตัวอย่างที่ไม่ยอมใครง่ายๆ
การออกแบบในระดับ Coarser
ตัวอย่างข้างต้นของอินเทอร์เฟซการสร้างแบบจำลองที่ระดับรูปภาพเป็นตัวอย่างที่ไม่มีเกมง่ายๆเนื่องจากการประมวลผลภาพเป็นฟิลด์ที่โตเต็มที่ที่ได้รับการศึกษาและปรับให้เหมาะกับความตาย ทว่าสิ่งที่เห็นได้ชัดเจนน้อยกว่านั้นอาจเป็นอนุภาคในตัวปล่อยอนุภาคสไปรท์กับคอลเล็กชั่นสไปรท์ขอบในกราฟของขอบหรือแม้แต่บุคคลกับกลุ่มคน
กุญแจสำคัญในการอนุญาตการปรับให้เหมาะสมที่สุดกับข้อมูล (ในการคาดการณ์ล่วงหน้าหรือการเข้าใจถึงปัญหาหลังเหตุการณ์) มักจะทำให้การออกแบบส่วนต่อประสานลดลง แนวคิดของการออกแบบส่วนต่อประสานสำหรับเอนทิตีเดี่ยวจะถูกแทนที่ด้วยการออกแบบสำหรับคอลเลกชันของเอนทิตีที่มีการดำเนินการขนาดใหญ่ที่ประมวลผลเป็นกลุ่ม เป้าหมายนี้โดยเฉพาะอย่างยิ่งและทันทีเข้าถึงลูปลำดับที่จำเป็นต้องเข้าถึงทุกอย่างและไม่สามารถช่วย แต่มีความซับซ้อนเชิงเส้น
การออกแบบเชิงข้อมูลมักเริ่มต้นด้วยแนวคิดการรวมข้อมูลเพื่อจัดทำแบบจำลองการรวมข้อมูลเป็นกลุ่ม ความคิดที่คล้ายกันสะท้อนไปยังการออกแบบอินเตอร์เฟสที่มาพร้อมกับมัน
นี่เป็นบทเรียนที่มีค่าที่สุดที่ฉันนำมาจากการออกแบบเชิงข้อมูลเนื่องจากฉันไม่เข้าใจสถาปัตยกรรมคอมพิวเตอร์มากพอที่จะค้นหาเลย์เอาต์ของหน่วยความจำที่ดีที่สุดสำหรับบางสิ่งในการลองครั้งแรก มันกลายเป็นสิ่งที่ฉันพูดย้ำด้วย profiler ในมือ (และบางครั้งก็พลาดไปตามทางที่ฉันไม่ได้เร่งความเร็ว) แต่ด้านการออกแบบส่วนต่อประสานของการออกแบบเชิงข้อมูลคือสิ่งที่ทำให้ฉันมีพื้นที่ว่างในการค้นหาการแสดงข้อมูลที่มีประสิทธิภาพมากขึ้น
กุญแจสำคัญคือการออกแบบส่วนต่อประสานในระดับที่หยาบกว่าที่เรามักจะอยากทำ สิ่งนี้มักจะมีประโยชน์ด้านเช่นการลดค่าใช้จ่ายการจัดส่งแบบไดนามิกที่เกี่ยวข้องกับฟังก์ชั่นเสมือนการเรียกตัวชี้ฟังก์ชั่นการเรียก dylib และการไร้ความสามารถสำหรับผู้ที่จะ inline แนวคิดหลักที่จะนำมาใช้ทั้งหมดนี้คือการดูการประมวลผลในแบบกลุ่ม (ถ้ามี)
ball->do_something();
เมื่อเทียบกับball_table.do_something(ball)
)(&ball_table, index)
ถ้าคุณต้องการที่จะปลอมเป็นนิติบุคคลที่เชื่อมโยงกันผ่านทางหลอกตัวชี้