【问题标题】:Porting C++ project from VS 6.0 to VS 2010 brought to slower code将 C++ 项目从 VS 6.0 移植到 VS 2010 会导致代码变慢
【发布时间】:2012-03-21 14:00:25
【问题描述】:

我将一个项目从 Visual C++ 6.0 移植到 VS 2010,发现代码的关键部分(脚本引擎)现在的运行速度比以前慢了大约三倍。 经过一些研究,我设法提取了似乎导致速度下降的代码片段。我尽可能将其最小化,因此更容易重现问题。 当分配一个包含另一个类(String)的复杂类(Variant)以及其他几个简单类型字段的联合时,该问题会重现。

玩这个例子我发现了更多的“魔法”: 1.如果我评论一个未使用的(!)类成员,速度会提高,并且代码最终运行得比那些遵循 VS 6.2 的要快 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;
}

【问题讨论】:

    标签: visual-studio-2010 code-generation


    【解决方案1】:

    这很有趣。首先,我无法重现发布版本的减速,只能在调试版本中重现。然后我关闭了 SSE2 优化并获得了相同的约 40 秒运行时间。

    问题似乎出在编译器为Variant 生成的副本分配中。如果没有 SSE2,它实际上会使用 fld/fstp 指令进行浮点复制,因为联合包含双精度。对于某些特定值,这显然是一项非常昂贵的操作。 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 进行简单的 memcpy。

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

    【讨论】:

    • Timo,谢谢你的建议,我没有朝这个方向搜索。
    • 现在我发现 /fp:strict 选项也有帮助。这很奇怪,因为我确信默认情况下,union 是通过简单的字节分配复制的。现在很清楚,情况并非总是如此。但是我还不清楚一件事:为什么删除“bool”和“String”成员可以解决问题?可能编译器为小型和大型结构生成不同的代码?
    • @Boris L:是的,我也不太了解编译器的行为,因为它是情境性的。实际上,直到我从联合中删除 Date 并删除 boolvoid* 后,我才发现问题。 MS 已确认这是一个错误 (connect.microsoft.com/VisualStudio/feedback/details/238546/…),但为什么他们没有为 VC9 或 VC10 修复它是我无法理解的。似乎在 VC11 中工作。
    猜你喜欢
    • 2011-03-30
    • 2019-05-01
    • 1970-01-01
    • 2016-03-04
    • 1970-01-01
    • 1970-01-01
    • 2022-01-14
    • 2012-11-17
    • 2015-05-23
    相关资源
    最近更新 更多