Перш за все, не плутайте це з дизайном, керованим даними.
Я розумію, що дизайн орієнтований на дані полягає в тому, що мова йде про організацію ваших даних для ефективної обробки. Особливо щодо пропусків кешу тощо. Дизайн, керований даними, з іншого боку, полягає в тому, щоб дані контролювали багато поведінки ваших програм (це дуже добре описано у відповіді Ендрю Кіта ).
Скажімо, у вашій програмі є кульові предмети з такими властивостями, як колір, радіус, приємність, положення тощо.
Об'єктно-орієнтований підхід
В OOP ви описали б такі кульки:
class Ball {
Point position;
Color color;
double radius;
void draw();
};
І тоді ви створили б колекцію кульок так:
vector<Ball> balls;
Підхід, орієнтований на дані
Однак у дизайні, орієнтованому на дані, ви більше шансів написати такий код:
class Balls {
vector<Point> position;
vector<Color> color;
vector<double> radius;
void draw();
};
Як бачите, вже немає жодної одиниці, яка б представляла одну Кулю. Кульові предмети існують лише неявно.
Це може мати багато переваг, ефективні. Зазвичай ми хочемо одночасно робити операції на багатьох кульках. Обладнання зазвичай хоче, щоб великі безперервні шматки пам'яті працювали ефективно.
По-друге, ви можете робити операції, які впливають на лише частину властивостей куль. Наприклад, якщо ви поєднуєте кольори всіх кульок різними способами, то ви хочете, щоб ваш кеш містив лише інформацію про кольори. Однак, коли всі властивості кулі зберігаються в одній одиниці, ви також потягнете за собою всі інші властивості кульки. Хоча вони вам і не потрібні.
Приклад використання кешу
Скажімо, кожен куля займає 64 байти, а точка займає 4 байти. Слот кеша також займає, скажімо, 64 байти. Якщо я хочу оновити позицію 10 балів, мені потрібно втягнути в кеш 10 * 64 = 640 байт пам'яті і отримати 10 пропусків кеша. Якщо я все ж можу працювати з кулями як окремі одиниці, це займе лише 4 * 10 = 40 байт. Це вкладається в один збір кеша. Таким чином, ми отримуємо лише 1 пропуск кеша, щоб оновити всі 10 балів. Ці числа довільні - я припускаю, що кеш-блок більший.
Але це ілюструє, як компонування пам'яті може мати серйозний вплив на хіти кешу і, таким чином, на продуктивність. Це збільшуватиметься лише у міру збільшення різниці між швидкістю процесора та оперативної пам’яті.
Як розташувати пам'ять
У своєму прикладі балу я багато спростив проблему, тому що зазвичай для будь-якого звичайного додатка ви, швидше за все, отримаєте доступ до кількох змінних разом. Наприклад, положення та радіус, ймовірно, часто використовуються разом. Тоді ваша структура повинна бути:
class Body {
Point position;
double radius;
};
class Balls {
vector<Body> bodies;
vector<Color> color;
void draw();
};
Причина, з якої ви повинні це зробити, полягає в тому, що якщо дані, що використовуються разом, розміщуються в окремих масивах, є ризик, що вони будуть конкурувати за ті самі слоти в кеші. Таким чином, завантаження одного викине іншого.
Отже, порівняно з об'єктно-орієнтованим програмуванням, класи, які ви закінчуєте створювати, не пов'язані з сутністю вашої ментальної моделі проблеми. Оскільки дані збираються разом на основі використання даних, ви не завжди матимете розумних імен, щоб давати свої класи в дизайні, орієнтованому на дані.
Ставлення до реляційних баз даних
Мислення, орієнтоване на дизайн, орієнтований на дані, дуже схоже на те, як ви думаєте про реляційні бази даних. Оптимізація реляційної бази даних також може залучати ефективніше використовувати кеш, хоча в цьому випадку кеш - це не кеш процесора, а сторінки в пам'яті. Хороший дизайнер баз даних також, швидше за все, розділяє нечасто доступні дані в окрему таблицю, а не створює таблицю з величезною кількістю стовпців, коли колись використовуються лише кілька стовпців. Він також може вирішити денормалізувати деякі таблиці, щоб дані не мали доступу з кількох місць на диску. Як і в дизайні, орієнтованому на дані, ці варіанти робляться, дивлячись, які бувають схеми доступу до даних та де вузьке місце продуктивності.