A>В чём может быть причина такой низкой производительности у С++?
В версии на C++ аллоцируется много мутабельных строк, в то время как в версии на Python строки иммутабельные, для них просто берётся view.
Плюс, у тебя vector аллоцируется каждый раз создаётся заново, а нужно создать его только один раз и переиспользовать его capacity многократно.
Здравствуйте, smeeld, Вы писали:
S>Покапитанить решили? Не интересно так.
Интересно или нет, но эту функцию никто здесь не упомянул. Получилось впечатление, что либо нужно писать что-то совсем уж вручную (как у тебя), или без STL никак не обойтись. Вот я и решил напомнить...
ветке сокрушались о бустовской реализация is_any_of. Возможно, что и на текущий день ситуация со производительностью split кардинально не поменялась. Вот в этой
Здравствуйте, anatoly1, Вы писали:
A>С++ медленнее питона при парсинге файла.
A>С++ — версия: A>...
A>А вот питон — версия: A>...
A>В чём может быть причина такой низкой производительности у С++?
Причина в том, что это у вас абсолютно разные коды. Версия на Питоне просто просматривает файл и всё. А версия на C++ ещё и делает некие дополнительные действия — создаёт копии определённых кусков данных из файла. Если вы хотите получить реальную C++ копию кода на Питоне, то это будет что-то вроде:
Здравствуйте, anatoly1, Вы писали:
A>Сделал так: A>
A>std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
A> std::stringstream ss(s);
A> std::string item;
A> for (int i = 0; std::getline(ss, item, delim); i++)
A> elems[i] = std::move(item);
A> return elems;
A>}
A>
Аллоцируются строки для каждого элемента в s, stringstream создаётся каждый раз заново. elems должен содержать просто string view.
Да и вообще, в зависимости от задачи, может и elems не нужен — можно обрабатывать по месту.
Здравствуйте, smeeld, Вы писали:
S>А вообще прикольно писать парсеры не тупо на контейнерах, а примерно так: S>struct parser{
надо создать форум "код по укурке"
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
ветке сокрушались о бустовской реализация is_any_of. Возможно, что и на текущий день ситуация со производительностью split кардинально не поменялась. Вот в этой
Здравствуйте, alex_public, Вы писали:
_>И соответственно этот код работает у меня где-то в 2,5 раза быстрее, чем его аналог на Питоне.
Осталось немного, и придёт, наконец, понимание, что здесь, в этой задачке, контейнеры и стримы вообще
ни куда не упёрлись. Их нельзя использовать на каждый чих. Сформировать malloc-ом буфер с содержимым
файла, разбить его на токены или предложения нуль терминатором, последовательно с занесением указателей в массив.
Вот указатели, а лучше их unique_ptr, можно уже складывать в вектор. Только не забыть задать ему первоначальный
размер отличный от нуля, а то vector, созданный дефолтным конструктором, сначала запрашивает память под объект,
потом под два, копируя туда содержание предыдущего буфера и удаляя его, потом под четыре и так далее, кто-то
скажет, что использовать такой способ организации буфера правильно?
Здравствуйте, smeeld, Вы писали:
S>Осталось немного, и придёт, наконец, понимание, что здесь, в этой задачке, контейнеры и стримы вообще S>ни куда не упёрлись. Их нельзя использовать на каждый чих. Сформировать malloc-ом буфер с содержимым S>файла,
Во-первых он файл уже отмэппил файл в память, без всяких аллокаций. А во-вторых, даже если выделять память под весь файл, malloc от вектора практически ничем не будет отличатся в плане скорости
S>разбить его на токены или предложения нуль терминатором, последовательно с занесением указателей в массив. S>Вот указатели, а лучше их unique_ptr, можно уже складывать в вектор.
Указатели на внутренние элементы массива в unique_ptr?
S>Только не забыть задать ему первоначальный S>размер отличный от нуля, а то vector, созданный дефолтным конструктором, сначала запрашивает память под объект,
Дефолтный конструктор создаёт пустой вектор, и на популярных реализациях с нулевой capacity.
S> потом под два, копируя туда содержание предыдущего буфера и удаляя его, потом под четыре и так далее, кто-то S>скажет, что использовать такой способ организации буфера правильно?
При неизвестном конечном размере — вполне себе, добавление amortized O(1) (в системах подверженных фрагментации памяти, лучше брать factor меньше золотого сечения)
А если размер известен, либо есть "типичное" значение, то тогда просто делается .reserve(x).
EP>Во-первых он файл уже отмэппил файл в память, без всяких аллокаций. А во-вторых, даже если выделять память под весь файл, malloc от вектора практически ничем не будет отличатся в плане скорости
Если не изменяет память, то ТС в vector пихает структуру std::string, std::basic_string::_Alloc_hider::_M_p которой содержит
указатель на кусок памяти с строкой, который аллоцируется, с копированием строки из файла, в строчке std::getline(ss, item, delim))
Так что кроме мапа файла есть ещё куча кусков, разбросанных по просторам виртуальной памяти.
EP>Указатели на внутренние элементы массива в unique_ptr?
Думал не заметите
EP>Дефолтный конструктор создаёт пустой вектор, и на популярных реализациях с нулевой capacity.
Это понятно, беда начинается, когда начинаем пустому vector делать push_back/emplace_back. Вот тут
он начинает наращивать свою capacity, последовательно-куча новых аллокаций на первых элементах.
EP>При неизвестном конечном размере — вполне себе, добавление amortized O(1) (в системах подверженных фрагментации памяти, лучше брать factor меньше золотого сечения) EP>А если размер известен, либо есть "типичное" значение, то тогда просто делается .reserve(x).
Это понятно, только у ТС так не сделано, потому и упомянуто.
Здравствуйте, smeeld, Вы писали:
EP>>Во-первых он файл уже отмэппил файл в память, без всяких аллокаций. А во-вторых, даже если выделять память под весь файл, malloc от вектора практически ничем не будет отличатся в плане скорости S>Если не изменяет память, то ТС в vector пихает структуру std::string, std::basic_string::_Alloc_hider::_M_p которой содержит
Ты же отвечал на сообщение alex_public? А у него mapped_file_source.
EP>>Дефолтный конструктор создаёт пустой вектор, и на популярных реализациях с нулевой capacity. S>Это понятно, беда начинается, когда начинаем пустому vector делать push_back/emplace_back. Вот тут S>он начинает наращивать свою capacity, последовательно-куча новых аллокаций на первых элементах.
Тут основная задача не в том чтобы на каждой итерации была одна аллокация вместо логарифма, а в том чтобы вообще избавится от аллокаций на каждой итерации, т.е. переиспользовать уже имеющуюся capacity. В этом случае от O(log(max_tokens_per_string)) аллокаций на всё приложение не будет никакой беды (хотя конечно reserve не помешает, но не критичен).
EP>Ты же отвечал на сообщение alex_public? А у него mapped_file_source.
У alex_public всё как надо, мене не понятно только одно: зачем
тащить всякое из boost, когда в любой ОС есть нативные средства
маппинга, который boost и врайпит. Мне, года проведшему на чистом
posix API в solaris, не понятно, почему такая мода на библиотеки?
Здравствуйте, smeeld, Вы писали:
S>У alex_public всё как надо, мене не понятно только одно: зачем S>тащить всякое из boost, когда в любой ОС есть нативные средства S>маппинга, который boost и врайпит. Мне, года проведшему на чистом S>posix API в solaris, не понятно, почему такая мода на библиотеки?
1. Кроссплатформенность.
2. Это часть Boost.Iostreams, в которой определен набор концепций-интерфейсов. Соответственно враппер даёт нужный интерфейс, со всеми вытекающими.
3. Идиоматичность — RAII, etc.
Учитывая всё это, мне вот наоборот непонятно зачем кому-то использовать голое API при наличии готовых библиотек, предоставляющих все необходимые возможности.
Однако ж, гонка вооружений по прострелу ноги в C++ впечатляет Жесть какая.
Under the following circumstances, the compilers are permitted to omit the copy- and move-constructors of class objects even if copy/move constructor and the destructor have observable side-effects.
Т.е. ладно бы компилятор был обязан отбросить, но он же не обязан- т.е. поведение программы непредсказуемо отличается между debug и release компиляцией и даже release с разными ключиками.
Только Java и C (без плюсанутых) спасут этот мир .
Распарсить большой файл, каждая строчка которого имеет формат вида:
a1;a2;a3;a4;...;a200
Необходимо каждую такую строку преобразовать в объект наподобие:
obj = ["a1", "a2", ..., "a200"]