Улучшение производительности преобразования System.String в std::wstring?

В настоящее время я оцениваю использование ADO.NET для приложения C++, которое в настоящее время использует старый добрый ADO. Учитывая, что мы полностью переделываем взаимодействие с базой данных, мы хотели бы определить, будет ли полезным использование более современной и активно разрабатываемой технологии ADO.NET.

После некоторых измерений выяснилось, что для некоторых тестовых запросов, которые извлекают много строк с несколькими столбцами, которые все содержат строки, ADO.NET на самом деле для нас примерно на 20% медленнее, чем при использовании простого ADO. Наш профилировщик предполагает, что преобразование результатов System.String в std::wstring, используемое приложением, является одним из узких мест. Я не могу переключить ни один из верхних уровней приложения на использование System.String, поэтому мы застряли на этом конкретном преобразовании.

Грубая схема кода выглядит так:

System::Data::SqlClient::SqlCommand^ sqlCmd =
  gcnew System::Data::SqlClient::SqlCommand(cmd, m_DBConnection.get());
System::Data::SqlClient::SqlDataReader^ reader = sqlCmd->ExecuteReader();
if (reader->HasRows)
{
    using namespace msclr::interop;
    while (reader->Read())
    {
      std::vector<std::wstring> results;
      for (int i=0; i < reader->FieldCount; ++i)
      {
        std::wstring col_data;
        TypeCode type = Type::GetTypeCode(reader->GetFieldType(i));
        switch (type)
        {
           // ... omit lots of different types
        case TypeCode::String:
          {
            System::String^ tmp = reader->GetString(i);
            col_data = marshal_as<std::wstring>(tmp);
          }
          break;
          // ... more type conversion code removed
        }
        results.push_back(col_data);
      }
      // NOTE: Callback into native result processing code
      ResultsCallback(results);
    }

Я потратил много времени на изучение различных способов получения std::wstring из System.String и измерил большинство из них. Все они работают примерно одинаково — мы говорим о десятичных точках в процентах использования ЦП. В конце концов, я просто решил использовать marshal_as<std::wstring>, так как он наиболее удобочитаем и кажется таким же эффективным, как и другие решения (например, использование PtrToStringChars или метода, описанного в MSDN здесь).

Использование DataReader очень хорошо работает с концептуальной точки зрения, поскольку большая часть обработки, которую мы выполняем с данными, в любом случае ориентирована на строки.

Единственным другим слегка неожиданным узким местом, которое я заметил, является извлечение TypeCode для столбцов результатов; Я уже планирую перенести это за пределы основного цикла обработки результатов и получать коды типов только один раз для каждого результата запроса.

После этого длинного введения кто-нибудь может порекомендовать менее затратный способ преобразования строковых данных из System.String в std::wstring, или я уже смотрю на оптимальную производительность здесь? Я, очевидно, больше ищу немного необычные способы, учитывая, что я уже пробовал все обычные...

EDIT: похоже, я попал в ловушку, которую сам же и устроил. Да, приведенный выше код примерно на 20 % медленнее, чем эквивалентный простой код ADO в режиме Debug. Однако при переключении в режим Release узкое место по-прежнему можно измерить, но приведенный выше код ADO.NET неожиданно почти на 50 % быстрее, чем старый код ADO. Так что, хотя меня все еще немного беспокоит стоимость преобразования строк, в режиме Release она не так велика, как кажется на первый взгляд.


person Timo Geusch    schedule 04.03.2011    source источник
comment
Есть еще одна ловушка, в которую вы попали. Запрос занимает несколько миллисекунд, чтобы получить первый результат от сервера dbase. Постобработка не может занять больше нескольких микросекунд. На несколько порядков меньше. Как бы быстро вы это ни делали, это все равно занимает несколько миллисекунд.   -  person Hans Passant    schedule 05.03.2011
comment
Да и нет. Когда вы читаете много данных из базы данных на очень регулярной основе (как мы делаем в этом случае), скорость определенных операций все еще является проблемой. Здесь речь идет о десятках секунд или минутах (в худшем случае), поэтому время, затрачиваемое на обработку каждой строки, к сожалению, имеет значение.   -  person Timo Geusch    schedule 05.03.2011


Ответы (1)


Я не вижу никакого способа оптимизировать это, поскольку реализация marshal_as<std::wstring> просто захватывает внутреннюю строку C и присваивает ее std::wstring. Вы не можете стать намного более эффективным, чем это.

Единственное решение, которое я вижу, это разделить ваши строки и заставить N потоков обрабатывать их параллельно. Единственная проблема заключается в том, что вам нужно будет зарезервировать достаточно места в вашем vector, чтобы предотвратить изменение размера во время обработки, но это выглядит достаточно просто.

Если вы используете Visual Studio 2010, я думаю, что библиотеки потоков C++0x будет достаточно для этой задачи, хотя я не уверен, сколько (если таковое имеется) реализовано в Visual Studio на данный момент.

person Collin Dauphinee    schedule 04.03.2011
comment
Спасибо — при текущей архитектуре разделение обработки на несколько потоков добавит больше сложности, что не оправдано увеличением производительности по сравнению с объемом кода, который необходимо было бы переписать для учета этого изменения. Радость устаревших систем. - person Timo Geusch; 05.03.2011