📦 Иллюзия объектов: Как мы морим процессор голодом
Представьте, что вы пишете симуляцию (или игру). По заветам ООП вы создаете сущность:
Затем складываете их в массив:
Теперь нам нужно в цикле обновить позицию (
Процессор читает память из RAM в свой сверхбыстрый L1 кэш кусками по 64 байта (Cache Line).
Когда он берет первую частицу, он затягивает в кэш её
Результат:
1. Cache Miss: Больше половины кэша забито мусором (
2. Bandwidth: Вы сжигаете пропускную способность памяти впустую.
Мы пишем код для людей, забывая, что исполнять его железу. Железо любит однородные, плотно упакованные данные. Как это исправить? Об этом в следующем посте.
#rust #architecture #dop #performance #cpu
👉 @rust_lib
Представьте, что вы пишете симуляцию (или игру). По заветам ООП вы создаете сущность:
struct Particle {
position: [f32; 3], // 12 байт
velocity: [f32; 3], // 12 байт
color: [u8; 4], // 4 байта
life: f32, // 4 байта
name: String, // 24 байта
}
// Итого: 56 байт
Затем складываете их в массив:
Vec<Particle>. Это называется Array of Structs (AoS).Теперь нам нужно в цикле обновить позицию (
position += velocity). Нам нужны только 24 байта из 56. Но процессор так не умеет!Процессор читает память из RAM в свой сверхбыстрый L1 кэш кусками по 64 байта (Cache Line).
Когда он берет первую частицу, он затягивает в кэш её
position, velocity, а заодно color, life и половину name. Результат:
1. Cache Miss: Больше половины кэша забито мусором (
color, name), который в данном цикле не нужен. Процессору приходится постоянно бегать в медленную RAM за новыми частицами.2. Bandwidth: Вы сжигаете пропускную способность памяти впустую.
Мы пишем код для людей, забывая, что исполнять его железу. Железо любит однородные, плотно упакованные данные. Как это исправить? Об этом в следующем посте.
#rust #architecture #dop #performance #cpu
👉 @rust_lib
👍22❤3👎1🔥1💯1
🚄 Struct of Arrays (SoA): Пишем для железа
В прошлом посте мы поняли, что массив жирных структур (
Решение из Data-Oriented Design - вывернуть структуру наизнанку. Это называется Struct of Arrays (SoA).
Вместо массива структур, мы делаем структуру массивов:
Теперь, чтобы обновить позиции, мы пишем:
Почему процессор плачет от счастья?
1. Идеальный кэш: Когда процессор берет 64 байта из
2. Hardware Prefetcher: У процессора есть умный блок, который видит: "Ага, он читает память линейно!". И он начинает предзагружать следующие позиции в кэш до того, как они понадобятся циклу.
3. SIMD (Автовекторизация): Компилятору в 100 раз проще применить те самые AVX-инструкции к плоскому однородному массиву.
SoA может ускорить математические циклы в 5-10 раз без изменения алгоритма! Но есть минус: удалять или добавлять одну частицу теперь неудобно (надо менять 4 разных вектора).
#rust #dop #soa #memory #optimization
👉 @rust_lib
В прошлом посте мы поняли, что массив жирных структур (
Vec<Particle>) забивает кэш процессора ненужными данными. Решение из Data-Oriented Design - вывернуть структуру наизнанку. Это называется Struct of Arrays (SoA).
Вместо массива структур, мы делаем структуру массивов:
struct Particles {
positions: Vec<[f32; 3]>,
velocities: Vec<[f32; 3]>,
colors: Vec<[u8; 4]>,
lifetimes: Vec<f32>,
}
Теперь, чтобы обновить позиции, мы пишем:
for i in 0..count {
positions[i][0] += velocities[i][0];
positions[i][1] += velocities[i][1];
positions[i][2] += velocities[i][2];
}
Почему процессор плачет от счастья?
1. Идеальный кэш: Когда процессор берет 64 байта из
positions, он получает ровно позиции. Никаких имен или цветов. Ни байта не тратится впустую.2. Hardware Prefetcher: У процессора есть умный блок, который видит: "Ага, он читает память линейно!". И он начинает предзагружать следующие позиции в кэш до того, как они понадобятся циклу.
3. SIMD (Автовекторизация): Компилятору в 100 раз проще применить те самые AVX-инструкции к плоскому однородному массиву.
SoA может ускорить математические циклы в 5-10 раз без изменения алгоритма! Но есть минус: удалять или добавлять одну частицу теперь неудобно (надо менять 4 разных вектора).
#rust #dop #soa #memory #optimization
👉 @rust_lib
👍12🔥5❤1🥰1🤔1
👾 ECS: Data-Oriented Design без боли
Вы скажете: "Struct of Arrays - это круто для процессора, но поддерживать такой код руками - ад! Я хочу просто написать
Вам не нужно писать SoA руками. В экосистеме Rust для этого есть ECS (Entity Component System). Самые известные примеры -
ECS абстрагирует работу с памятью. Вы пишете компоненты как маленькие структуры:
А затем пишете Системы (функции), которые запрашивают только то, что им нужно:
Что происходит под капотом?
Магия. ECS-фреймворк сам раскладывает компоненты в памяти в виде плотных массивов (почти как SoA, чаще всего используя паттерн Archetypes).
Когда
Итог: Вы получаете красивый, модульный код (почти как в ООП), но с производительностью хардкорного низкоуровневого C. Если вы пишете что-то, где много сущностей (игры, симуляции, HFT, парсеры) - посмотрите в сторону ECS.
#rust #ecs #bevy #architecture #dop
👉 @rust_lib
Вы скажете: "Struct of Arrays - это круто для процессора, но поддерживать такой код руками - ад! Я хочу просто написать
entity.update()".Вам не нужно писать SoA руками. В экосистеме Rust для этого есть ECS (Entity Component System). Самые известные примеры -
Bevy ECS, Hecs, Legion.ECS абстрагирует работу с памятью. Вы пишете компоненты как маленькие структуры:
struct Position { x: f32, y: f32 }
struct Velocity { dx: f32, dy: f32 }
А затем пишете Системы (функции), которые запрашивают только то, что им нужно:
// Пример из Bevy ECS
fn physics_system(mut query: Query<(&mut Position, &Velocity)>) {
for (mut pos, vel) in query.iter_mut() {
pos.x += vel.dx;
pos.y += vel.dy;
}
}
Что происходит под капотом?
Магия. ECS-фреймворк сам раскладывает компоненты в памяти в виде плотных массивов (почти как SoA, чаще всего используя паттерн Archetypes).
Когда
physics_system запрашивает данные, она бежит по памяти линейно, идеально утилизируя кэш.Итог: Вы получаете красивый, модульный код (почти как в ООП), но с производительностью хардкорного низкоуровневого C. Если вы пишете что-то, где много сущностей (игры, симуляции, HFT, парсеры) - посмотрите в сторону ECS.
#rust #ecs #bevy #architecture #dop
👉 @rust_lib
👍15❤4🥰1🤗1