我正在使用Visual C++ 2008编译的程序中执行一些浮点运算。此外,我还启用了优化(/O2)。
C++代码大致如下:
int Calculate( CalculationParams ¶ms )
{
const ConfigurationParams& configParams = ConfigReader::Instance().Parameters();
float m1 = configParams.p1 * configParams.p2;
float m2 = configParams.p3 * configParams.p4;
float m3 = configParams.p5 * configParams.p6;
....
}
ConfigReader是一个单例对象,包含了计算所需的参数结构。这个对象可以通过configParams引用来进行访问。
当优化被激活时,偶尔会出现错误的计算结果。
查看反汇编代码,我发现了以下信息:
int Calculate( CalculationParams ¶ms )
{
...
const ConfigurationParams& configParams = ConfigReader::Instance().Parameters();
call ConfigReader::Instance()
move ebx, eax
float m1 = configParams.p1 * configParams.p2;
fld dword ptr[ebx + 0D4h]
add ebx, 8
fmul dword ptr [ebx + 0ECh]
float m2 = configParams.p3 * configParams.p2;
float m3 = configParams.p4 * configParams.p2;
...
}
首先,我们可以看到它没有调用Parameters()。这是可以理解的,因为参数结构位于类中,8个字节之后(在两个其他浮点数之后)。因此,在调用之后,eax具有ConfigReader CLASS的地址(而不是ConfigurationParams结构的地址)。
然后它尝试加载一个浮点数。这就是问题出现的地方。由于ebx指向ConfigReader类时,加载操作的偏移量似乎不正确,所以问题就出现了。它应该先添加8才能使偏移量正确。
编译器是否认为fld操作比add操作花费更长的时间,并且在从内存加载浮点数之前,ebx将自动添加8?这样行得通吗?我们偶尔遇到问题是否源于在此时发生中断,导致ebx在加载浮点数时没有偏移量为8?
我期望唯一正确的方法是在fld之前进行add操作。很难理解这样甚至会起作用...
有没有办法关闭这种重新排列优化呢?
编辑: ConfigReader如下:
class ConfigReader
{
public:
static ConfigReader& Instance();
const ConfigurationParams& Parameters() const { return myParameters; }
private:
ConfigReader();
float internalParam1;
float internalParam2;
ConfigurationParams myParameters;
}
struct ConfigurationParams
{
char s1[10];
char s2[50];
int i1;
int i2;
int i3;
int i4;
int i5;
int i6;
int i7;
int i8;
int i9;
int i10;
int i11;
int i12;
int i13;
int i14;
int i15;
int i16;
int i17;
int i18;
int i19;
int i20;
int i21;
int i22;
int i23;
float f1;
float f2;
int i25;
int i26;
int i27;
int i28;
int i29;
int i30;
int i31;
bool b1;
float f3;
float f4;
float f5;
float p1;
float p3;
float p4;
float f9;
float f10;
float f11;
float f12;
float p2;
float f14;
float f15;
float f16;
int i32;
int i33;
int i34;
int i35;
}