🚀 Микро-оптимизация в C++20: Early Return + Атрибуты вероятности
В прошлом посте мы разобрали, как Early Return (ранний возврат) спасает нас от вложенных
Встречайте атрибуты
🧠 В чем суть?
Современные процессоры пытаются предсказать, какую ветку кода программа выполнит следующей (Branch Prediction). Если процессор угадал - всё летает. Если ошибся - теряем такты на очистку конвейера.
С помощью атрибутов мы даем компилятору (и процессору) «инсайд»: какая ветка будет выполняться чаще.
🛠 Как это выглядит в коде?
Обычно ошибки и проверки аргументов (Guard Clauses) срабатывают редко. Это идеальное место для
⚙️ Что происходит под капотом?
Компилятор переставит инструкции ассемблера так, чтобы «счастливый путь» шел линейно, без прыжков (jmp), что улучшает работу кэша инструкций. Код обработки ошибок (ветка
⚠️ Важный нюанс:
Используйте это только тогда, когда вы уверены в вероятностях (например, ошибки случаются в 1 случае из 1000). Если поставить атрибуты наугад, можно сделать только хуже (pessimization).
🔥 Итог:
Чистый код (
#cpp #cpp20 #coding #optimization #tips #programming
➡️ @cpp_geek
В прошлом посте мы разобрали, как Early Return (ранний возврат) спасает нас от вложенных
if и делает код чище. Но в C++20 мы можем сделать этот код еще и потенциально быстрее!Встречайте атрибуты
[[likely]] и [[unlikely]].🧠 В чем суть?
Современные процессоры пытаются предсказать, какую ветку кода программа выполнит следующей (Branch Prediction). Если процессор угадал - всё летает. Если ошибся - теряем такты на очистку конвейера.
С помощью атрибутов мы даем компилятору (и процессору) «инсайд»: какая ветка будет выполняться чаще.
🛠 Как это выглядит в коде?
Обычно ошибки и проверки аргументов (Guard Clauses) срабатывают редко. Это идеальное место для
[[unlikely]].
void ProcessImage(Image* img) {
// 1. Проверка на null.
// Это случается редко, помечаем как "маловероятно".
if (img == nullptr) [[unlikely]] {
return; // Компилятор уведет этот код "подальше" из горячего пути
}
// 2. Еще одна проверка
if (img->IsEmpty()) [[unlikely]] {
return;
}
// --- Happy Path ---
// Процессор сразу прыгнет сюда, ожидая, что проверки выше ложны.
img->ApplyFilter();
img->Save();
}
⚙️ Что происходит под капотом?
Компилятор переставит инструкции ассемблера так, чтобы «счастливый путь» шел линейно, без прыжков (jmp), что улучшает работу кэша инструкций. Код обработки ошибок (ветка
[[unlikely]]) будет сдвинут в конец функции или в «холодную» зону.⚠️ Важный нюанс:
Используйте это только тогда, когда вы уверены в вероятностях (например, ошибки случаются в 1 случае из 1000). Если поставить атрибуты наугад, можно сделать только хуже (pessimization).
🔥 Итог:
Чистый код (
Early Return) + Подсказки компилятору ([[unlikely]]) = Читаемость и Производительность.#cpp #cpp20 #coding #optimization #tips #programming
➡️ @cpp_geek
🔥12
✂️ C++17: Перестаньте копировать строки! (
Мы привыкли передавать строки в функции по константной ссылке:
Не всегда. 🛑
Если вы передаете в такую функцию обычный текст в кавычках (строковый литерал) или часть другой строки, C++ втайне от вас создаст временный объект
Решение?
👀 Что это такое?
Никаких аллокаций. Никаких копий. Ноль оверхеда.
🆚 Сравним:
🔥 Суперсила: Substrings без боли
Самое вкусное начинается, когда нужно взять подстроку.
⚫️
⚫️
⚠️ Осторожно! (Подводный камень)
Так как
⚫️ ✅ Использовать как аргумент функции.
⚫️ ❌ Возвращать из функции, если исходная строка была локальной переменной.
💡 Итог:
Если вам нужно только «почитать» строку (в аргументах функции), почти всегда используйте
#cpp #cpp17 #optimization #stringview #coding #tips
➡️ @cpp_geek
std::string_view)Мы привыкли передавать строки в функции по константной ссылке:
const std::string&. Нам кажется, что это эффективно, ведь мы не копируем объект, верно?Не всегда. 🛑
Если вы передаете в такую функцию обычный текст в кавычках (строковый литерал) или часть другой строки, C++ втайне от вас создаст временный объект
std::string, выделит память в куче (heap allocation), скопирует туда данные и только потом передаст ссылку.Решение?
std::string_view.👀 Что это такое?
std::string_view - это супер-легкий объект, который ничего не хранит сам. Он просто «смотрит» на существующую строку. Внутри него только указатель на начало текста и длина.Никаких аллокаций. Никаких копий. Ноль оверхеда.
🆚 Сравним:
// 🐢 ПЛОХО (до C++17)
void Log(const std::string& msg) { /* ... */ }
// При вызове создается временный std::string!
Log("Critical Error");
// 🚀 ХОРОШО (C++17)
void Log(std::string_view msg) { /* ... */ }
// Никаких аллокаций. Просто передаем указатель и длину.
Log("Critical Error");
🔥 Суперсила: Substrings без боли
Самое вкусное начинается, когда нужно взять подстроку.
std::string::substr() - создает новую строку (копирование + аллокация).std::string_view::substr() - просто сдвигает указатель и меняет размер (математическая операция за наносекунды).⚠️ Осторожно! (Подводный камень)
Так как
string_view не владеет данными, а только смотрит на них, вы должны быть уверены, что исходная строка живет дольше, чем string_view.💡 Итог:
Если вам нужно только «почитать» строку (в аргументах функции), почти всегда используйте
std::string_view вместо const std::string&.#cpp #cpp17 #optimization #stringview #coding #tips
➡️ @cpp_geek
Please open Telegram to view this post
VIEW IN TELEGRAM
👍9❤4
🏗 Что на самом деле происходит, когда
Мы все любим
Происходит Реаллокация (Reallocation). И это гораздо дороже, чем кажется.
⚙️ Алгоритм катастрофы:
1. Поиск новой земли: Вектор понимает, что места нет. Он обращается к оперативной памяти и просит выделить новый блок памяти. Обычно он в 1.5 или 2 раза больше текущего.
2. Великое переселение: Все элементы из старого блока памяти копируются (или перемещаются, если есть
⚫️ Если у вас там 1,000,000 тяжелых объектов - удачи процессору. 😅
3. Уничтожение: Для всех объектов в старом блоке вызываются деструкторы.
4. Снос: Старый блок памяти возвращается системе.
🚨 Почему это проблема?
1. Удар по производительности:
Обычно
2. Инвалидация итераторов и ссылок (ОПАСНО):
Это источник багов №1.
🛡 Как лечить?
Если вы хотя бы примерно знаете, сколько элементов будет в векторе, всегда используйте
💡 Итог:
#cpp #stdvector #performance #memory #coding #tips
➡️ @cpp_geek
std::vector «лопается»?Мы все любим
push_back. Это удобно: кидаешь данные в вектор, а он сам разбирается с памятью. Но что происходит, когда вы добавляете элемент, а capacity (вместимость) вектора закончилась?Происходит Реаллокация (Reallocation). И это гораздо дороже, чем кажется.
⚙️ Алгоритм катастрофы:
1. Поиск новой земли: Вектор понимает, что места нет. Он обращается к оперативной памяти и просит выделить новый блок памяти. Обычно он в 1.5 или 2 раза больше текущего.
2. Великое переселение: Все элементы из старого блока памяти копируются (или перемещаются, если есть
noexcept move-конструктор) в новый блок.3. Уничтожение: Для всех объектов в старом блоке вызываются деструкторы.
4. Снос: Старый блок памяти возвращается системе.
🚨 Почему это проблема?
1. Удар по производительности:
Обычно
push_back работает за O(1) (мгновенно). Но в момент реаллокации сложность подскакивает до O(N). Это создает непредсказуемые лаги (latency spikes). В системах реального времени (gamedev, high-load) это недопустимо.2. Инвалидация итераторов и ссылок (ОПАСНО):
Это источник багов №1.
std::vector<int> vec = {1, 2, 3};
int& ref = vec[0]; // Ссылка на первый элемент
// ... добавляем много элементов ...
for(int i=0; i < 100; ++i) vec.push_back(i);
// 💥 Вектор переехал в новую память.
// Старая память удалена. ref теперь указывает в мусор.
std::cout << ref; // Undefined Behavior (Crash)
🛡 Как лечить?
Если вы хотя бы примерно знаете, сколько элементов будет в векторе, всегда используйте
reserve().
std::vector<User> users;
users.reserve(1000); // Сразу выделяем память
// Теперь первые 1000 push_back будут дешевыми
// и не вызовут реаллокации.
💡 Итог:
std::vector это отличный инструмент, но за его «магию» расширения платит процессор. Помогайте ему через reserve().#cpp #stdvector #performance #memory #coding #tips
➡️ @cpp_geek
Please open Telegram to view this post
VIEW IN TELEGRAM
👍8❤4👀4💯1