sse*_*ell 9 c++ compiler-construction optimization performance visual-studio-2010
在" 游戏编码完成"第3版中,作者提到了一种减少数据结构大小和提高访问性能的技术.从本质上讲,它依赖于当成员变量与内存对齐时获得性能的事实.这是编译器可以利用的明显潜在优化,但通过确保每个变量对齐,它们最终会膨胀数据结构的大小.
或者这至少是他的主张.
他说,真正的性能提升是通过使用你的大脑并确保你的结构设计得合理,以利用速度增加,同时防止编译器膨胀.他提供了以下代码片段:
#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 )
Run Code Online (Sandbox Code Playgroud)
struct在未指定的测试中使用上述对象,他报告了性能增加15.6%(222ms与之相比192ms)和更小的尺寸FastStruct.这对我来说都是有意义的,但它在我的测试中无法阻止:

同时结果和大小(计数char unused[ 2 ])!
现在,如果#pragma pack( push, 1 )仅隔离FastStruct(或完全删除),我们确实看到了差异:

所以,最后,问题在于:现代编译器(特别是VS2010)已经针对比特对齐进行了优化,因此性能提升不足(但增加结构尺寸作为副作用,如Mike Mcshaffry所述)?或者我的测试不够密集/不确定以返回任何重要结果?
对于测试,我在未对齐__int64成员上执行了数学运算,列主要多维数组遍历/检查,矩阵运算等各种任务.这两种结构都没有产生不同的结果.
最后,即使它们没有性能提升,这仍然是一个有用的消息,要记住将内存使用量降至最低.但是,如果我没有看到性能提升(无论多么轻微),我会喜欢它.
Mys*_*ial 13
让我来证明:
#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;
}
Run Code Online (Sandbox Code Playgroud)
酷睿i7 920 @ 3.5 GHz
slow = 4.578
fast = 4.434
sum = 99999990000000000
Run Code Online (Sandbox Code Playgroud)
好的,差别不大.但它在多次运行中仍然保持一致.
因此,对齐在Nehalem Core i7上产生了很小的差异.
Intel Xeon X5482 Harpertown @ 3.2 GHz(酷睿2代Xeon)
slow = 22.803
fast = 3.669
sum = 99999990000000000
Run Code Online (Sandbox Code Playgroud)
现在来看看......
结论:
你看到了结果.您决定是否值得花时间进行这些优化.
编辑:
相同的基准但没有#pragma pack:
酷睿i7 920 @ 3.5 GHz
slow = 4.49
fast = 4.442
sum = 99999990000000000
Run Code Online (Sandbox Code Playgroud)
Intel Xeon X5482 Harpertown @ 3.2 GHz
slow = 3.684
fast = 3.717
sum = 99999990000000000
Run Code Online (Sandbox Code Playgroud)
取自我的评论:
如果省略#pragma pack,编译器会保持一致,所以你不会看到这个问题.所以这实际上是一个例子,说明如果你滥用 会发生什么#pragma pack.
这种手部优化通常很长时间.如果您正在打包空间,或者如果您具有类似SSE类型的强制对齐类型,则对齐只是一个重要的考虑因素.编译器的默认对齐和打包规则是有意设计的,以显着提高性能,虽然手动调整它们可能是有益的,但通常不值得.
可能在您的测试程序中,编译器从不在堆栈中存储任何结构,只是将成员保存在寄存器中,这些寄存器没有对齐,这意味着它与结构大小或对齐方式完全无关.
事情就是:可能存在别名和其他与子字访问相关的恶意,并且访问整个单词比访问子字的速度慢.所以一般来说,如果你只是访问一个成员,那么在时间上打包比单词大小更有效率.
| 归档时间: |
|
| 查看次数: |
2151 次 |
| 最近记录: |