我将一个项目从Visual C ++ 6.0移植到VS 2010,发现代码的关键部分(脚本引擎)现在运行速度比以前慢了大约三倍。 经过一些研究,我设法提取代码片段,这似乎导致减速。我尽可能地减少了它,因此重现问题会更容易。 在分配包含另一个类(String)的复杂类(Variant)以及其他几个简单类型字段的并集时,会重现该问题。
玩这个例子我发现了更多“神奇”: 1.如果我评论一个未使用的(!)类成员,速度会增加,代码最终会比符合VS 6.2的代码运行得更快。 如果我删除“联合”包装器,情况也是如此。 3.如果将字段的值从1更改为0
,则同样为真我不知道到底是怎么回事。 我检查了所有代码生成和优化开关,但没有任何成功。
代码示例如下: 在我的Intel 2.53 GHz CPU上,在VS 6.2下编译的此测试运行1.0秒。 在VS 2010下编译 - 40秒 在VS 2010下编译,用“魔术”线条评论 - 0.3秒。
问题是使用任何优化开关重现,但应禁用“整个程序优化”(/ GL)。否则这个太聪明的优化器会知道out测试实际上什么都不做,测试将运行0秒。
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
class String
{
public:
char *ptr;
int size;
String() : ptr(NULL), size( 0 ) {};
~String() {if ( ptr != NULL ) free( ptr );};
String& operator=( const String& str2 );
};
String& String::operator=( const String& string2 )
{
if ( string2.ptr != NULL )
{
// This part is never called in our test:
ptr = (char *)realloc( ptr, string2.size + 1 );
size = string2.size;
memcpy( ptr, string2.ptr, size + 1 );
}
else if ( ptr != NULL )
{
// This part is never called in our test:
free( ptr );
ptr = NULL;
size = 0;
}
return *this;
}
struct Date
{
unsigned short year;
unsigned char month;
unsigned char day;
unsigned char hour;
unsigned char minute;
unsigned char second;
unsigned char dayOfWeek;
};
class Variant
{
public:
int dataType;
String valStr; // If we comment this string, the speed is OK!
// if we drop the 'union' wrapper, the speed is OK!
union
{
__int64 valInteger;
// if we comment any of these fields, unused in out test, the speed is OK!
double valReal;
bool valBool;
Date valDate;
void *valObject;
};
Variant() : dataType( 0 ) {};
};
void TestSpeed()
{
__int64 index;
Variant tempVal, tempVal2;
tempVal.dataType = 3;
tempVal.valInteger = 1; // If we comment this string, the speed is OK!
for ( index = 0; index < 200000000; index++ )
{
tempVal2 = tempVal;
}
}
int main(int argc, char* argv[])
{
int ticks;
char str[64];
ticks = GetTickCount();
TestSpeed();
sprintf( str, "%.*f", 1, (double)( GetTickCount() - ticks ) / 1000 );
MessageBox( NULL, str, "", 0 );
return 0;
}
答案 0 :(得分:0)
这很有趣。首先,我无法重现发布版本中的减速,仅在调试版本中。然后我关闭了SSE2优化并获得了相同的〜40s运行时间。
问题似乎出现在Variant
的编译器生成的副本分配中。如果没有SSE2,它实际上会使用fld
/ fstp
指令执行浮点副本,因为联合包含一个double。并且有一些特定的值,这显然是一个非常昂贵的操作。 64位整数值1
映射到4.940656458412e-324#DEN
,这是一个非规范化数字,我相信这会导致问题。当您tempVal.valInteger
未初始化时,它可能包含一个更快的值。
我做了一个小测试来证实这一点:
union {
uint64_t i;
volatile double d1;
};
i = 0xcccccccccccccccc; //with this value the test takes 0.07 seconds
//i = 1; //change to 1 and now the test takes 36 seconds
volatile double d2;
for(int i=0; i<200000000; ++i)
d2 = d1;
所以你可以做的是为Variant定义你自己的副本赋值,它只是简单地记住了union。
Variant& operator=(const Variant& rhs)
{
dataType = rhs.dataType;
union UnionType
{
__int64 valInteger;
double valReal;
bool valBool;
Date valDate;
void *valObject;
};
memcpy(&valInteger, &rhs.valInteger, sizeof(UnionType));
valStr = rhs.valStr;
return *this;
}