2014-10-04 3 views
9

Я смотрю на некоторые из моих старых (и исключительно ориентированных на Win32) вещи и думаю о том, чтобы сделать его более современным/портативным - т. Е. Переопределить некоторые широко используемые элементы повторного использования в C++ 11. Одна из этих частей конвертируется между utf8 и utf16. В Win32 API я использую MultiByteToWideChar/WideCharToMultiByte, пытаясь передать этот материал на C++ 11, используя пример кода: https://stackoverflow.com/a/14809553. В результатеutf8 <-> utf16: codecvt плохая производительность

сборки выпуска (составитель МСВС 2013, работает на Core i7 3610QM)

stdlib     = 1587.2 ms 
Win32     = 127.2 ms 

Debug построить

stdlib     = 5733.8 ms 
Win32     = 127.2 ms 

Вопрос заключается - там что-то не так с кодом ? Если все будет в порядке - есть ли веские причины для такой разницы в производительности?

код Тест ниже: UTF8 перекодировать

#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' один раз, чтобы вычислить необходимый размер буфера, затем выделить буфер, а затем снова позвонить для фактического преобразования. Так что это немного влияет на сроки. –

+6

Win32, так как Vista использует SSE внутренне с большим эффектом, что-то очень мало транскодеров UTF. Это будет трудно победить. –

+0

@Remy Lebeau: да, если я НЕ хочу выделять лишнюю (действительно временную память), мне нужно еще раз вызвать MultiByteToWideChar/WideCharToMultiByte - это приведет к тому, что win32 usecase начнется примерно с 127 * 2 = 250 мс. Это все еще на 6.5 быстрее, чем stdlib. –

ответ

4

Win32 со времени Vista, использует SSE внутренне с большим эффектом, чем-то очень мало других UTF транскодеры сделать. Я подозреваю, что невозможно будет превзойти даже самый оптимизированный переносимый код.

Однако этот номер, который вы указали для codecvt, является просто чрезвычайно медленным, если он занимает 10-кратное время и предлагает наивную реализацию. В то время как я писал свой собственный декодер UTF-8, я смог достичь в 2-3 раза перформанса Win32. Здесь есть много возможностей для улучшения, но вам нужно настроить собственный код codecvt для его получения.

+3

Перекодировка UTF8 _Win32, так как Vista использует SSE для внутреннего эффекта ... - есть ли у вас ссылка? – polyvertex

7

В моем собственном тестировании я обнаружил, что вызов конструктора для wstring_convert имеет значительные накладные расходы, по крайней мере, в Windows. Как утверждают другие ответы, вы, вероятно, будете бороться за обычную реализацию Windows, но попробуйте изменить свой код, чтобы построить конвертер вне цикла. Я ожидаю, что вы увидите улучшение от 5x до 20x, особенно в сборке отладки.

+1

Это оказалась проблема, с которой я столкнулся. Сделал конструктор статическим: стрела! –

+0

Теперь вопрос: можете ли вы безопасно использовать этот статический объект из нескольких потоков? ;) –

Смежные вопросы