c++ - 用于空间和性能提升的位对齐

标签 c++ performance visual-studio-2010 optimization compiler-construction

在书中Game Coding Complete, 3rd Edition,作者提到了一种既可以减小数据结构大小又可以 提高访问性能的技术。从本质上讲,它依赖于这样一个事实:当成员变量与内存对齐时,您可以获得性能。这是编译器可以利用的明显潜在优化,但通过确保每个变量对齐,它们最终会膨胀数据结构的大小。

或者至少这是他的主张。

他说,真正的性能提升是通过动脑并确保您的结构设计得当,以利用速度提升的优势,同时防止编译器膨胀。他提供了以下代码片段:

#pragma pack( push, 1 )

struct SlowStruct
{
    char c;
    __int64 a;
    int b;
    char d;
};

struct FastStruct
{
    __int64 a;
    int b;
    char c;
    char d;  
    char unused[ 2 ]; // fill to 8-byte boundary for array use
};

#pragma pack( pop )

在未指定的测试中使用上述 struct 对象,他报告了 15.6% 的性能提升(222ms192ms) 和更小的 FastStruct 大小。这一切对我来说在纸面上都有意义,但在我的测试下却站不住脚:

enter image description here

同时产生大小(计算char unused[ 2 ])!

现在,如果 #pragma pack( push, 1 ) 仅与 FastStruct 隔离(或完全删除),我们确实会看到不同之处:

enter image description here

所以,最后,问题来了:现代编译器(特别是 VS2010)是否已经针对位对齐进行了优化,因此没有提高性能(但增加结构大小作为副作用,如 Mike Mcshaffry 所说)?还是我的测试强度不够/不确定,无法返回任何重要结果?

对于测试,我在未对齐的 __int64 成员上执行了各种任务,包括数学运算、列主多维数组遍历/检查、矩阵运算等。这两种结构都不会产生不同的结果。

最后,即使它们没有性能提升,这仍然是一个有用的花絮,需要牢记以将内存使用量保持在最低限度。但是,如果我只是没有看到性能提升(无论多么小),我会很高兴。

最佳答案

高度依赖于硬件。

让我演示一下:

#pragma pack( push, 1 )

struct SlowStruct
{
    char c;
    __int64 a;
    int b;
    char d;
};

struct FastStruct
{
    __int64 a;
    int b;
    char c;
    char d;  
    char unused[ 2 ]; // fill to 8-byte boundary for array use
};

#pragma pack( pop )

int main (void){

    int x = 1000;
    int iterations = 10000000;

    SlowStruct *slow = new SlowStruct[x];
    FastStruct *fast = new FastStruct[x];



    //  Warm the cache.
    memset(slow,0,x * sizeof(SlowStruct));
    clock_t time0 = clock();
    for (int c = 0; c < iterations; c++){
        for (int i = 0; i < x; i++){
            slow[i].a += c;
        }
    }
    clock_t time1 = clock();
    cout << "slow = " << (double)(time1 - time0) / CLOCKS_PER_SEC << endl;
    
    //  Warm the cache.
    memset(fast,0,x * sizeof(FastStruct));
    time1 = clock();
    for (int c = 0; c < iterations; c++){
        for (int i = 0; i < x; i++){
            fast[i].a += c;
        }
    }
    clock_t time2 = clock();
    cout << "fast = " << (double)(time2 - time1) / CLOCKS_PER_SEC << endl;



    //  Print to avoid Dead Code Elimination
    __int64 sum = 0;
    for (int c = 0; c < x; c++){
        sum += slow[c].a;
        sum += fast[c].a;
    }
    cout << "sum = " << sum << endl;


    return 0;
}

酷睿 i7 920 @ 3.5 GHz

slow = 4.578
fast = 4.434
sum = 99999990000000000

好吧,区别不大。但它在多次运行中仍然保持一致。
因此对齐方式在 Nehalem Core i7 上产生了很小的差异。


Intel Xeon X5482 Harpertown @ 3.2 GHz(Core 2 - generation Xeon)

slow = 22.803
fast = 3.669
sum = 99999990000000000

现在看看...

快 6.2 倍!!!


结论:

您会看到结果。您决定是否值得花时间进行这些优化。


编辑:

相同的基准测试,但没有 #pragma pack:

酷睿 i7 920 @ 3.5 GHz

slow = 4.49
fast = 4.442
sum = 99999990000000000

Intel Xeon X5482 Harpertown @ 3.2 GHz

slow = 3.684
fast = 3.717
sum = 99999990000000000
  • Core i7 的编号没有改变。显然它可以处理 对于这个基准没有问题的错位。
  • Core 2 Xeon 现在显示两个版本的时间相同。这证实了未对齐是 Core 2 架构上的一个问题。

摘 self 的评论:

如果您省略 #pragma pack,编译器将保持所有内容对齐,因此您不会看到此问题。所以这实际上是一个示例,说明如果您误用 #pragma pack 可能会发生什么。

关于c++ - 用于空间和性能提升的位对齐,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9086656/

相关文章:

visual-studio-2010 - Visual Studio 给我编译错误 : Too Few Arguments to call

c++ - OpenMP - 嵌套 for 循环在外部循环之前并行时变得更快。为什么?

c++ - 我需要 MFC 自动创建的 CShellManager 吗?

mysql - 在 mysql 表中存储多个范围以使查询更快/更容易

c# - 是否有 IntelliSense 选项/插件在鼠标悬停时显示参数名称?

c - Visual Studio 的 C4028 警告(形式参数与声明不同)是虚假的吗?

c++ - Qt : Create a Stack of image

c++ - 使用路径变量时包含不扩展的路径

javascript - rails : Importing CDN files vs using the assets for bootstrap

Java - 比较两个巨大的文本文件