2014-10-04 98 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

相关问题