在架构的性能优化领域中,性能优化被分为了四个层次,分别是架构级性能优化,(子)系统间性能优化,数据结构和算法性能优化以及代码级性能优化。

分层的前两级都是涉及的架构层次的优化方案,分层的后两级则是纯代码层面的优化方案,利用代码编码的最佳实践,合理的数据结构和算法以及编译器优化指令,来达到充分榨取系统性能的目的。

代码级性能优化思路

  1. 使用性能工具(gprof、perf、Valgrind/Callgrind、VTune),找到真正影响的瓶颈
  2. 优化代码不牺牲代码可读性和维护性
  3. 考虑平台特性,兼容性等

提高性能的代码编码最佳实践

行优先访问

// 反例
for (int i = 0; i < N; i++)
    for (int j = 0; j < M; j++)
        sum += matrix[j][i];  // 列优先访问

// 正例:缓存友好
for (int i = 0; i < M; i++)
    for (int j = 0; j < N; j++)
        sum += matrix[i][j];  // 行优先访问

循环展开

  1. 在一些场景中,对于 for 循环,有时我们并非需要逐个遍历时,可以一次遍历多个元素
for (int i = 0; i < 100; i += 4) {
    data[i] = process(data[i]);
    data[i+1] = process(data[i+1]);
    data[i+2] = process(data[i+2]);
    data[i+3] = process(data[i+3]);
}
  1. 优化前,每次 for 循环的时候会进行函数名的解析和查找。优化后,函数地址在循环前已经确认,相当于通过函数指针进行调用。

    举例来说,现在你要通过地址簿查找老王家的地址,优化前,每次查找你都要去地址簿从头至尾找一遍。优化后,相当于你把老王家的地址记在了一个纸片片上,每次查找就是拿纸片即可。

// 优化前
for (auto& item : container) {
    result += expensive_function(item);
}

// 优化后
auto func = expensive_function; // 避免多次查表
for (auto& item : container) {
    result += func(item);
}

内联函数

  1. 短小的函数推荐使用内联
inline int square(int x) {
    return x * x;
}

// 正例:由于 inline 只是对编译器的建议,若要强制,则使用 constexpr
constexpr int compile_time_square(int x) {
    return x * x;
}

避免不必要的拷贝

  1. 通过如下等方式避免无意义的拷贝
    1. 引用和常量引用
    2. 移动语义
      1. std::move
      2. 移动构造函数和赋值运算符
    3. 返回值优化
    4. 完美转发
    5. string_view 和 span(c++17/20)
    6. 智能指针
    7. 就地构造

引用和常量引用

// 引用和常量引用
void process_data(const std::vector<int>& data) {  // 常量引用
    // 处理数据
}

移动语义

//  std::move
void process() {
    std::vector<std::string> data = create_strings();
    
    std::vector<std::string> moved_data = std::move(data);
    // data现在为空,moved_data接管了资源,没有拷贝
}
// 移动构造函数和赋值运算符
class MyClass {
private:
    std::vector<int> data_;
    
public:
    // 移动构造函数
    MyClass(MyClass&& other) noexcept 
        : data_(std::move(other.data_)) {}  // 移动vector,而非拷贝
    
    // 移动赋值运算符
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            data_ = std::move(other.data_);  // 移动赋值
        }
        return *this;
    }
};

返回值优化

// 命名返回值优化 (NRVO)
std::vector<int> create_vector() {
    std::vector<int> result;  // 命名的局部变量
    result.push_back(1);
    result.push_back(2);
    return result;  // 编译器优化,避免拷贝
}

// 返回值优化 (RVO)
std::string create_string() {
    return std::string("hello");  // 直接构造在调用处
}

完美转发

template<typename T>
void wrapper(T&& arg) {
    process(std::forward<T>(arg));  // 保持值类别
}

// 使用
std::string str = "hello";
wrapper(str);        // 传递左引用,无拷贝
wrapper("hello");    // 传递右值,无拷贝
wrapper(std::move(str)); // 传递右值引用,无拷贝

string_view 和 span(c++17/20)

// 避免字符串拷贝
void old_print(const std::string& str);  // 可能产生临时string的构造
void new_print(std::string_view str);    // ✓ 轻量级,无拷贝

// 避免容器拷贝
void process_array(std::span<int> data); // ✓ 视图,无拷贝

智能指针

class LargeObject {
    // 大数据成员...
};

// 避免拷贝大对象 - 使用智能指针共享
std::shared_ptr<LargeObject> obj = std::make_shared<LargeObject>();
std::shared_ptr<LargeObject> obj2 = obj;  // 共享所有权,无拷贝

就地构造

std::vector<std::string> vec;

// 避免临时对象拷贝
vec.push_back(std::string("hello"));  // 创建临时对象,然后移动
vec.emplace_back("hello");            // ✓ 直接构造,无拷贝无移动

std::map<int, std::string> map;
map.emplace(1, "hello");  // 直接构造pair,无拷贝

分支预测优化

// 大概率发生的场景前置
if (likely_condition) {  // 使用likely宏
    // 主要逻辑
} else {
    // 次要逻辑
}

短路径求值与提前退出

  1. 利用逻辑短路:&& 和 || 在满足条件时立即返回,避免后续计算
  2. 提前退出循环/函数:满足条件后立即返回或 break,减少不必要的执行
if (ptr != nullptr && ptr->isValid()) { /* ... */ }

预先计算和查找表

  1. 将计算结果提前存储在数组中,以空间换时间,避免运行时计算。记得一款古早的经典游戏就是采用的这种做法
// 优化前 - 每次都要计算sin
float slow_sin(float angle) {
    return std::sin(angle);
}

// 优化后 - 使用查找表
class SinTable {
private:
    static const int TABLE_SIZE = 3600; // 0.1度精度
    float table[TABLE_SIZE];
    
public:
    SinTable() {
        for (int i = 0; i < TABLE_SIZE; ++i) {
            table[i] = std::sin(i * M_PI / 1800.0); // 0.1度转弧度
        }
    }
    
    float fast_sin(float angle_degrees) {
        int index = static_cast<int>(angle_degrees * 10) % TABLE_SIZE;
        if (index < 0) index += TABLE_SIZE;
        return table[index];
    }
};

// 使用
SinTable sin_table;
float result = sin_table.fast_sin(45.5f); // 直接查表,避免计算

内存管理优化

  1. 避免频繁的动态内存分配:频繁 new/delete 会带来内存碎片和性能开销,推荐使用对象池、内存池、栈上分配或预分配
  // 对象池示例
  ObjectPool pool;
  void* obj = pool.allocate();
  // 使用 obj
  pool.deallocate(obj);

延迟计算

待更新

批处理操作

数据结构与算法优化

数据结构优化

  1. 充分了解常用容器的使用场景,合理使用这些数据结构
    1. std::vector:适合随机访问
    2. std::unordered_map:适合查找

算法选择优化

// 根据数据规模选择算法
void sort_data(std::vector<int>& data) {
    if (data.size() < 50) {
        insertion_sort(data);      // 小数据用插入排序
    } else if (data.size() < 1000) {
        quick_sort(data);          // 中等数据用快速排序
    } else {
        std::sort(data.begin(), data.end());  // 大数据用内排序
    }
}

编译器优化

编译器优化选项

  1. 使用 -O2、-O3 等编译选项:让编译器自动优化代码,如循环展开、常量折叠等
  2. 针对特定 CPU 优化:-march=native 等选项可生成针对本机优化的代码
 g++ -O2 -o program program.cpp

编译器指令优化

// 强制内联
__attribute__((always_inline)) void critical_function() {
    // 关键代码
}

// 热路径优化
__attribute__((hot)) void frequently_called_function() {
    // 频繁调用的函数
}

// 冷路径优化  
__attribute__((cold)) void rarely_called_function() {
    // 很少调用的函数
}

内存对齐

// 结构体对齐优化
struct alignas(64) CacheLineAligned {
    int data[16];  // 对齐到缓存行大小
};

// 避免false sharing
struct ThreadData {
    alignas(64) int local_counter;  // 每个线程独立缓存行
    // ... 其他数据
};
Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐