🚀 Portable SIMD: Пишем один раз, работает везде
В прошлом посте вы видели ужас ручных интринсиков. Представьте, что вам нужно поддерживать SSE2, AVX2, AVX-512, да еще и ARM NEON для макбуков. Это ад из
Rust сообщество решает эту проблему. Встречайте Portable SIMD (сейчас это крейт
Идея: мы используем абстрактные типы "широких" чисел, а компилятор сам мапит их на лучшие инструкции целевой платформы.
Выглядит это потрясающе чисто:
Никакого
Если вы компилируете это под AVX2 - будет AVX2. Под ARM NEON - будет NEON.
Это будущее высокопроизводительного Rust. Ждем стабилизации.
#rust #simd #nightly #future
👉 @rust_lib
В прошлом посте вы видели ужас ручных интринсиков. Представьте, что вам нужно поддерживать 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
🔥22❤2👍1🥰1🤗1
🪄 Разрушаем магию: Твоя Future - это просто
Многие приходят в Rust из Go или Erlang и думают, что
Когда вы пишете
Смотрим код:
Под капотом LLVM генерирует невидимый
Когда рантайм вызывает метод
1. Выполняется кусок кода до первого
2. Состояние переключается (в
3. Управление возвращается обратно в рантайм! (Никакого блокирования потока).
Почему это круто? Такой
#rust #async #under_the_hood #future
👉 @rust_lib
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
👍20✍7❤3🥰1🤗1