Rust
2.22K subscribers
137 photos
101 videos
2 files
190 links
Полезный контент по программированию на Rust
Download Telegram
🛠 Cargo Expand: Загляни под капот макросам

Признайтесь, у вас бывало такое: навесили #[derive(Serialize)] или сложный макрос из sqlx, получили странную ошибку компиляции и сидите в ступоре?

Макросы это круто, но это черный ящик. Чтобы превратить его в прозрачный, поставьте cargo-expand.


cargo install cargo-expand



Запускаем:


cargo expand



И видите весь тот ужас (или красоту), который генерируется до того, как код попадет к компилятору. Это маст-хэв тулза при отладке async трейтов (привет, async-trait крейт) и понимании того, как работает "магия" фреймворков типа Actix или Rocket.

P.S. Только не пугайтесь, когда увидите, во что разворачивается println!.

#rust #tools #cargo #macro

👉 @rust_lib
👍141🔥1🥰1👻1
🐮 Cow: Ленивое клонирование

Все знают: лишний clone() - это грех. Но иногда нам нужно вернуть строку, которая может быть изменена, а может и нет.

Допустим, мы чистим инпут от спецсимволов.

1. Если спецсимволов нет - зачем аллоцировать новую строку? Можно вернуть ссылку на исходную.
2. Если есть - придется создать новую String.

Здесь на сцену выходит std::borrow::Cow (Clone on Write).


use std::borrow::Cow;

fn sanitize(input: &str) -> Cow<str> {
if input.contains('!') {
// Аллокация происходит только тут
Cow::Owned(input.replace('!', "?"))
} else {
// Тут мы просто возвращаем ссылку. Zero allocation.
Cow::Borrowed(input)
}
}



Это мощнейший инструмент для хот-пасов (hot paths), где 90% данных не требуют изменений. Не ленитесь использовать Cow там, где можно избежать лишних аллокаций.

#rust #performance #std

👉 @rust_lib
👍315🥰1😁1🤗1
🤠 Трюк Индианы Джонса: std::mem::take

Типичная ситуация: у вас есть &mut self, и вы хотите забрать поле (например, String или Vec), что-то с ним сделать, и, возможно, вернуть обратно или сохранить измененную версию.

Компилятор бьет по рукам: нельзя просто так переместить (move) значение из-за мутабельной ссылки. Новички часто делают .clone(), чтобы успокоить borrow checker. Но это лишняя аллокация!

Решение: std::mem::take.

Оно забирает значение из ссылки, оставляя на его месте Default::default().


struct Buffer {
data: Vec<u8>,
}

impl Buffer {
fn process(&mut self) {
// Мы "выкрадываем" вектор.
// В self.data теперь пустой Vec (без аллокаций).
let raw_data = std::mem::take(&mut self.data);

// Тяжелая обработка в другом потоке/функции,
// требующая владения (ownership)
let processed = heavy_transform(raw_data);

// Возвращаем результат
self.data = processed;
}
}



Это работает для всего, что реализует Default (Option, String, Vec). Для типов без Default есть std::mem::replace.

Это zero-cost, семантически верно и намного быстрее клонирования.

#rust #std #optimization #tips

👉 @rust_lib
🔥26👍96❤‍🔥1🥰1😁1🤗1
Почему Vec::new() это ловушка для производительности.

📈 Векторный рост: Избегаем реаллокаций

Мы часто пишем:


let mut vec = Vec::new();
for item in heavy_iter {
vec.push(item);
}



Что происходит под капотом?

1. Вектор создается пустым (0 байт).

2. Первый push: аллокация на 4 элемента (условно).

3. Пятый push: места нет.
• Создается новый буфер (размером x2).
• Все старые элементы копируются туда.
• Старый буфер освобождается.
• Новый элемент записывается.



Это называется Reallocation. Это дорого. Если у вас 10 миллионов элементов, вы сделаете десятки реаллокаций и скопируете гигабайты данных просто так.

Решение: Если вы хотя бы примерно знаете размер - подскажите компилятору.


// Идеально, если знаем точно
let mut vec = Vec::with_capacity(exact_count);

// Или используем итераторы, они часто сами знают свой размер
let vec: Vec<_> = heavy_iter.collect();



collect() умный. Он смотрит на size_hint итератора и сразу аллоцирует нужный буфер (если итератор "честный").

Правило: Vec::new() - только если вы правда не знаете, будет ли там хоть один элемент. В остальных случаях - with_capacity.

#rust #performance #vectors

👉 @rust_lib
👍316🥰2😢2🤗1
⚡️ Забудь про RefCell для примитивов

Вам нужно изменить поле внутри неизменяемой структуры (&self).
Рука тянется к RefCell? Стоп.

RefCell имеет оверхед:

1. Хранит счетчик заимствований (borrow count).
2. В рантайме проверяет правила (может паниковать!).

Если ваш тип реализует Copy (числа, bool, маленькие структуры) - используйте Cell.

В чем магия Cell?
У него нет никакого рантайм-оверхеда. Вообще. Он не проверяет заимствования.
Он работает так: "Я даю тебе копию значения" (get) или "Я заменяю значение целиком" (set). Вы не можете получить ссылку (&) на содержимое Cell, поэтому правила Rust не нарушаются.


use std::cell::Cell;

struct Metric {
count: Cell<u64>, // Zero overhead
}

impl Metric {
fn inc(&self) {
// Мы меняем значение по неизменяемой ссылке &self
let current = self.count.get();
self.count.set(current + 1);
}
}



Это атомарно? Нет. Это работает только в одном потоке. Но для однопоточного кода (или внутри thread_local) это самая быстрая мутабельность, которая только возможна.

#rust #std #optimization #interior_mutability

👉 @rust_lib
👍1361🥰1🤗1
🗝 Турбируем HashMap

Стандартный std::collections::HashMap в Rust использует алгоритм хэширования SipHash.
Почему? Он криптографически стоек к HashDoS атакам (когда злоумышленник подбирает ключи так, чтобы все они попадали в один бакет, превращая мапу в связный список и убивая CPU).

Но SipHash медленный. Если вы решаете алгоритмические задачи, пишете геймдев или у вас внутренний сервис без внешнего доступа - вам не нужна защита от DoS. Вам нужна скорость.

Используйте сторонние хэшеры. Самые популярные:

1. FxHash (rustc-hash) - используется внутри самого компилятора Rust и Firefox. Молниеносно быстрый для коротких ключей (integers).
2. AHash - современный, быстрый, использует аппаратные инструкции AES.

Пример с крейтом rustc-hash:


use rustc_hash::FxHashMap;

// Это та же HashMap, но с быстрым хэшером
let mut map: FxHashMap<u32, &str> = FxHashMap::default();



Прирост производительности на операциях вставки/поиска может достигать 2x-3x на простых ключах.

Итог:

• Backend наружу? Оставляем дефолтный SipHash.
• GameDev / Algo / Internal Data Processing? Ставим FxHash или AHash.

#rust #performance #hashmap #external_crates

👉 @rust_lib
👍176🥰21🏆1
🔮 Ты не пройдешь: Магия предсказателя ветвлений

Вы когда-нибудь задумывались, почему обработка отсортированного массива чисел происходит в разы быстрее, чем случайного, даже если логика одна и та же?

Всё дело в конвейере (pipeline). Процессор выполняет инструкции не по одной, а потоком: пока одна декодируется, другая уже выполняется. Но тут появляется if (ветвление).

Процессор не знает, пойдет код в then или в else, пока не вычислит условие. Но ждать он не может - конвейер встанет. Поэтому он угадывает.

• Угадал? Выполнение продолжается без задержек.

• Не угадал? Происходит Pipeline Flush. Процессор выбрасывает все инструкции, которые успел "набрать" по неверному пути, и начинает заново с правильного адреса. Это огромная потеря тактов (10-20 циклов CPU).

Пример на Rust:


// Если data отсортирован: T T T T T F F F F F (паттерн ясен)
// Если data случайный: T F T T F F T F (паттерн непредсказуем)
for &x in &data {
if x > 128 {
sum += x;
}
}



На случайных данных BPU ошибается в 50% случаев. На сортированных почти никогда.

Вывод: Чем предсказуемее ваши данные, тем быстрее работает ваш код. Иногда data.sort() перед обработкой окупается с лихвой.

#rust #cpu #lowlevel #performance #branch_prediction

👉 @rust_lib
👍21👎321🥰1
🥶 Холодный расчет: #[cold] и likely

Компилятор Rust (LLVM) очень умный, но иногда мы знаем о поведении программы больше, чем он. Мы можем подсказать ему, какая ветка кода будет выполняться чаще.

1. Атрибут #[cold]
Идеально для обработки ошибок. Вы говорите компилятору: "Сюда мы будем заходить крайне редко".
Компилятор сдвинет этот код в "дальний угол" бинарника, чтобы "горячий" путь шел линейно в памяти (это улучшает работу кэша инструкций и BPU).

#[cold] // <-- Подсказка
fn handle_fatal_error() {
// Логирование, паника, сложная логика...
}

fn process(data: &str) {
if let Err(_) = parse(data) {
handle_fatal_error(); // Компилятор знает, что это маловероятно
return;
}
// Happy path идет дальше линейно
}




2. likely / unlikely (Nightly / Crates)
В стабильном Rust пока нет std::intrinsics::likely, но есть крейт likely или хаки. Это прямая инструкция процессору через LLVM.

// С крейтом `likely`
if likely(x > 0) {
// Оптимизатор будет строить код так,
// будто это условие почти всегда true
}



Используйте #[cold] для ошибок и граничных случаев. Это стабильно, бесплатно и делает код чище.

#rust #optimization #llvm #hints

👉 @rust_lib
👍162🥰21🔥1🤗1
🚫 Убиваем if: Branchless Programming

Самый быстрый if - это отсутствие if.
Иногда можно заменить ветвление на арифметику. Это гарантирует отсутствие Pipeline Flush, так как поток инструкций линейный.

Задача: Вернуть a, если condition true, и b, если false.

С ветвлением:


let result = if condition { a } else { b };
// Генерирует инструкцию перехода (JMP/JNE) -> Риск misprediction



Branchless (без ветвления):
В Rust bool можно скастить в u8 (true = 1, false = 0).


// (a * 1) + (b * 0) = a
// (a * 0) + (b * 1) = b
let result = a * (condition as i32) + b * (!condition as i32);



Но подождите!
Современные компиляторы часто делают это сами. Они превращают простой if в инструкцию CMOV (Conditional Move). Это инструкция процессора: "Скопируй данные, если флаг выставлен". Это не ветвление, это просто копирование по условию.

Как проверить?
Смотрите в ассемблер (через godbolt или cargo-show-asm). Если видите cmov - поздравляю, у вас branchless код, и он будет работать быстро, даже если данные случайны. Если видите jle / jne - это прыжок.

Совет: Пишите читаемый код. И только если профайлер показывает, что BPU захлебывается в горячем цикле - переписывайте на арифметику.

#rust #assembly #branchless #performance

👉 @rust_lib
👍143🔥21🥰1🤗1
🏎️ SIMD для ленивых: Автовекторизация в Rust

SIMD - это когда процессор одной инструкцией складывает не два числа, а сразу 4, 8 или 16.

Многие думают, что SIMD это сложно и требует unsafe. Но лучший SIMD тот, который LLVM написал за вас.

Допустим, у вас есть цикл:


pub fn add_arrays(a: &[f32], b: &[f32], out: &mut [f32]) {
// Простые смертные пишут так:
for i in 0..a.len() {
out[i] = a[i] + b[i];
}
}



Если вы скомпилируете это в релизе для x86_64, компилятор сгенерирует инструкции SSE2 (обрабатывая по 4 числа f32 за раз). Почему? Потому что SSE2 гарантированно есть на всех 64-битных процессорах Intel/AMD.

Как сделать еще быстрее?
Если вы знаете, что ваш код будет работать на современном железе, разрешите компилятору использовать AVX2 (по 8 чисел f32).


RUSTFLAGS="-C target-cpu=native" cargo build --release



Но есть нюанс: Компилятор очень осторожен. Он не применит SIMD, если:

1. Есть риск паники. Если срезы разной длины, компилятор вставит проверки границ (bounds check) внутри цикла. Это убивает векторизацию.
Лечение: Используйте итераторы (zip) или явно проверяйте длины до цикла и используйте assert!, чтобы дать хинт оптимизатору.


2. Есть риск алиасинга. Если out пересекается в памяти с a или b.
Лечение: Rust тут молодец, его правила заимствования гарантируют отсутствие мутабельного алиасинга.

Всегда смотрите в ассемблер (cargo-show-asm), чтобы убедиться, что цикл развернулся в SIMD-инструкции (ищите что-то вроде vaddps вместо addss).

#rust #simd #performance #llvm #autovectorization

👉 @rust_lib
👍138🥰1🤗1
💀 Хардкорный режим: std::arch и интринсики

Автовекторизация не сработала? Алгоритм слишком сложен для LLVM? Придется марать руки.

Встречайте std::arch. Это модуль, дающий доступ к конкретным инструкциям процессора (интринсикам).
Это очень unsafe.
Это не портабельно (код для x86 не заработает на ARM M1).

Как выглядит сложение массивов на AVX2 (x86_64):


#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;

// Атрибут важен! Он говорит компилятору, что внутри этой функции
// можно использовать 256-битные регистры.
#[target_feature(enable = "avx2")]
unsafe fn add_avx2(a: &[f32], b: &[f32], out: &mut [f32]) {
// Мы обязаны обрабатывать данные чанками по 8 штук.
// (Опустим проверки длин и обработку "хвостов" для краткости)
for i in (0..a.len()).step_by(8) {
// Загружаем 8 float'ов в 256-битный регистр
let vec_a = _mm256_loadu_ps(a.as_ptr().add(i));
let vec_b = _mm256_loadu_ps(b.as_ptr().add(i));

// ОДНА инструкция сложения для 8 пар чисел
let vec_res = _mm256_add_ps(vec_a, vec_b);

// Выгружаем обратно в память
_mm256_storeu_ps(out.as_mut_ptr().add(i), vec_res);
}
}



Главная опасность: Если вы вызовете эту функцию на старом процессоре (без AVX2), программа упадет с ошибкой "Illegal Instruction".

Решение: Runtime detection. Вы должны проверять фичи процессора в рантайме.


fn add_smart(a: &[f32], b: &[f32], out: &mut [f32]) {
if is_x86_feature_detected!("avx2") {
// Безопасно, мы проверили
unsafe { add_avx2(a, b, out) };
} else {
// Медленный фоллбэк для старого железа
add_scalar(a, b, out);
}
}



Это сложно, больно, но дает максимальный контроль над железом.

#rust #simd #unsafe #lowlevel #intrinsics

👉 @rust_lib
👍9🔥3🙈21🥰1🤗1
🚀 Portable SIMD: Пишем один раз, работает везде

В прошлом посте вы видели ужас ручных интринсиков. Представьте, что вам нужно поддерживать SSE2, AVX2, AVX-512, да еще и ARM NEON для макбуков. Это ад из cfg_if и дублирования кода.

Rust сообщество решает эту проблему. Встречайте Portable SIMD (сейчас это крейт portable-simd или фича #![feature(portable_simd)] в nightly).

Идея: мы используем абстрактные типы "широких" чисел, а компилятор сам мапит их на лучшие инструкции целевой платформы.

Выглядит это потрясающе чисто:


#![feature(portable_simd)] // Нужен nightly Rust
use std::simd::{f32x8, Simd};

fn add_portable(a: &[f32], b: &[f32], out: &mut [f32]) {
// Берем чанки по 8 элементов
let chunks_a = a.chunks_exact(8);
let chunks_b = b.chunks_exact(8);
let chunks_out = out.chunks_exact_mut(8);

for ((slice_a, slice_b), slice_out) in chunks_a.zip(chunks_b).zip(chunks_out) {
// Превращаем слайсы в SIMD-вектора (почти бесплатно)
let vec_a = f32x8::from_slice(slice_a);
let vec_b = f32x8::from_slice(slice_b);

// Обычный оператор "+" работает как SIMD сложение!
let vec_res = vec_a + vec_b;

// Записываем обратно
vec_res.copy_to_slice(slice_out);
}
// + нужно обработать "хвост", если длина не кратна 8
}



Никакого unsafe! Никаких _mm256_ugly_names!
Если вы компилируете это под AVX2 - будет AVX2. Под ARM NEON - будет NEON.

Это будущее высокопроизводительного Rust. Ждем стабилизации.

#rust #simd #nightly #future

👉 @rust_lib
🔥222👍1🥰1🤗1
🪄 Разрушаем магию: Твоя Future - это просто enum

Многие приходят в Rust из Go или Erlang и думают, что async запускает легковесный поток (горутину). Забудьте. Асинхронный Rust работает совершенно иначе - он не аллоцирует стек под каждую задачу.

Когда вы пишете async fn, компилятор превращает вашу функцию в конечный автомат (State Machine).

Смотрим код:


async fn fetch_data() {
let req = build_request();
let resp = send(req).await; // <-- Точка остановки 1
process(resp).await; // <-- Точка остановки 2
}



Под капотом LLVM генерирует невидимый enum, который хранит локальные переменные между вызовами .await:


// Примерно так это видит компилятор:
enum FetchDataStateMachine {
Start,
WaitSend { req: Request }, // Храним локальные переменные
WaitProcess { resp: Response },
Done,
}



Когда рантайм вызывает метод poll() у этой Future:

1. Выполняется кусок кода до первого .await.
2. Состояние переключается (в WaitSend).
3. Управление возвращается обратно в рантайм! (Никакого блокирования потока).

Почему это круто? Такой enum весит считанные байты. Вы можете создать миллион таких Future на обычном ноутбуке, и они займут всего пару десятков мегабайт оперативки. Никакая ОС не позволит вам создать миллион настоящих потоков.

#rust #async #under_the_hood #future

👉 @rust_lib
👍1773🥰1🤗1
Хватит дергать: Waker и паттерн пейджера

Окей, Future вернула управление (сказала Poll::Pending). Но как рантайм узнает, что данные по сети пришли и пора снова вызвать poll()? Если он будет постоянно опрашивать все Future в цикле (while loop), ваш CPU улетит в космос на 100% загрузке.

Здесь на сцену выходят Executor, Reactor и Waker.

Executor - это менеджер задач. Он берет Future и вызывает у нее poll().
Reactor - это слушатель ОС (обертка над epoll в Linux, kqueue в macOS или IOCP в Windows). Он ждет событий от сетевой карты или диска.
Waker - это тот самый пейджер, который выдают в бургерной.

Как это работает:

1. Future делает сетевой запрос и понимает, что данных еще нет.

2. Она регистрирует этот запрос в Reactor и отдает ему свой Waker (колбэк, в котором зашит ID этой Future).

3. Future возвращает Pending. Executor забывает про неё и идет выполнять другие задачи.

4. ...проходит время... Сетевая карта получает пакеты. ОС дергает epoll.

5. Reactor видит: "Ага, пришли данные для сокета X". Он берет Waker, привязанный к этому сокету, и вызывает waker.wake().

6. Waker сигнализирует Executor'у: "Задача #42 готова, помести её обратно в очередь на выполнение".

7. Executor снова вызывает poll(), и стейт-машина делает следующий шаг.

Никакого холостого хода CPU. Только чистая событийная архитектура.

#rust #async #waker #tokio #epoll

👉 @rust_lib
👍9🔥42🥰2👨‍💻1🤗1
🚀 Подборка полезных IT каналов в Max


Системное администрирование, DevOps 📌

https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.

1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С

Программирование C++📌

https://max.ru/cpp_lib Библиотека C/C++ разработчика

Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика

Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика

GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub

Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных

Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков

Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов

Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻

Шутки программистов 📌
https://max.ru/itumor Шутки программистов

Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free

Книги, статьи для дизайнеров 📌

https://max.ru/odesigners Статьи, книги для дизайнеров

Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике

Вакансии 📌
https://max.ru/progjob Вакансии в IT

Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных


Бонус 📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
💩25👎11🤡6👍2🤣2🤬1🙈1
🥷 Кража во спасение: Work Stealing в Tokio

Если вы используете tokio::main, вы запускаете многопоточный рантайм (Multi-Threaded Scheduler). Под капотом создается пул потоков (воркеров), обычно по числу ядер процессора.

Как честно распределить 100,000 задач между 8 потоками, чтобы никто не простаивал и не было узкого горлышка (bottleneck)?

Если сделать одну Глобальную очередь (Global Queue), куда смотрят все потоки, они передерутся за Мьютекс (Mutex contention). Производительность рухнет.

Поэтому Tokio использует Локальные очереди (Local Queues):

1. У каждого потока-воркера есть своя личная очередь задач (до 256 штук).

2. Воркер берет задачи только из своей очереди. Никаких блокировок! Это невероятно быстро.

3. Новые задачи, порожденные текущей (tokio::spawn), падают в локальную очередь этого же потока.

А что если один поток закончил все задачи, а у другого их еще 200?
Включается Work Stealing (Кража работы):

1. Скучающий воркер смотрит в локальные очереди других потоков.

2. Он находит самую загруженную очередь и втихаря "крадет" ровно половину её задач.

3. Забирает их себе в локальную очередь и начинает молотить.

Это гениальный компромисс: 99% времени потоки работают без блокировок, и лишь изредка (при перекосе баланса) происходит синхронизация для кражи. Именно поэтому Tokio выдерживает миллионы RPS на мощных серверах.

#rust #tokio #architecture #multithreading #work_stealing

👉 @rust_lib
🔥171👍1🥰1🤗1
Wgpu - это безопасная и переносимая графическая библиотека для Rust, основанная на API WebGPU. Она подходит для универсальной графики и вычислений на GPU.

Приложения, использующие wgpu, могут работать нативно на Vulkan, Metal, DirectX 12 и OpenGL ES, а также в браузерах через WebAssembly на WebGPU и WebGL2.

https://wgpu.rs/

https://github.com/gfx-rs/wgpu

👉 @rust_lib
🔥92🥰1🤗1
💻Angular — один из самых строгих и системных инструментов разработки пользовательских интерфейсов. Его выбирают крупные компании, где важны масштабируемость, предсказуемость кода и возможность развивать продукт долгие годы. Мы расскажем вам как работать с этим инструментом. Записывайтесь на открытые уроки в преддверии старта курса «Angular-разработчик»:

📆26 марта в 20:00 МСК на открытом уроке разберём, как LLM ускоряют фронтенд-разработку. Покажем их развитие, подготовим Angular-проект к работе с ИИ, создадим приложение и обсудим, где ИИ реально помогает разработчику.

📆9 апреля в 20:00 МСК на открытом уроке разберём сигналы в Angular: создадим реактивную форму с валидацией, обсудим управление состоянием и сравним с подходом на RxJS.

📆21 апреля в 20:00 МСК разберём архитектуру Angular-приложения: слои, feature-подход, разделение UI, логики и API, а также паттерны и структуру реального проекта.

Подробности об уроках и регистрация: https://vk.cc/cVBvJD

Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576, www.otus.ru
🤮92🤔1🎉1🐳1🌭1
📦 Иллюзия объектов: Как мы морим процессор голодом

Представьте, что вы пишете симуляцию (или игру). По заветам ООП вы создаете сущность:


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
👍183🔥1💯1
🚀 Подборка полезных IT каналов в Max


Системное администрирование, DevOps 📌

https://max.ru/i_odmin Все для системного администратора
https://max.ru/bash_srv Bash Советы
https://max.ru/sysadminof Книги для админов, полезные материалы
https://max.ru/i_odmin_book Библиотека Системного Администратора
https://max.ru/i_devops DevOps: Пишем о Docker, Kubernetes и др.

1C разработка 📌
https://max.ru/odin1c_rus Cтатьи, курсы, советы, шаблоны кода 1С

Программирование C++📌

https://max.ru/cpp_lib Библиотека C/C++ разработчика

Программирование Go📌
https://max.ru/golang_lib Библиотека Go (Golang) разработчика

Программирование React📌
https://max.ru/react_lib React

Программирование Python 📌
https://max.ru/python_of Python академия.
https://max.ru/BookPython Библиотека Python разработчика

Java разработка 📌
https://max.ru/bookjava Библиотека Java разработчика

GitHub Сообщество 📌
https://max.ru/githublib Интересное из GitHub

Базы данных (Data Base) 📌
https://max.ru/database_info Все про базы данных

Фронтенд разработка 📌
https://max.ru/frontend_1 Подборки для frontend разработчиков

Библиотеки 📌
https://max.ru/programmist_of Книги по программированию
https://max.ru/proglb Библиотека программиста
https://max.ru/bfbook Книги для программистов

Программирование 📌
https://max.ru/bookflow Лекции, видеоуроки, доклады с IT конференций
https://max.ru/itmozg Программисты, дизайнеры, новости из мира IT
https://max.ru/php_lib Библиотека PHP программиста 👨🏼‍💻👩‍💻

Шутки программистов 📌
https://max.ru/itumor Шутки программистов

Защита, взлом, безопасность 📌
https://max.ru/thehaking Канал о кибербезопасности
https://max.ru/xakkep_1 Хакер Free

Книги, статьи для дизайнеров 📌

https://max.ru/odesigners Статьи, книги для дизайнеров

Математика 📌
https://max.ru/Pomatematike Канал по математике
https://max.ru/phismat_1 Обучающие видео, книги по Физике и Математике

Вакансии 📌
https://max.ru/progjob Вакансии в IT

Мир технологий 📌
https://max.ru/mir_teh Канал для любознательных


Бонус 📌
https://max.ru/piterspb_78 Свежие новости Санкт-Петербурга
https://max.ru/mockva_life Свежие новости Москвы
💩15🤡41🌭1🍌1🖕1