跳至内容

优化 C++/编写高效代码/内存访问

来自维基教科书,开放世界中的开放书籍

本节介绍了通过利用处理器缓存和操作系统虚拟内存管理器进行的二级内存交换功能来提高主内存访问性能的指南。

内存访问顺序

[编辑 | 编辑源代码]

按递增地址顺序访问内存。具体来说

  • 按递增顺序扫描数组;
  • 使用最右边的索引作为最内层循环,扫描多维数组;
  • 在类构造函数和赋值运算符 (operator=) 中,按声明顺序访问成员变量。

数据缓存优化按递增顺序访问内存。

当扫描多维数组时,最内层循环应该遍历最后一个索引,最内层循环应该遍历倒数第二个索引,依此类推。这样,可以确保以与内存中排列顺序相同的顺序处理数组单元。例如,以下代码已优化

float a[num_levels][num_rows][num_columns];
for (int lev = 0; lev < num_levels; ++lev) {
    for (int r = 0; r < num_rows; ++r) {
         for (int c = 0; c < num_columns; ++c) {
            a[lev][r][c] += 1;
        }
    }
}

内存对齐

[编辑 | 编辑源代码]

保持编译器默认的内存对齐。

默认情况下,编译器会对基本类型使用对齐标准,在这种标准下,对象只能具有为特定因子的倍数的内存地址。这种标准保证了最佳性能,但它可能会在连续对象之间添加 _填充_(或 _空洞_)。

如果需要避免某些结构的填充,则仅在这些结构定义周围使用 _pragma_ 指令。

在编译单元中分组函数

[编辑 | 编辑源代码]

在同一个编译单元中定义一个类的所有成员函数、该类所有 _friend_ 函数以及该类所有 _friend_ 类的所有成员函数,除非由于文件大小的原因导致结果文件变得难以管理。

这样,从函数编译得到的机器码和在类和函数中定义的静态数据将具有相邻地址。此外,即使是未执行整个程序优化的编译器也可以优化这些函数之间的调用。

在编译单元中分组变量

[编辑 | 编辑源代码]

在最常使用全局变量的编译单元中定义每个全局变量。

这样,这些变量将具有彼此相邻的地址,并且与在这些编译单元中定义的静态变量相邻。此外,即使是未执行整个程序优化的编译器也可以优化对这些变量的访问,来自最常使用它们的函数。

编译单元中的私有函数和变量

[编辑 | 编辑源代码]

在匿名命名空间中声明对编译单元全局的变量和函数,但这些变量和函数未被其他编译单元使用。

在 C 语言和 C++ 中,这些变量和函数可以声明为 _static_。但是,在现代 C++ 中,不推荐使用 _static_ 全局变量和函数,应该用在匿名命名空间中声明的变量和函数代替它们。

在这两种情况下,编译器都会收到通知,这些标识符永远不会被其他编译单元使用。这允许未执行整个程序优化的编译器优化这些变量和函数的使用。

华夏公益教科书