https://gitverse.ru/nicktretyakov1/RustML Для того чтобы не крутить бесконечный while-цикл, постоянно вызывая poll() у всех Future. Нужно использовать связку Executor + Reactor + Waker. Если бы Executor просто в цикле делал: loop { for future in all_futures { future.poll(...); } } то при тысячах задач, большинство из которых ждут сеть/диск, CPU бы сгорал впустую на 100 %. Это особенно плохо и для обычных серверов, и для задач, где рядом крутятся тяжёлые вычисления (в т...
Один Rust не п...Rust
async/await это Enum
Когда вы пишете async fn, компилятор превращает функцию в структуру (или enum), которая реализует Future. Этот enum хранит: // То, что пишет программист async fn fetch_data(url: String) -> Result<Data, Error> { let req = build_request(&url); // локальная переменная let resp = client.send(req).await?; // ← точка приостановки let data = process(resp).await?; // ← ещё одна Ok(data) } Компилятор примерно генерирует такое: enum FetchDataStateMachine { // Начальное состояние (до первого await) Start { url: String, // захваченные параметры }, // После первого ...
Portable SIMD даёт большой выигрыш в ML
Portable SIMD в Rust — это абстракция над SIMD-инструкциями (Single Instruction, Multiple Data), которая позволяет писать код с «широкими» типами (f32x4, f32x8, f32x16 и т.д.), а компилятор/LLVM сам выбирает лучшие инструкции целевой архитектуры (AVX2/AVX-512 на x86, NEON на ARM, SIMD128 на WASM и т.д.). Portable SIMD (особенно std::simd на nightly или fearless_simd на stable) — это самый чистый и безопасный способ написания кода для CPU-инференса и предобработки в Rust. Он идеально подходит для: Для больших матриц всё равно лучше опираться на хорошо оттюненный BLAS или специализированные тайловые ядра, а Portable SIMD использовать внутри этих ядер или для всего остального...
SIMD в машинном обучении: как заставить LLVM работать за вас
90% ML-кода на Rust не требует ни unsafe, ни std::arch, ни интринсиков. Нужно лишь понимать, что мешает авто-векторизации, и убирать эти препятствия. Ниже — подробный разбор с реальными паттернами для ML. LLVM проходит по циклу и пытается доказать, что итерации независимы: Если всё сходится — LLVM генерирует «vectorized loop» + «scalar epilogue» для хвоста. 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 с -C opt-level=3 LLVM сгенерирует SSE2 (addps) — по 4 f32 за инструкцию. Проверить можно через cargo asm или godbolt...
std::arch для ML
Для 90% ML-задач в Rust лучше использовать — faer, candle, burn, ndarray с matrixmultiply, llama.cpp биндинги. Поскольку уже содержат оптимизированные SIMD-ядра, runtime-диспетчеризацию и протестированы на десятках платформ. std::arch стоит использовать, когда вы пишете свой inference-движок, исследовательский прототип с нестандартными операциями, или оптимизируете конкретное ядро, которое библиотеки не покрывают. std::arch — это модуль стандартной библиотеки Rust, дающий прямой доступ к интринсикам (intrinsics) — функциям, которые компилируются в конкретные инструкции процессора (SIMD, FMA, крипто, битовые операции)...
Как заменить ветвление в ML (branchless)
let result = if condition { a } else { b }; Компилятор обычно генерирует JMP / JNE / JLE. Когда условие почти случайное (сравнения, выбор между двумя значениями), Branch Prediction Unit постоянно ошибается → pipeline flush → сильное замедление. В Rust bool можно безопасно привести к целому: // Выбираем a, если condition == true, иначе b let result = a * (condition as i32) + b * (!condition as i32); Или чуть аккуратнее (избегаем лишнего отрицания): let mask = condition as i32; // 0 или 1 let result = a * mask + b * (1 - mask); Для f32/f64 то же самое: let mask = condition as f32; // 0...
