2014-10-04 17 views
9

Eski (ve yalnızca win32 yönelimli) bazı şeylere bakıyorum ve daha modern/taşınabilir hale getirmeyi düşünüyorum, yani C++ 11'deki bazı yeniden kullanılabilir bölümleri yeniden canlandırmayı düşünüyorum. Bu parçalardan biri utf8 ile utf16 arasında dönüşüyor. Win32 API'sinde https://stackoverflow.com/a/14809553 örnek kodunu kullanarak bu nesneleri C++ 11'e aktarmaya çalışıyorum, MultiByteToWideChar/WideCharToMultiByte kullanıyorum. koduyla yanlış bir şey var - sonuç (Core i7 3610QM çalıştırmak, MSVS 2013 tarafından derlenen)utf16: codecvt kötü performans

Yayın inşa

stdlib     = 1587.2 ms 
Win32     = 127.2 ms 

ayıklama soru olduğunu

stdlib     = 5733.8 ms 
Win32     = 127.2 ms 

inşa olduğunu ? Her şey yolunda gibi görünüyorsa - bu performans farkı için iyi bir sebep var mı?

Testi kod aşağıda: Vista beri

#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 kodunuz arabellekleri doğru bir şekilde ayırmıyor. UTF-8 ve UTF-16, veri uzunlukları arasında 1'den 1'e kadar bir ilişkiye sahip değildir. Gerekli arabellek boyutunu hesaplamak için 'MultiByteToWideChar' /' WideCharToMultiByte' sözcüğünü çağırmanız, ardından arabelleği ayırmanız, ardından gerçek dönüşümü yapmak için yeniden aramanız gerekir. Yani bu zamanlamayı biraz etkiler. –

+6

Win32, Vista'nın SSE'yi dahili olarak büyük bir etkiye kullandığı için, çok az UTF kod dönüştürücüsü yapıyor. Yenmek zor olacak. –

+0

@Remy Lebeau: Evet, ekstra (gerçekten geçici bellek) ayırmak istemiyorum, MultiByteToWideChar/WideCharToMultiByte'ı bir kez daha çağırmam gerekiyor - bu, win32 usecase'i 127 * 2 = 250ms civarında bir şeyle getirecektir. Bu hala 6.5 kez stdlib'den daha hızlı. –

cevap

4

Win32'nin UTF8 kodlama büyük etkisi içten SSE kullanır şey çok az diğer UTF transcoders yapmak. En yüksek düzeyde optimize edilmiş taşınabilir kodlarla bile geçmenin imkansız olacağından şüpheleniyorum. Ancak, codecvt için verdiğiniz bu sayı, 10 katın üzerindeyse çok yavaştır ve naif bir uygulama önerir. Kendi UTF-8 kod çözücüsümü yazarken, Win32'un 2-3 katı içinde ulaşabildim. Burada iyileştirmek için çok yer var, ancak bunu elde etmek için özel bir codecvt uygulamanız gerekiyor.

+3

_Win32'nin UTF8 transkodu, Vista'yı SSE'yi dahili olarak büyük bir etki kullanmasından beri ..._ - referansınız var mı? – polyvertex

7

Kendi testimde, kurucunun wstring_convert numaralı telefonunun en azından Windows'ta büyük bir ek yükü olduğunu buldum. Diğer cevapların da gösterdiği gibi, muhtemelen yerel Windows uygulamasını yenmek için mücadele edeceksiniz, ancak kodun dönüştürücüyü döngü dışında oluşturması için değiştirmeyi deneyin. Özellikle hata ayıklama yapısında 5x ile 20x arasında bir iyileşme göreceğinizi umuyorum.

+1

Bu, tam olarak karşı karşıya olduğum sorun olduğu ortaya çıktı. Yapıcıyı statik yaptı: bom! –

+0

Şimdi soru şu: bu statik nesneyi birden çok ileti dizisinden güvenle kullanabilir misiniz? ;) –