乐闻世界logo
搜索文章和话题

C++ 性能优化技巧有哪些

2月18日 17:38

C++ 性能优化技巧

C++ 以其高性能著称,但要充分发挥其性能潜力,需要掌握各种优化技巧和最佳实践。

编译器优化

编译选项:

bash
# 基本优化 g++ -O2 program.cpp -o program # 最高级别优化 g++ -O3 program.cpp -o program # 链接时优化(LTO) g++ -O3 -flto program.cpp -o program # 针对特定架构优化 g++ -O3 -march=native program.cpp -o program # 启用向量化 g++ -O3 -ftree-vectorize program.cpp -o program

内联函数:

cpp
// 显式内联 inline int add(int a, int b) { return a + b; } // 编译器自动内联(小函数) int multiply(int a, int b) { return a * b; } // 强制内联(GCC/Clang) __attribute__((always_inline)) int divide(int a, int b) { return a / b; } // 禁止内联 __attribute__((noinline)) void heavyFunction() { // 复杂计算 }

内存优化

数据局部性:

cpp
// 不推荐:缓存不友好 struct BadLayout { int a; char padding1[64]; // 浪费缓存行 int b; char padding2[64]; int c; }; // 推荐:缓存友好 struct GoodLayout { int a, b, c; // 紧凑排列 }; // 数组访问模式 void processArray(int* arr, size_t size) { // 推荐:顺序访问 for (size_t i = 0; i < size; ++i) { process(arr[i]); } // 不推荐:随机访问 for (size_t i = 0; i < size; ++i) { process(arr[randomIndex()]); } }

内存对齐:

cpp
// 使用 alignas 指定对齐 struct alignas(64) AlignedData { int data[16]; }; // 使用 aligned_alloc 分配对齐内存 void* alignedMemory = aligned_alloc(64, 1024); // 查询对齐要求 constexpr size_t alignment = alignof(double);

避免内存碎片:

cpp
// 推荐:对象池 template <typename T, size_t PoolSize> class ObjectPool { private: std::array<T, PoolSize> pool; std::bitset<PoolSize> used; public: T* allocate() { size_t index = used.find_first_not_set(); if (index != std::string::npos) { used.set(index); return &pool[index]; } return nullptr; } void deallocate(T* ptr) { size_t index = ptr - pool.data(); used.reset(index); } };

算法优化

避免不必要的拷贝:

cpp
// 不推荐:返回值拷贝 std::vector<int> createVector() { std::vector<int> temp; // 填充数据 return temp; // C++11 之前会拷贝 } // 推荐:移动语义 std::vector<int> createVector() { std::vector<int> temp; // 填充数据 return temp; // C++11 之后会移动 } // 使用移动语义 std::vector<int> vec = createVector(); // 移动构造

使用引用传递:

cpp
// 不推荐:值传递 void process(std::vector<int> data) { // 处理数据 } // 推荐:const 引用传递 void process(const std::vector<int>& data) { // 处理数据 } // 需要修改时使用引用 void modify(std::vector<int>& data) { // 修改数据 }

选择合适的容器:

cpp
// 需要随机访问:vector std::vector<int> vec; // 需要频繁两端插入:deque std::deque<int> dq; // 需要频繁中间插入删除:list std::list<int> lst; // 需要按键查找:map/unordered_map std::map<std::string, int> mp; std::unordered_map<std::string, int> ump; // 需要快速查找:set/unordered_set std::set<int> s; std::unordered_set<int> us;

并发优化

使用线程池:

cpp
class ThreadPool { private: std::vector<std::thread> workers; std::queue<std::function<void()>> tasks; std::mutex queueMutex; std::condition_variable condition; bool stop; public: ThreadPool(size_t threads) : stop(false) { for (size_t i = 0; i < threads; ++i) { workers.emplace_back([this] { while (true) { std::function<void()> task; { std::unique_lock<std::mutex> lock(queueMutex); condition.wait(lock, [this] { return stop || !tasks.empty(); }); if (stop && tasks.empty()) return; task = std::move(tasks.front()); tasks.pop(); } task(); } }); } } template <class F, class... Args> auto enqueue(F&& f, Args&&... args) -> std::future<typename std::result_of<F(Args...)>::type> { using return_type = typename std::result_of<F(Args...)>::type; auto task = std::make_shared<std::packaged_task<return_type()>>( std::bind(std::forward<F>(f), std::forward<Args>(args)...) ); std::future<return_type> res = task->get_future(); { std::unique_lock<std::mutex> lock(queueMutex); if (stop) throw std::runtime_error("enqueue on stopped ThreadPool"); tasks.emplace([task]() { (*task)(); }); } condition.notify_one(); return res; } ~ThreadPool() { { std::unique_lock<std::mutex> lock(queueMutex); stop = true; } condition.notify_all(); for (auto& worker : workers) { worker.join(); } } };

使用原子操作:

cpp
// 不推荐:使用互斥锁 std::mutex mtx; int counter = 0; void increment() { std::lock_guard<std::mutex> lock(mtx); ++counter; } // 推荐:使用原子操作 std::atomic<int> counter(0); void increment() { counter.fetch_add(1, std::memory_order_relaxed); }

减少锁竞争:

cpp
// 不推荐:全局锁 std::mutex globalMutex; std::map<int, int> globalMap; // 推荐:分段锁 class ShardedMap { private: static constexpr size_t NUM_SHARDS = 16; std::array<std::mutex, NUM_SHARDS> mutexes; std::array<std::map<int, int>, NUM_SHARDS> maps; size_t getShard(int key) { return key % NUM_SHARDS; } public: void insert(int key, int value) { size_t shard = getShard(key); std::lock_guard<std::mutex> lock(mutexes[shard]); maps[shard][key] = value; } bool find(int key, int& value) { size_t shard = getShard(key); std::lock_guard<std::mutex> lock(mutexes[shard]); auto it = maps[shard].find(key); if (it != maps[shard].end()) { value = it->second; return true; } return false; } };

SIMD 优化

使用编译器内置函数:

cpp
#include <immintrin.h> void addArrays(float* a, float* b, float* result, size_t size) { size_t i = 0; // 使用 AVX2 处理 8 个 float for (; i + 8 <= size; i += 8) { __m256 va = _mm256_loadu_ps(&a[i]); __m256 vb = _mm256_loadu_ps(&b[i]); __m256 vr = _mm256_add_ps(va, vb); _mm256_storeu_ps(&result[i], vr); } // 处理剩余元素 for (; i < size; ++i) { result[i] = a[i] + b[i]; } }

使用自动向量化:

cpp
// 编译器会自动向量化 void addArrays(float* __restrict__ a, float* __restrict__ b, float* __restrict__ result, size_t size) { #pragma omp simd for (size_t i = 0; i < size; ++i) { result[i] = a[i] + b[i]; } }

缓存优化

预取数据:

cpp
#include <xmmintrin.h> void processArray(int* arr, size_t size) { for (size_t i = 0; i < size; ++i) { // 预取下一个缓存行 if (i + 16 < size) { _mm_prefetch(&arr[i + 16], _MM_HINT_T0); } process(arr[i]); } }

缓存行对齐:

cpp
// 避免伪共享 struct alignas(64) Counter { std::atomic<int> value; char padding[64 - sizeof(std::atomic<int>)]; }; class SharedCounters { private: Counter counters[4]; public: void increment(int threadId) { counters[threadId % 4].value.fetch_add(1); } };

性能分析

使用性能分析工具:

bash
# 使用 perf(Linux) perf record -g ./your_program perf report # 使用 gprof g++ -pg program.cpp -o program ./program gprof program gmon.out > analysis.txt # 使用 Valgrind 的 callgrind valgrind --tool=callgrind ./your_program kcachegrind callgrind.out.<pid>

使用计时器:

cpp
#include <chrono> class Timer { private: std::chrono::high_resolution_clock::time_point start; public: Timer() : start(std::chrono::high_resolution_clock::now()) {} double elapsed() const { auto end = std::chrono::high_resolution_clock::now(); return std::chrono::duration<double>(end - start).count(); } }; // 使用 Timer timer; // 执行代码 std::cout << "Elapsed time: " << timer.elapsed() << " seconds" << std::endl;

最佳实践

1. 预分配内存

cpp
// 推荐 std::vector<int> vec; vec.reserve(10000); // 预先分配空间 // 不推荐 std::vector<int> vec; for (int i = 0; i < 10000; ++i) { vec.push_back(i); // 多次重新分配 }

2. 使用 emplace 代替 push

cpp
// 推荐 vec.emplace_back(arg1, arg2); // 原地构造 // 不推荐 vec.push_back(Type(arg1, arg2)); // 可能创建临时对象

3. 避免过早优化

cpp
// 先写清晰的代码 int sum = 0; for (int val : data) { sum += val; } // 性能分析后再优化 if (isPerformanceCritical) { // 使用 SIMD 或并行化 }

4. 使用 constexpr 进行编译期计算

cpp
// 推荐 constexpr int factorial(int n) { return n <= 1 ? 1 : n * factorial(n - 1); } constexpr int result = factorial(10); // 编译期计算

5. 避免虚函数调用开销

cpp
// 性能关键代码避免虚函数 class FastProcessor { public: void process(int value) { // 非虚函数 // 快速处理 } }; // 使用 CRTP 实现静态多态 template <typename Derived> class Base { public: void interface() { static_cast<Derived*>(this)->implementation(); } };

注意事项

  • 性能优化应该基于实际测量,而非猜测
  • 优先优化算法和数据结构,而非微优化
  • 考虑可读性和可维护性,不要过度优化
  • 使用性能分析工具找出真正的瓶颈
  • 注意不同编译器和平台的优化差异
  • 考虑使用现代 C++ 特性(如移动语义)提升性能
  • 在多线程环境中注意缓存一致性和内存屏障
标签:C++