2014-10-04 93 views
9

我正在研究一些舊的(和專門面向win32的)東西,並考慮讓它更現代/便攜 - 即在C++ 11中重新實現一些可廣泛使用的部分。其中一個部分是utf8和utf16之間的convertin。在Win32 API中,我使用MultiByteToWideChar/WideCharToMultiByte,嘗試使用以下示例代碼將這些內容移植到C++ 11:https://stackoverflow.com/a/14809553。其結果是utf8 <-> utf16:codecvt性能差

發佈版本(由MSVS 2013編譯,在酷睿i7 3610QM運行)

stdlib     = 1587.2 ms 
Win32     = 127.2 ms 

調試版本

stdlib     = 5733.8 ms 
Win32     = 127.2 ms 

的問題是 - 是有什麼錯碼?如果一切似乎都沒有問題 - 這種性能差異有什麼好的理由嗎?

測試代碼如下:由於Vista的

#include <iostream> 
#include <fstream> 
#include <string> 
#include <iterator> 
#include <clocale> 
#include <codecvt> 

#define XU_BEGIN_TIMER(NAME)      \ 
    {           \ 
     LARGE_INTEGER __freq;     \ 
     LARGE_INTEGER __t0;     \ 
     LARGE_INTEGER __t1;     \ 
     double   __tms;     \ 
     const char*  __tname = NAME;   \ 
     char   __tbuf[0xff];   \ 
               \ 
     QueryPerformanceFrequency(&__freq);  \ 
     QueryPerformanceCounter(&__t0);   

#define XU_END_TIMER()        \ 
     QueryPerformanceCounter(&__t1);   \ 
     __tms = (__t1.QuadPart - __t0.QuadPart) * 1000.0/__freq.QuadPart; \ 
     sprintf_s(__tbuf, sizeof(__tbuf), " %-24s = %6.1f ms\n", __tname, __tms); \ 
     OutputDebugStringA(__tbuf);    \ 
     printf(__tbuf);       \ 
    } 

std::string read_utf8() { 
    std::ifstream infile("C:/temp/UTF-8-demo.txt"); 
    std::string fileData((std::istreambuf_iterator<char>(infile)), 
         std::istreambuf_iterator<char>()); 
    infile.close(); 

    return fileData; 
} 

void testMethod() { 
    std::setlocale(LC_ALL, "en_US.UTF-8"); 
    std::string source = read_utf8(); 
    { 
     std::string utf8; 

     XU_BEGIN_TIMER("stdlib") { 
      for(int i = 0; i < 1000; i++) { 
       std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert2utf16; 
       std::u16string utf16 = convert2utf16.from_bytes(source); 

       std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> convert2utf8; 
       utf8 = convert2utf8.to_bytes(utf16); 
      } 
     } XU_END_TIMER(); 

     FILE* output = fopen("c:\\temp\\utf8-std.dat", "wb"); 
     fwrite(utf8.c_str(), 1, utf8.length(), output); 
     fclose(output); 
    } 

    char* utf8 = NULL; 
    int cchA = 0; 

    { 
     XU_BEGIN_TIMER("Win32") { 
      for(int i = 0; i < 1000; i++) { 
       WCHAR* utf16 = new WCHAR[source.length() + 1]; 
       int cchW; 
       utf8 = new char[source.length() + 1]; 

       cchW = MultiByteToWideChar(
        CP_UTF8, 0, source.c_str(), source.length(), 
        utf16, source.length() + 1); 

       cchA = WideCharToMultiByte(
        CP_UTF8, 0, utf16, cchW, 
        utf8, source.length() + 1, NULL, false); 

       delete[] utf16; 
       if(i != 999) 
        delete[] utf8; 
      } 
     } XU_END_TIMER(); 

     FILE* output = fopen("c:\\temp\\utf8-win.dat", "wb"); 
     fwrite(utf8, 1, cchA, output); 
     fclose(output); 

     delete[] utf8; 
    } 
} 
+0

您的Win32代碼沒有正確分配緩衝區。 UTF-8和UTF-16在它們的數據長度之間沒有1對1的關係。您應該一次調用'MultiByteToWideChar' /'WideCharToMultiByte'來計算必要的緩衝區大小,然後分配緩衝區,然後再次調用以進行實際轉換。所以這會影響一點點時間。 – 2014-10-04 20:13:59

+6

由於Vista在內部使用SSE取得了很好的效果,所以很少有UTF代碼轉換器可以執行Win32。這將很難被擊敗。 – 2014-10-04 20:15:36

+0

@Remy Lebeau:是的,如果我不想分配額外的(真正的臨時內存),我需要再次調用MultiByteToWideChar/WideCharToMultiByte - 這會將win32用例帶到127 * 2 = 250ms左右。這仍然比stdlib快6.5倍。 – 2014-10-04 20:31:06

回答

4

的Win32的UTF8轉碼使用上證所內部有很大的影響,一些很少有其他UTF轉碼器做。我懷疑即使是最高度優化的便攜式代碼也無法擊敗。

但是,如果這個數字超過了10倍的時間,那麼您爲codecvt給出的這個數字非常慢,並且暗示了一個幼稚的實現。在編寫我自己的UTF-8解碼器時,我能夠達到Win32的2-3倍。這裏有很多改進的餘地,但是你需要定製一個codecvt來獲得它。

+3

_Win32的UTF8轉碼因爲Vista在內部使用SSE效果很好...... _ - 你有參考嗎? – polyvertex 2015-03-18 16:26:17

7

在我自己的測試中,我發現wstring_convert的構造函數調用有至少在Windows上的大量開銷。正如其他答案所示,您可能很難擊敗本機Windows實現,但嘗試修改代碼以在循環之外構建轉換器。我預計你會看到5倍和20倍之間的改進,特別是在調試版本中。

+1

事實證明,這正是我面臨的問題。使構造器靜態化:繁榮! – 2016-05-18 16:24:58

+0

現在問題是 - 你可以安全地從多個線程使用該靜態對象嗎? ;) – 2018-01-25 06:23:21

相關問題