Кэш-локальность
Современные CPU имеют несколько уровней кэша. Доступ к данным в кэше в 100 раз быстрее, чем к оперативной памяти.
▸Data-Oriented Design
1// ❌ Плохо: Structure of Arrays2struct Particle {3 float x, y, z;4 float vx, vy, vz;5 float mass;6};78std::vector<Particle> particles(100000);910// ✅ Хорошо: Array of Structures11struct Particles {12 std::vector<float> x, y, z;13 std::vector<float> vx, vy, vz;14 std::vector<float> mass;15};1617void updatePositions(Particles& p, float dt) {18 for (size_t i = 0; i < p.x.size(); ++i) {19 p.x[i] += p.vx[i] * dt;20 p.y[i] += p.vy[i] * dt;21 p.z[i] += p.vz[i] * dt;22 }23}
▸Prefetching
1for (size_t i = 0; i < n; ++i) {2 __builtin_prefetch(&data[i + 16]); // Prefetch 16 элементов вперед3 process(data[i]);4}
Управление памятью
▸Статическое выделение
1// Статические массивы не требуют аллокации2constexpr int BUFFER_SIZE = 1024;3int buffer[BUFFER_SIZE]; // На стеке45// std::array — безопасная альтернатива6std::array<int, 1024> arr;
▸Пул объектов
1template <typename T, size_t PoolSize>2class ObjectPool {3 alignas(T) char storage[sizeof(T) * PoolSize];4 std::vector<size_t> free_indices;5public:6 ObjectPool() {7 for (size_t i = 0; i < PoolSize; ++i) {8 free_indices.push_back(i);9 }10 }1112 T* allocate() {13 if (free_indices.empty()) return nullptr;14 size_t idx = free_indices.back();15 free_indices.pop_back();16 return reinterpret_cast<T*>(&storage[idx * sizeof(T)]);17 }1819 void deallocate(T* ptr) {20 size_t idx = (reinterpret_cast<char*>(ptr) - storage) / sizeof(T);21 free_indices.push_back(idx);22 }23};
SIMD оптимизации
1#include <immintrin.h>23// Суммирование массива с SSE4float sum_array_sse(const float* data, size_t n) {5 __m128 sum = _mm_setzero_ps();6 size_t i = 0;78 for (; i + 4 <= n; i += 4) {9 __m128 vec = _mm_loadu_ps(&data[i]);10 sum = _mm_add_ps(sum, vec);11 }1213 // Горизонтальное суммирование14 sum = _mm_hadd_ps(sum, sum);15 sum = _mm_hadd_ps(sum, sum);1617 float result;18 _mm_store_ss(&result, sum);1920 // Обработка остатка21 for (; i < n; ++i) {22 result += data[i];23 }2425 return result;26}
Компиляторные опции
▸GCC/Clang
1# Оптимизации2-O2 # Базовые оптимизации3-O3 # Агрессивные оптимизации4-Os # Оптимизация по размеру56# Профиль-guided оптимизации7-fprofile-generate8-fprofile-use910# LTO (Link Time Optimization)11-flto
▸Профилирование
1# GCC profiling2g++ -pg -O2 main.cpp3./a.out4gprof a.out gmon.out > analysis.txt56# perf (Linux)7perf record ./a.out8perf report910# Valgrind11valgrind --tool=callgrind ./a.out
Избегание ненужных копий
1// ❌ Копирование2void process(std::vector<int> v) { ... }34// ✅ Передача по ссылке5void process(const std::vector<int>& v) { ... }67// ✅ Move semantics8void process(std::vector<int>&& v) { ... }910// ✅ std::string_view (C++17)11void process(std::string_view sv) { ... }
Branch Prediction
1// ❌ Непредсказуемые ветвления2if (data[i] > threshold) { ... }34// ✅ Сортировка для предсказуемости5std::sort(data.begin(), data.end());6// Теперь ветвления более предсказуемы
Заключение
Производительность C++ определяется пониманием архитектуры CPU и правильным использованием языка. Кэш-локальность, управление памятью и SIMD-оптимизации могут ускорить код на порядки. Регулярное профилирование помогает найти узкие места.