C++ 如何欺骗编译器优化内存读取?
我听说了编译器优化 例如:C++ 如何欺骗编译器优化内存读取?,c++,compiler-construction,C++,Compiler Construction,我听说了编译器优化 例如: while(myBool) doStuff(); 编译器知道您不会在while循环中修改myBool,它只读取myBool一次,不会每次都检查它 这可能会受到volatile关键字的影响,对吗 所以我试图“欺骗”编译器,它认为值并没有改变 int main() { struct st { int a; int b; //I want to make difference by writing volatile
while(myBool)
doStuff();
编译器知道您不会在while循环中修改myBool,它只读取myBool一次,不会每次都检查它
这可能会受到volatile
关键字的影响,对吗
所以我试图“欺骗”编译器,它认为值并没有改变
int main()
{
struct st
{
int a;
int b; //I want to make difference by writing volatile here
}s;
s.b = 1;
while(s.b > 0)
{
*((&s.a)+1) = 0;
std::cout << "test" << std::endl;
}
}
intmain()
{
结构街
{
INTA;
int b;//我想通过在这里写volatile来改变
}s;
s、 b=1;
而(s.b>0)
{
*(&s.a)+1)=0;
std::cout我想您是在问如何确保编译器假定值几乎相同(在您的示例中为true),以便编译器针对该值进行优化。
如果您这样做了,那么您的应用程序正在寻找一组特定于编译器的关键字。
这是我在g中使用的++
#define predict_true__(exp) __builtin_expect((exp), 1)
#define predict_true__(exp) (exp)
你可以这样做
while ( predict_true__( myBool ))
doStuff() ;
它将高效运行,因为myBool是真的。
希望它能帮助你,你会知道在你使用视觉效果的情况下应该看什么
另外:我发现下面的帖子谈到了这一点。不过,我恐怕这是一个悲伤的话题:你所做的是未定义的行为。你不能通过写*(&s.a)+1来访问s.b
相反,因为不能保证s.a
和s.b
之间没有任何填充字节
如果您想强制不使用填充,请查看您的编译器选项。对于GCC/Clang,您可以使用声明结构。对于MSVC,您可以使用
没有充分的理由通过s.a
使用该代码访问s.b
。在这种情况下,假设您有充分的理由不直接访问s.b
,您真正应该做的是使用数组(volatile
,如果需要的话).保证数组在内存中是连续的,而不需要特殊属性/杂注
此外,编译器在某些情况下不会进行某些优化:如果i
是一个变量,并且您先写入a[i]
,然后读取a[1]
,编译器就不能假定a[1]
不仅仅是写入的,因此它无法将其缓存在寄存器中。然而,如果您有两个不相关的变量a
和b
,并且您写入*(&a+1)
,然后从b
读取,编译器将假定b
不仅仅是写入的。这就是*(&a+1)的一个很好的原因
是未定义的行为,因为它会导致编译器做出不真实的假设,并导致程序以奇怪的方式运行。简而言之,结构在内存中不保证是连续的。数组是连续的。你不能这样访问结构
一个可能会有帮助的SO问题:。它会更详细地说明为什么不能这样做以及为什么不能这样做。但总结是,结构的字段之间可能有填充,也可能没有填充。我特别喜欢Charlie在这个问题上的回答。试图摆脱每次迭代都对int和0进行一次比较的做法听起来像过早的优化。这实际上可以节省多少时间?关键是他想欺骗编译器在不应该的时候进行优化。他不是在试图节省时间。这不是java。你不必欺骗编译器进行优化。我不确定我是否理解这个问题。你想让编译器进行优化吗(在本例中,不重新读取变量)它应该在什么时候出现?这与说您想在编译器中找到错误不一样吗?@GManNickG:不一定是编译器错误。调用未定义的行为也可以工作,而不是编译器错误。这只会更改(静态)分支预测,不改变应用程序的行为(引入错误)如果myBool
实际上不是true
。对于具有良好分支预测的处理器,它甚至不会对任何经常调用的分支产生性能差异,从而matter@chris:谢谢你指出这一点。这就是我们在办公室使用的。我会把信息传递给这里的人,让他们改变来预测真实的,再次感谢!