#SIMD #железо #оптимизации #Python #C_плюс_плюс
В прошлом посте про CUDA мы обсуждали, что CPU это для последовательных задач, а GPU для параллельных вычислений.
Но это не вся правда. Твой центральный процессор тоже умеет считать параллельно, не прибегая к видеокарте (вы цены на них видели?! 🌟 ). И если ты не используешь эту возможность, твой код работает в лучшем случае на 10-20% от реальной мощности кристалла.
Сегодня разбираем SIMD (Single Instruction, Multiple Data).
Физика процесса: Как это работает в железе?🐸
В классической архитектуре (SISD - Single Instruction, Single Data) процесс вычисления выглядит так:
1. Процессор загружает число А в регистр (сверхбыстрая память внутри ядра, обычно 64 бита, если твоя машина 64-битный процессор).
2. Загружает число B во второй регистр.
3. ALU (арифметико-логическое устройство) получает команду ADD, складывает их за 1 такт и кладет результат обратно в регистр.
В этом сценарии, даже если у тебя 64-битный процессор, а ты складываешь 32-битные float, половина регистра просто пустует, либо используется неэффективно.
Инженеры решили, что так не пойдет и расширили регистры🤩
Так появились векторные регистры. Они имеют ширину 128, 256 или даже 512 бит.
SIMD (Single Instruction, Multiple Data) работает так:
Мы заполняем этот огромный 256-битный регистр сразу восемью числами float (по 32 бита). А затем ALU одной командой vaddps складывает их с другой восьмеркой чисел.
Итог: 8 операций сложения за тот же самый 1 такт процессора.
Зоопарк архитектур: Intel vs ARM🌟
У разных производителей свой набор инструкций для этого:
x86 (Intel / AMD):
• SSE (128 бит): База. 4 числа за раз. Есть везде.
• AVX / AVX2 (256 бит): Стандарт для современного софта. 8 чисел float или 4 double.
• AVX-512 (512 бит): Тяжелая артиллерия (Xeon, современные i9). 16 операций за такт. Нюанс: при активном использовании процессор может снижать частоту, чтобы не перегреться.
ARM (Apple Silicon / Server ARM):
• NEON (128 бит): Есть во всех айфонах и макбуках. Аналог SSE/AVX.
• SVE (Scalable Vector Extension): Более умная технология. В Intel ширина вектора жестко прошита. В SVE код пишется "агностически" к длине регистра. Процессор сам решает: "Я маленький чип, считаю по 128 бит" или "Я суперкомпьютер, считаю по 2048 бит".
Уровень C++: Как управлять этим вручную👨💻
Компиляторы (GCC/Clang) с флагом -O3 -march=native стараются сами найти циклы и превратить их в векторные инструкции (автовекторизация). Но они часто ошибаются.
Чтобы гарантировать скорость, используют интринсики (Intrinsics)(офигенное название, всегда нравилось) - псевдо-функции, которые транслируются прямо в инструкции CPU.
Пример на AVX2 (складываем пачками по 8 штук):
P.S. В серьезном продакшене делают Runtime Dispatch: программа при запуске делает CPUID, видит, что AVX2 нет, и переключается на медленную ветку SSE, чтобы не крашнуться с Illegal Instruction.
Продолжение⬇️
В прошлом посте про CUDA мы обсуждали, что CPU это для последовательных задач, а GPU для параллельных вычислений.
Но это не вся правда. Твой центральный процессор тоже умеет считать параллельно, не прибегая к видеокарте (
Сегодня разбираем SIMD (Single Instruction, Multiple Data).
Физика процесса: Как это работает в железе?
В классической архитектуре (SISD - Single Instruction, Single Data) процесс вычисления выглядит так:
1. Процессор загружает число А в регистр (сверхбыстрая память внутри ядра, обычно 64 бита, если твоя машина 64-битный процессор).
2. Загружает число B во второй регистр.
3. ALU (арифметико-логическое устройство) получает команду ADD, складывает их за 1 такт и кладет результат обратно в регистр.
В этом сценарии, даже если у тебя 64-битный процессор, а ты складываешь 32-битные float, половина регистра просто пустует, либо используется неэффективно.
Инженеры решили, что так не пойдет и расширили регистры
Так появились векторные регистры. Они имеют ширину 128, 256 или даже 512 бит.
SIMD (Single Instruction, Multiple Data) работает так:
Мы заполняем этот огромный 256-битный регистр сразу восемью числами float (по 32 бита). А затем ALU одной командой vaddps складывает их с другой восьмеркой чисел.
Итог: 8 операций сложения за тот же самый 1 такт процессора.
Зоопарк архитектур: Intel vs ARM
У разных производителей свой набор инструкций для этого:
x86 (Intel / AMD):
• SSE (128 бит): База. 4 числа за раз. Есть везде.
• AVX / AVX2 (256 бит): Стандарт для современного софта. 8 чисел float или 4 double.
• AVX-512 (512 бит): Тяжелая артиллерия (Xeon, современные i9). 16 операций за такт. Нюанс: при активном использовании процессор может снижать частоту, чтобы не перегреться.
ARM (Apple Silicon / Server ARM):
• NEON (128 бит): Есть во всех айфонах и макбуках. Аналог SSE/AVX.
• SVE (Scalable Vector Extension): Более умная технология. В Intel ширина вектора жестко прошита. В SVE код пишется "агностически" к длине регистра. Процессор сам решает: "Я маленький чип, считаю по 128 бит" или "Я суперкомпьютер, считаю по 2048 бит".
Уровень C++: Как управлять этим вручную
Компиляторы (GCC/Clang) с флагом -O3 -march=native стараются сами найти циклы и превратить их в векторные инструкции (автовекторизация). Но они часто ошибаются.
Чтобы гарантировать скорость, используют интринсики (Intrinsics)
Пример на AVX2 (складываем пачками по 8 штук):
#include <immintrin.h>
// Загружаем 256 бит данных в регистр
__m256 a_vec = _mm256_loadu_ps(arr_a);
__m256 b_vec = _mm256_loadu_ps(arr_b);
// Одна команда для сложения 8 пар чисел
__m256 res = _mm256_add_ps(a_vec, b_vec);
P.S. В серьезном продакшене делают Runtime Dispatch: программа при запуске делает CPUID, видит, что AVX2 нет, и переключается на медленную ветку SSE, чтобы не крашнуться с Illegal Instruction.
Продолжение
Please open Telegram to view this post
VIEW IN TELEGRAM
❤4👍3 2 1