Есть массив String[], который конвертируется в vector<wstring> вот так:
std::vector<std::wstring> buf(vals->Length);
for (auto i = 0; i < vals->Length; i++)
{
auto ptr = Marshal::StringToHGlobalUni(vals[i]);
auto pStr = (wchar_t*)(void*)ptr;
std::wstring str(pStr);
Marshal::FreeHGlobal(ptr);
buf[i] = str;
}
Проблема в том, что на тестовом массиве эта конверсия занимает 24 секунды, в то время как заполнение исходного String[] (в C# !!!) занимает всего 6 секунд.
Вопрос — в чем тут проблема?
Здравствуйте, Codealot, Вы писали:
C>Проблема в том, что на тестовом массиве эта конверсия занимает 24 секунды, в то время как заполнение исходного String[] (в C# !!!) занимает всего 6 секунд.
И что вас смущает?
Здравствуйте, Codealot, Вы писали:
C>Есть массив String[], который конвертируется в vector<wstring> вот так: C>
C> std::vector<std::wstring> buf(vals->Length); //создание вектора на N элементов пустых и ненужных wstring
C> for (auto i = 0; i < vals->Length; i++)
C> {
C> auto ptr = Marshal::StringToHGlobalUni(vals[i]); //1
C> auto pStr = (wchar_t*)(void*)ptr;
C> std::wstring str(pStr); //2
C> Marshal::FreeHGlobal(ptr);
C> buf[i] = str; //3
C> }
C>
C>Проблема в том, что на тестовом массиве эта конверсия занимает 24 секунды, в то время как заполнение исходного String[] (в C# !!!) занимает всего 6 секунд. C>Вопрос — в чем тут проблема?
У тебя 3 аллокаций/копирования, вместо возможной одной.
Вариант с 1-й аллокацией/копированием:
std::vector<std::wstring> buf;
buf.reserve(vals->Length); //зарезервировали память для N элементов wstringfor (auto i = 0; i < vals->Length; i++)
{
buf.push_back(std::move(msclr::interop::marshal_as<std::wstring>(vals[i])));
}
Немного более оптимальная и многословная версия, развёрнутый метод marshal_as и где объект wstring создаётся inplace в векторе:
std::vector<std::wstring> buf;
buf.reserve(vals->Length); //зарезервировали память для N элементов wstringfor (auto i = 0; i < vals->Length; i++)
{
auto val = vals[i];
cli::pin_ptr<const wchar_t> ptr = PtrToStringChars(val);
buf.emplace_back(static_cast<const wchar_t *>(ptr), val->Length);
}
ps: и убедись что проверяешь релизную версию С++, т.к. в отладочной версии в std немалое количество всяких проверок.
Ну и как уже подсказали, если исходный managed массив строк сформирован на основе уже готовых строк, то в в .NET это по сути копирование указателя в массив, в варианте C++/wstring — это всегда аллокация/копирование.
pps: в современном C++ ещё есть string_view, и если строки в C++/Cli нужны на короткое время, то имеет смысл запинить их все и юзать string_view — 0 аллокаций и копирования.
Здравствуйте, pilgrim_, Вы писали:
_>Немного более оптимальная и многословная версия, развёрнутый метод marshal_as и где объект wstring создаётся inplace в векторе:
А marshal_as категорически не хочет компилироваться.
_>ps: и убедись что проверяешь релизную версию С++, т.к. в отладочной версии в std немалое количество всяких проверок.
Ха. В дебаге всё еще в разы хуже.
_>Ну и как уже подсказали, если исходный managed массив строк сформирован на основе уже готовых строк, то в в .NET это по сути копирование указателя в массив, в варианте C++/wstring — это всегда аллокация/копирование.
Естественно. Но даже если полностью копировать строки, всё равно в разы быстрее.
Здравствуйте, Codealot, Вы писали:
C>Здравствуйте, pilgrim_, Вы писали:
_>>Немного более оптимальная и многословная версия, развёрнутый метод marshal_as и где объект wstring создаётся inplace в векторе:
C>А marshal_as категорически не хочет компилироваться.
Включи заголовочный файл: msclr\marshal_cppstd.h
Вариант без marshal_as: PtrToStringChars живёт в vcclr.h
Здравствуйте, Codealot, Вы писали: C>Немного быстрее, но ненамного. Оно разве не должно копировать содержимое строки в новый объект wstring как ссылку?
Тогда бы у вас в результате операции FreeHGlobal объекты wstring указывали в космос.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Codealot, Вы писали:
C>Есть массив String[], который конвертируется в vector<wstring> вот так: C>
C> std::vector<std::wstring> buf(vals->Length);
C> for (auto i = 0; i < vals->Length; i++)
C> {
C> auto ptr = Marshal::StringToHGlobalUni(vals[i]);
C> auto pStr = (wchar_t*)(void*)ptr;
C> std::wstring str(pStr);
C> Marshal::FreeHGlobal(ptr);
C> buf[i] = str;
C> }
C>
C>Проблема в том, что на тестовом массиве эта конверсия занимает 24 секунды, в то время как заполнение исходного String[] (в C# !!!) занимает всего 6 секунд. C>Вопрос — в чем тут проблема?
Навскидку — вы используете самый неудачный конструктор wstring, т.к. по документации он перед копированием вычисляет длину строки через length(). То есть сканирует строку в поисках \0.
Ну, не считая, конечно, адски медленного выделения и освобождения памяти в HGlobal.
Чем вас не устроил стандартный marshal_as? https://docs.microsoft.com/en-us/cpp/dotnet/overview-of-marshaling-in-cpp
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Codealot, Вы писали:
C>Есть массив String[], который конвертируется в vector<wstring> вот так: C>Проблема в том, что на тестовом массиве эта конверсия занимает 24 секунды, в то время как заполнение исходного String[] (в C# !!!) занимает всего 6 секунд.
Не то чтобы я могу что-то сказать по делу, но мне очень интересно — сколько там этих строк примерно, что шесть секунд нужно на преобразование?
ни разу не специалист по CLI, но маршалинг обычно подразумевает обращение через очередь сообщений.
Если при вызове функции используется честный маршалинг, то оно в принципе быстро работать не будет.