مصفوفة متوازية

في مجال الحوسبة ، تُعدّ مجموعة المصفوفات المتوازية (المعروفة أيضًا باسم بنية المصفوفات أو SoA) شكلاً من أشكال هياكل البيانات الضمنية التي تستخدم مصفوفات متعددة لتمثيل مصفوفة واحدة من السجلات . تحتفظ هذه البنية بمصفوفة بيانات منفصلة ومتجانسة لكل حقل من حقول السجل، بحيث تحتوي كل مصفوفة على نفس عدد العناصر. وبالتالي، تُعتبر العناصر الموجودة في نفس الفهرس في كل مصفوفة حقولًا ضمنية لسجل واحد. ويتم استبدال المؤشرات من عنصر إلى آخر بفهارس المصفوفة. يختلف هذا عن الأسلوب المعتاد لتخزين جميع حقول كل سجل معًا في الذاكرة (المعروف أيضًا باسم مصفوفة الهياكل أو AoS). على سبيل المثال، يمكن تعريف مصفوفة تحتوي على 100 اسم، كل منها سلسلة نصية، و100 عمر، كل منها عدد صحيح، مع ربط كل اسم بالعمر الذي له نفس الفهرس.

أمثلة

مثال بلغة C باستخدام المصفوفات المتوازية:

int ages [] = { 0 , 17 , 2 , 52 , 25 }; char * names [] = { "None" , "Mike" , "Billy" , "Tom" , "Stan" }; int parent [] = { 0 /*None*/ , 3 /*Tom*/ , 1 /*Mike*/ , 0 /*None*/ , 3 /*Tom*/ };for ( i = 1 ; i <= 4 ; i ++ ) { printf ( "الاسم: %s، العمر: %d، الوالد: %s \n " , names [ i ], ages [ i ], names [ parent [ i ]]); }

في لغة بيرل (باستخدام تجزئة للمصفوفات للاحتفاظ بمراجع لكل مصفوفة):

بياناتي %data = ( الاسم الأول => [ 'جو' , 'بوب' , 'فرانك' , 'هانز' ], الاسم الأخير => [ 'سميث' , 'سيجر' , 'سيناترا' , 'شولتز' ], الطول بالسنتيمتر => [ 169 , 158 , 201 , 199 ]);for $i ( 0 .. $# { $data { first_name }}) { printf "الاسم: %s %s\n" , $data { first_name }[ $i ], $data { last_name }[ $i ]; printf "الطول بالسنتيمتر: %i\n" , $data { height_in_cm }[ $i ]; }

أو بلغة بايثون :

الأسماء الأولى = [ "جو" ، "بوب" ، "فرانك" ، "هانز" ] أسماء العائلة = [ "سميث" ، "سيجر" ، "سيناترا" ، "شولتز" ] الأطوال بالسنتيمتر = [ 169 ، 158 ، 201 ، 199 ]for i in range ( len ( first_names )): print ( "الاسم: %s %s " % ( first_names [ i ], last_names [ i ])) print ( "الطول بالسنتيمتر: %s " % heights_in_cm [ i ])# باستخدام دالة zip: for first_name , last_name , height_in_cm in zip ( first_names , last_names , heights_in_cm ): print ( f "الاسم: { first_name } { last_name } " ) print ( f "الطول بالسنتيمتر: { height_in_cm } " )

الإيجابيات والسلبيات

تتمتع المصفوفات المتوازية بعدد من المزايا العملية مقارنة بالأسلوب العادي:

  • يمكن لهذه التقنيات توفير مساحة تخزين كبيرة في بعض الحالات بتجنب مشاكل المحاذاة. على سبيل المثال، تعمل بعض البنى بشكل أفضل إذا تم تخزين الأعداد الصحيحة ذات 4 بايتات دائمًا بدءًا من مواقع الذاكرة التي تكون من مضاعفات العدد 4. إذا كان الحقل السابق بايتًا واحدًا، فقد يتم إهدار 3 بايتات. تستطيع العديد من المترجمات الحديثة تجنب هذه المشاكل تلقائيًا، مع أن بعض المبرمجين في الماضي كانوا يُعلنون عن الحقول صراحةً بترتيب تنازلي لقيود المحاذاة.
  • إذا كان عدد العناصر صغيرًا، فإن مؤشرات المصفوفة يمكن أن تشغل مساحة أقل بكثير من المؤشرات الكاملة، خاصة في بعض البنى.
  • إن فحص حقل واحد من كل سجل في المصفوفة بشكل متسلسل سريع للغاية على الأجهزة الحديثة، لأن هذا يعادل اجتيازًا خطيًا لمصفوفة واحدة، مما يُظهر موضعًا مثاليًا للمرجع وسلوك التخزين المؤقت.
  • قد تسمح هذه التقنيات بمعالجة فعالة باستخدام تعليمات SIMD في بعض بنى مجموعات التعليمات

تعتمد العديد من هذه المزايا بشكل كبير على لغة البرمجة والتنفيذ المحدد المستخدم.

ومع ذلك، فإن المصفوفات المتوازية لها أيضاً العديد من العيوب القوية، وهو ما يفسر سبب عدم تفضيلها بشكل عام:

  • يكون لديهم موقع مرجعي أسوأ بكثير عند زيارة السجلات بشكل غير متسلسل وفحص حقول متعددة لكل سجل، لأن المصفوفات المختلفة قد يتم تخزينها على مسافات بعيدة بشكل تعسفي.
  • إنها تحجب العلاقة بين حقول سجل واحد (على سبيل المثال، لا توجد معلومات عن النوع تربط الفهرس بينها، وقد يتم استخدام الفهرس بشكل خاطئ).
  • لا يوجد لديهم دعم لغوي مباشر يُذكر (عادةً لا تعبر اللغة وبنيتها النحوية عن أي علاقة بين المصفوفات في المصفوفة المتوازية، ولا يمكنها اكتشاف الأخطاء).
  • بما أن مجموعة الحقول ليست كيانًا واحدًا، فإن تمريرها عملية شاقة وعرضة للأخطاء. على سبيل المثال، بدلًا من استدعاء دالة لإجراء عملية ما على سجل واحد (أو بنية أو كائن)، يجب أن تأخذ الدالة الحقول كمعاملات منفصلة. عند إضافة حقل جديد أو تغييره، يجب تغيير العديد من قوائم المعاملات، بينما تمرير الكائنات ككل يتجنب هذه التغييرات تمامًا.
  • تُعدّ عمليات توسيعها أو تقليصها مكلفة، إذ يتطلب الأمر إعادة تخصيص الذاكرة لكل مصفوفة من المصفوفات المتعددة. يمكن للمصفوفات متعددة المستويات التخفيف من هذه المشكلة، لكنها تؤثر سلبًا على الأداء نظرًا للحاجة إلى مزيد من التوجيه غير المباشر للعثور على العناصر المطلوبة.
  • ولعلّ الأسوأ من ذلك كله، أنها تزيد بشكل كبير من احتمالية حدوث الأخطاء. يجب تطبيق أي عملية إدراج أو حذف أو نقل بشكل متسق على جميع المصفوفات، وإلا فلن تكون المصفوفات متزامنة مع بعضها البعض، مما يؤدي إلى نتائج غريبة.

يمكن التخفيف من مشكلة عدم ترابط المراجع في بعض الحالات: إذا أمكن تقسيم بنية البيانات إلى مجموعات من الحقول التي يتم الوصول إليها معًا بشكل عام، فيمكن إنشاء مصفوفة لكل مجموعة، وتكون عناصرها عبارة عن سجلات تحتوي فقط على هذه المجموعات الفرعية من حقول البنية الأكبر (انظر تصميم البيانات ). تُعد هذه طريقة فعّالة لتسريع الوصول إلى البنى الكبيرة جدًا ذات الأعضاء الكثيرة، مع الحفاظ على ترابط أجزاء البنية. يُمكن استخدام المراجع كبديل لربط الأجزاء باستخدام فهارس المصفوفات، ولكن هذه الطريقة قد تكون أقل كفاءة من حيث الوقت والمساحة.

ثمة بديل آخر يتمثل في استخدام مصفوفة واحدة، حيث يمثل كل عنصر فيها بنية سجل. توفر العديد من لغات البرمجة طريقة لتعريف السجلات الفعلية، ومصفوفات منها. في لغات أخرى، قد يكون من الممكن محاكاة ذلك بتعريف مصفوفة بحجم n*m، حيث m هو حجم جميع الحقول مجتمعة، وتجميع الحقول فيما يُعتبر سجلاً فعلياً، حتى وإن كانت تلك اللغة تفتقر إلى دعم مباشر للسجلات. بعض تحسينات المُصرّف ، وخاصةً للمعالجات المتجهة ، قادرة على إجراء هذا التحويل تلقائياً عند إنشاء مصفوفات من البنى في البرنامج.

انظر أيضاً

مراجع