将VS ++中的C ++项目移植到VS 2010会导致代码变慢

时间:2012-03-03 06:02:30

标签: visual-studio-2010 code-generation

我将一个项目从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;
}

1 个答案:

答案 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;
}