Re: Медленный C++
От: Evgeny.Panasyuk Россия  
Дата: 26.05.14 15:20
Оценка: 3 (2) +3
Здравствуйте, anatoly1, Вы писали:

A>Boost'овский split, кстати, ещё медленней работает.


Прежде всего, вместо создания множества std::string (с аллокацией на каждую), для выделения частей нужно использовать string view, например
typedef boost::iterator_range<std::string::const_iterator> string_view;
boost::algorithm::split умеет с ними работать.

То есть вместо:
std::vector<std::string> parts;
достаточно:
std::vector<string_view> parts;

A>В чём может быть причина такой низкой производительности у С++?

В версии на C++ аллоцируется много мутабельных строк, в то время как в версии на Python строки иммутабельные, для них просто берётся view.
Плюс, у тебя vector аллоцируется каждый раз создаётся заново, а нужно создать его только один раз и переиспользовать его capacity многократно.
Re[4]: Медленный C++
От: Pavel Dvorkin Россия  
Дата: 26.05.14 15:21
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Покапитанить решили? Не интересно так.


Интересно или нет, но эту функцию никто здесь не упомянул. Получилось впечатление, что либо нужно писать что-то совсем уж вручную (как у тебя), или без STL никак не обойтись. Вот я и решил напомнить...
With best regards
Pavel Dvorkin
Re: Медленный C++
От: Ikemefula Беларусь http://blogs.rsdn.org/ikemefula
Дата: 26.05.14 16:07
Оценка:
Здравствуйте, anatoly1, Вы писали:

A>В чём может быть причина такой низкой производительности у С++?


Скорее всего дело не в С++, а в том, как идет работа с файлом.

Если предположить, что и там и там это одинаково, то издержки что в С++, что в Пейтоне будут ничтожными на общем фоне.

Что бы исключить этот случай, нужно подсунуть вместо file open текстовую простыню. Если и там отстает С++, то дело в стл-структурах.
Re: Медленный C++
От: visual_wind  
Дата: 26.05.14 16:31
Оценка: 2 (1)
Здравствуйте, anatoly1, Вы писали:

A>С++ медленнее питона при парсинге файла.

[...]
A>В чём может быть причина такой низкой производительности у С++?

Была уже много лет назад вот эта
Автор: Denis2005
Дата: 16.09.06
тема, в которой полоскали split, правда бустовский. Там вот в этой
Автор: eao197
Дата: 17.09.06
ветке сокрушались о бустовской реализация is_any_of. Возможно, что и на текущий день ситуация со производительностью split кардинально не поменялась. Вот в этой
Автор: Андрей Хропов
Дата: 24.09.06
ветке автор приводил завершающие измерения проиводительности для разных языков.
Re: Медленный C++
От: anatoly1  
Дата: 26.05.14 17:30
Оценка:
Сделал так:

#include <string>
#include <vector>
#include <sstream>
#include <fstream>

using namespace std;

std::vector<std::string> &split(const std::string &s, char delim, std::vector<std::string> &elems) {
    std::stringstream ss(s);
    std::string item;
    for (int i = 0; std::getline(ss, item, delim); i++)
        elems[i] = std::move(item);
    return elems;
}

int main()
{
    std::ifstream f;
    f.open("test.csv");

    std:string s;
    std::vector<std::string> sv(201);

    while ( std::getline(f, s) )
        split(s, ';', sv);

    return 0;
}


Стало:

real    0m3.087s
user    0m2.569s
sys    0m0.081s
Re: Медленный C++
От: alex_public  
Дата: 26.05.14 18:15
Оценка: +1
Здравствуйте, anatoly1, Вы писали:

A>С++ медленнее питона при парсинге файла.


A>С++ — версия:

A>...

A>А вот питон — версия:

A>...

A>В чём может быть причина такой низкой производительности у С++?


Причина в том, что это у вас абсолютно разные коды. Версия на Питоне просто просматривает файл и всё. А версия на C++ ещё и делает некие дополнительные действия — создаёт копии определённых кусков данных из файла. Если вы хотите получить реальную C++ копию кода на Питоне, то это будет что-то вроде:
auto split(const char* s, const char* e, char d)
{
    vector<pair<const char*, const char*>> r;
    for(const char* c=s; c!=e; ++c) if(*c==d){
        r.emplace_back(s, c);
        s=c+1;
    }
    return r;
}
int main()
{
    mapped_file_source file("test.csv");
    for(auto l: split(file.data(), file.data()+file.size(), '\n'))
        auto s=split(l.first, l.second, ';');
    return 0;
}


И соответственно этот код работает у меня где-то в 2,5 раза быстрее, чем его аналог на Питоне.
Re[2]: Медленный C++
От: Evgeny.Panasyuk Россия  
Дата: 26.05.14 18:33
Оценка:
Здравствуйте, 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 не нужен — можно обрабатывать по месту.
Re[2]: Медленный C++
От: Evgeny.Panasyuk Россия  
Дата: 26.05.14 18:38
Оценка:
Здравствуйте, alex_public, Вы писали:

_>то это будет что-то вроде:

_>
_>auto split(const char* s, const char* e, char d)
_>{
_>    vector<pair<const char*, const char*>> r;
_>      // [...]
_>    return r;
_>}
_>

_>И соответственно этот код работает у меня где-то в 2,5 раза быстрее, чем его аналог на Питоне.

Можно ещё и vector не аллоцировать каждый раз, а переиспользовать capacity — будет ещё быстрее.
Re[2]: Медленный C++
От: Vain Россия google.ru
Дата: 26.05.14 19:01
Оценка: +1
Здравствуйте, smeeld, Вы писали:

S>А вообще прикольно писать парсеры не тупо на контейнерах, а примерно так:

S>struct parser{
надо создать форум "код по укурке"
[In theory there is no difference between theory and practice. In
practice there is.]
[Даю очевидные ответы на риторические вопросы]
Re[2]: Медленный C++
От: Evgeny.Panasyuk Россия  
Дата: 26.05.14 19:13
Оценка:
Здравствуйте, visual_wind, Вы писали:

_>Была уже много лет назад вот эта
Автор: Denis2005
Дата: 16.09.06
тема, в которой полоскали split, правда бустовский. Там вот в этой
Автор: eao197
Дата: 17.09.06
ветке сокрушались о бустовской реализация is_any_of. Возможно, что и на текущий день ситуация со производительностью split кардинально не поменялась. Вот в этой
Автор: Андрей Хропов
Дата: 24.09.06
ветке автор приводил завершающие измерения проиводительности для разных языков.


У этой темы было продолжение
Автор: Hard_Club
Дата: 15.07.13
.
Re: Медленный C++
От: Ops Россия  
Дата: 26.05.14 20:01
Оценка:
Здравствуйте, anatoly1, Вы писали:

A>Boost'овский split, кстати, ещё медленней работает.


Если boost, то есть tokenizer, для такой задачи самое то.
Переубедить Вас, к сожалению, мне не удастся, поэтому сразу перейду к оскорблениям.
Re[2]: Медленный C++
От: smeeld  
Дата: 26.05.14 20:11
Оценка: :))
Здравствуйте, alex_public, Вы писали:

_>И соответственно этот код работает у меня где-то в 2,5 раза быстрее, чем его аналог на Питоне.


Осталось немного, и придёт, наконец, понимание, что здесь, в этой задачке, контейнеры и стримы вообще
ни куда не упёрлись. Их нельзя использовать на каждый чих. Сформировать malloc-ом буфер с содержимым
файла, разбить его на токены или предложения нуль терминатором, последовательно с занесением указателей в массив.
Вот указатели, а лучше их unique_ptr, можно уже складывать в вектор. Только не забыть задать ему первоначальный
размер отличный от нуля, а то vector, созданный дефолтным конструктором, сначала запрашивает память под объект,
потом под два, копируя туда содержание предыдущего буфера и удаляя его, потом под четыре и так далее, кто-то
скажет, что использовать такой способ организации буфера правильно?
Re[3]: Медленный C++
От: Evgeny.Panasyuk Россия  
Дата: 26.05.14 20:29
Оценка:
Здравствуйте, smeeld, Вы писали:

S>Осталось немного, и придёт, наконец, понимание, что здесь, в этой задачке, контейнеры и стримы вообще

S>ни куда не упёрлись. Их нельзя использовать на каждый чих. Сформировать malloc-ом буфер с содержимым
S>файла,

Во-первых он файл уже отмэппил файл в память, без всяких аллокаций. А во-вторых, даже если выделять память под весь файл, malloc от вектора практически ничем не будет отличатся в плане скорости

S>разбить его на токены или предложения нуль терминатором, последовательно с занесением указателей в массив.

S>Вот указатели, а лучше их unique_ptr, можно уже складывать в вектор.

Указатели на внутренние элементы массива в unique_ptr?

S>Только не забыть задать ему первоначальный

S>размер отличный от нуля, а то vector, созданный дефолтным конструктором, сначала запрашивает память под объект,

Дефолтный конструктор создаёт пустой вектор, и на популярных реализациях с нулевой capacity.

S> потом под два, копируя туда содержание предыдущего буфера и удаляя его, потом под четыре и так далее, кто-то

S>скажет, что использовать такой способ организации буфера правильно?

При неизвестном конечном размере — вполне себе, добавление amortized O(1) (в системах подверженных фрагментации памяти, лучше брать factor меньше золотого сечения)
А если размер известен, либо есть "типичное" значение, то тогда просто делается .reserve(x).
Re[4]: Медленный C++
От: smeeld  
Дата: 26.05.14 20:54
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


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).

Это понятно, только у ТС так не сделано, потому и упомянуто.
Re[5]: Медленный C++
От: Evgeny.Panasyuk Россия  
Дата: 26.05.14 21:22
Оценка:
Здравствуйте, 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 не помешает, но не критичен).
Re[6]: Медленный C++
От: smeeld  
Дата: 26.05.14 21:44
Оценка: +1
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>Ты же отвечал на сообщение alex_public? А у него mapped_file_source.


У alex_public всё как надо, мене не понятно только одно: зачем
тащить всякое из boost, когда в любой ОС есть нативные средства
маппинга, который boost и врайпит. Мне, года проведшему на чистом
posix API в solaris, не понятно, почему такая мода на библиотеки?
й
Re: Медленный C++
От: Философ Ад http://vk.com/id10256428
Дата: 26.05.14 21:59
Оценка:
Здравствуйте, anatoly1, Вы писали:

A>С++ медленнее питона при парсинге файла.


A>
A>real    0m18.429s
A>user    0m15.919s
A>sys    0m0.259s
A>


A>
A>real    0m4.029s
A>user    0m3.346s
A>sys    0m0.124s
A>



Как измерения проводились?
Что означают эти строки?

чем "real" отличается от "sys"?

как я могу повторить ваш эксперимент?
Всё сказанное выше — личное мнение, если не указано обратное.
Re[7]: Медленный C++
От: Evgeny.Panasyuk Россия  
Дата: 26.05.14 22:15
Оценка: +2
Здравствуйте, smeeld, Вы писали:

S>У alex_public всё как надо, мене не понятно только одно: зачем

S>тащить всякое из boost, когда в любой ОС есть нативные средства
S>маппинга, который boost и врайпит. Мне, года проведшему на чистом
S>posix API в solaris, не понятно, почему такая мода на библиотеки?

1. Кроссплатформенность.
2. Это часть Boost.Iostreams, в которой определен набор концепций-интерфейсов. Соответственно враппер даёт нужный интерфейс, со всеми вытекающими.
3. Идиоматичность — RAII, etc.

Учитывая всё это, мне вот наоборот непонятно зачем кому-то использовать голое API при наличии готовых библиотек, предоставляющих все необходимые возможности.
Re[5]: Медленный C++
От: Aртём Австралия жж
Дата: 27.05.14 01:58
Оценка: -4 :)
Здравствуйте, anatoly1, Вы писали:

A>http://en.cppreference.com/w/cpp/language/copy_elision


Однако ж, гонка вооружений по прострелу ноги в 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 (без плюсанутых) спасут этот мир .
Re[2]: Медленный C++
От: anatoly1  
Дата: 27.05.14 04:36
Оценка:
Здравствуйте, Философ, Вы писали:

Ф>Как измерения проводились?

Ф>Что означают эти строки?
Ф>чем "real" отличается от "sys"?

http://linux.about.com/library/cmd/blcmdl1_time.htm

Ф>как я могу повторить ваш эксперимент?


Распарсить большой файл, каждая строчка которого имеет формат вида:
a1;a2;a3;a4;...;a200
Необходимо каждую такую строку преобразовать в объект наподобие:
obj = ["a1", "a2", ..., "a200"]
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.