Коли добре використовувати паралельні масиви?


14

Я зіткнувся з кодом (новим кодом), який використовує те, що я називаю "Паралельні масиви" або "Списки". Значить, є 2 масиви, які містять пов’язані дані та пов'язані їх позицією (індексом) у масиві.

Я вважаю це заплутаним і схильним до різного роду помилок. Я зазвичай пропоную рішення створити об'єкт, який називається Companyполями CompanyId та CompanyName.

Дуже реальний приклад:

List<string> companyNames;
List<int> companyIds;

//...They get populated somewhere and we then process

for(var i=0; i<companyNames.Count; i++)
{
    UpdateCompanyName(companyIds[i],companyNames[i]);
}

Чи вважаються ці паралельні масиви поганою практикою ?


9
Просто ще один доказ того, що не було винайдено жодної мови, на якій ви не можете написати Fortran.
Енді манго

3
Можливі (досить значні) переваги кешування, щоб зробити щось подібне (хоча вам потрібні суміжні масиви, не пов'язані списками), і це стало дещо популярним в програмуванні ігор, пов'язаних з "дизайном, орієнтованим на дані". Однак, схоже, це не стосується вашої справи. Не схоже, що ви створюєте критичний для продуктивності код.
Дерек Елкінс вийшов SE

2
@DerekElkins ... Цікаво, що ваш коментар слідує за порівнянням цього з кодом Fortran. Ранні версії Fortran бракували підтримки для визначених користувачем структур, і навіть після того, як він був доданий ідіоматичний код Fortran використовує кілька масивів властивостей, а не масивів структур. І це часто зараховується як частина причини, по якій Фортран часто вважається найшвидшою мовою.
Жуль

3
Думка, дотична до цього питання: багато функціональних мов активно заохочують працювати з такими списками. Вони мають функцію, яку зазвичай називають zip, яка перетворює їх у список кортежів. Ваш код виглядає як C #. Остання версія C # додала підтримку кортежів першого класу. Цікаво, чи, отже, вони десь додали функцію zip, яка може автоматично перетворити ваші списки на корисну для вас структуру?
Жуль

4
Що ж, іноді є причини, щоб навмисно використовувати два масиви, але в 99% всіх випадків я це бачив, єдиною причиною цього була лінь оригінального автора ввести сприйняту структуру даних.
Док Браун

Відповіді:


23

Ось кілька причин, чому хтось може використовувати масиви pararel:

  1. Мовою, яка не підтримує класів чи конструкцій
  2. Щоб уникнути блокування ниток, коли окремі потоки змінюють лише один із стовпців
  3. Коли метод стійкості змушує ці речі зберігатись окремо, і ви їх відновлюєте.
  4. Вони можуть споживати менше пам’яті, якщо структури набиті. (не застосовується для цих типів даних у C #)
  5. Коли частини даних потрібно зберігати близько один до одного, щоб ефективно використовувати кеш процесора (це не допоможе у наведеному вище коді).
  6. Використання оп-кодів для єдиних інструкцій з декількома даними (SIMD). (не застосовується для цього коду чи рядків взагалі)

Я не бачу жодної вагомої причини зробити це в цьому випадку ... і, ймовірно, кращі варіанти у всьому вищесказаному або не такі корисні мовою високого рівня.


3
Вони також можуть споживати менше пам’яті, якщо структури набиті. Кілька великих масивів, виділених інтелектуально, можуть споживати менше пам'яті, ніж масив структур.
Френк Хілеман

4
4. Коли частини даних потрібно зберігати близько один до одного, щоб ефективно використовувати кеш процесора. (Необхідно в рідкісних випадках.)
Blrfl

@Frank Hileman, Whilie Я думаю, що відповідь TheCatWhisperer є абсолютно правильною, ваш коментар насправді є найкращою причиною обрати такий підхід. Якщо споживання пам'яті є критичним, накладні витрати на обробку конструкцій можуть бути значними, особливо якщо в грі велика кількість.
Володимир Стокіч

До ваших пропозицій
додали

Re (2), як це? Я можу написати програму з одним масивом структур та блокуванням на поле так само легко, як і я можу написати одну з кількома масивами та блокуванням на масив.
Соломон повільно

7

Я винен у використанні паралельних масивів . Іноді ти так сильно вписуєшся в структуру, що не хочеш думати, як її абстрагувати. Абстрагування може бути трохи складніше для рефактора, тому ви не бажаєте запускати його прямо до того часу, поки не доведете, що вам насправді потрібно.

У той момент, хоча варто подумати про рефакторинг, щоб абстрагувати деталі. Найчастіше найбільшою причиною, по якій я не хочу це робити, виявляється те, що важко думати про хороше ім’я.

Якщо ви бачите хороший спосіб абстрагувати паралельні масиви, робіть це кожен раз. Але не паралізуйте себе, відмовляючись торкатися їх. Іноді трохи брудного коду є найкращим кроком до великого коду.


6

Ця закономірність іноді називається також Структура масивів (на відміну від масиву структур) і є надзвичайно корисною при векторизації коду. Замість того, щоб писати обчислення, яке працює на одній структурі і векторизує її біти, ви пишете обчислення так, як зазвичай, за винятком SSS, щоб воно працювало на 4 структурах замість однієї. Зазвичай це простіше, і майже завжди швидше. Формат SoA робить це дуже природно. Це також покращує вирівнювання, що робить операції з пам'яттю SSE швидшими.


Так, такий підхід застосовується при машинному навчанні на GPU. Зазвичай прийнято розбивати поля безлічі окремих прикладів, упаковувати всі значення кожного поля в окремий тензор і передавати ці тензори разом, щоб їх обчислювали, щоб скласти список прогнозів.
Відновіть Моніку
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.