2012-03-03 116 views
2

我將一個項目從Visual C++ 6.0移植到VS 2010,發現代碼的一個關鍵部分(腳本引擎)現在運行速度比現在慢三倍左右之前。 經過一番研究,我設法提取了代碼片段,這似乎會導致放緩。我儘可能地減少了它,所以它很容易重現問題。 當分配包含另一個類(String)的複合類(Variant)和簡單類型的其他幾個字段的聯合時,會再現該問題。從VS 6.0移植到VS 2010的C++項目帶來了較慢的代碼

與示例玩,我發現更多的「魔術」: 1.如果我評論未使用的類成員之一,速度增加,並且代碼運行最後比那些VS 6.2 2.遵守更快(!)同樣是真實的,如果我刪除了「聯盟」包裝」 3.同樣是真實的事件,如果改變的申請從1到0

我不知道這到底是怎麼回事值。 我已籤所有代碼生成和優化開關,但沒有任何成功

代碼示例如下: On my In tel 2.53 GHz CPU這個測試,在VS 6.2下編譯運行1.0秒。 編譯根據VS 2010 - 40秒 編譯在VS 2010與「魔術」線評論 - 0.3秒。

問題是使用任何優化開關重現,但應該禁用「整體程序優化」(/ GL)。否則,這個太聰明的優化器會知道out test實際上什麼都不做,測試將運行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

這很有趣。首先,我無法重現發佈版本中的減速,只能在調試版本中進行。然後,我關閉了SSE2優化,並獲得了大約40秒的運行時間。

該問題似乎在編譯器生成的複製分配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; 

所以你可以做的是定義變自己的副本任務,只是做了聯盟的一個簡單的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; 
} 
+0

蒂莫,感謝您的建議,我沒有朝這個方向搜索。 – 2012-03-03 17:00:46

+0

現在我發現/ fp:strict選項也有幫助。 這非常奇怪,因爲我確定默認情況下通過簡單的字節分配複製了聯合。現在很明顯,情況並非總是如此。 但有一點我還不清楚:爲什麼取消「bool」和「String」成員可以解決問題?編譯器可能爲小型和大型結構生成不同的代碼? – 2012-03-03 17:15:03

+0

@Boris L:是的,我不太瞭解編譯器的行爲,因爲它是如此的情景。直到我從聯合中刪除了'Date',並且刪除'bool'或'void *'沒有效果,我才真正解決了這個問題。 MS已確認它是一個錯誤(http://connect.microsoft。com/VisualStudio/feedback/details/238546 /錯誤代碼生成for-union-copy-assignment-in-visual-studio-2005-c-compiler)但爲什麼他們沒有修復它,因爲VC9或VC10超出了我。似乎在VC11工作。 – Timo 2012-03-04 03:09:29