🔮 Ты не пройдешь: Магия предсказателя ветвлений
Вы когда-нибудь задумывались, почему обработка отсортированного массива чисел происходит в разы быстрее, чем случайного, даже если логика одна и та же?
Всё дело в конвейере (pipeline). Процессор выполняет инструкции не по одной, а потоком: пока одна декодируется, другая уже выполняется. Но тут появляется
Процессор не знает, пойдет код в
• Угадал? Выполнение продолжается без задержек.
• Не угадал? Происходит Pipeline Flush. Процессор выбрасывает все инструкции, которые успел "набрать" по неверному пути, и начинает заново с правильного адреса. Это огромная потеря тактов (10-20 циклов CPU).
Пример на Rust:
На случайных данных BPU ошибается в 50% случаев. На сортированных почти никогда.
Вывод: Чем предсказуемее ваши данные, тем быстрее работает ваш код. Иногда
#rust #cpu #lowlevel #performance #branch_prediction
👉 @rust_lib
Вы когда-нибудь задумывались, почему обработка отсортированного массива чисел происходит в разы быстрее, чем случайного, даже если логика одна и та же?
Всё дело в конвейере (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👎3✍2❤1🥰1
💀 Хардкорный режим: std::arch и интринсики
Автовекторизация не сработала? Алгоритм слишком сложен для LLVM? Придется марать руки.
Встречайте
Это очень unsafe.
Это не портабельно (код для x86 не заработает на ARM M1).
Как выглядит сложение массивов на AVX2 (x86_64):
Главная опасность: Если вы вызовете эту функцию на старом процессоре (без AVX2), программа упадет с ошибкой "Illegal Instruction".
Решение: Runtime detection. Вы должны проверять фичи процессора в рантайме.
Это сложно, больно, но дает максимальный контроль над железом.
#rust #simd #unsafe #lowlevel #intrinsics
👉 @rust_lib
Автовекторизация не сработала? Алгоритм слишком сложен для 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🙈2❤1🥰1🤗1