Грабли: gcc 4.8.1 не соответствует C++ 11
От: SkyDance Земля  
Дата: 17.10.13 04:09
Оценка: 40 (7) :))) :)
Товарищи после обновления с 4.7 на 4.8.1 получили жуткие тормоза и набор всяческих странных спецэффектов. Причем в 4.7 все работало отлично. Пришлось вооружиться профайлером и дебаггером, убить пару часов чтобы найти постине феерический баг [C++0x] std::list::size complexity

Оказывается, эти гении в GNU специально сломали совместимость со стандартом C++ 11, где английским по пустому листу сказано — size() должен иметь complexity O(1), на что у нас во многих местах заложена логика. Причина, которую там называют — мол, с C++98 size() был O(n), и чтобы не ломать бинарную совместимость (и не добавлять счетчик в std::list) эти гении тупо считают size() как distance(being(), end())

Не знаю, кто там у них такой умный, но на всякий случай предупрежу об этом занятном эффекте. Все-таки, обычно принято ругать MSVC, а тут мне очень хочется оторвать кому-то руки и пришить таки к плечам.
Re: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Erop Россия  
Дата: 17.10.13 04:46
Оценка: +1
Здравствуйте, SkyDance, Вы писали:

SD>Не знаю, кто там у них такой умный, но на всякий случай предупрежу об этом занятном эффекте. Все-таки, обычно принято ругать MSVC, а тут мне очень хочется оторвать кому-то руки и пришить таки к плечам.


И этот кто-то -- комитет, так как менять сложность метода между версиями --

Впрочем вы тоже молодцы, заложились на такое тонкое место.
В любом случае это повод заменить list на самописный...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: SkyDance Земля  
Дата: 17.10.13 04:55
Оценка:
E>И этот кто-то -- комитет, так как менять сложность метода между версиями --

Не то чтобы меняли... В C++98 допускалось для list иметь O(n) или O(1) (implementation defined), начиная, кажется, с C++03 уже было O(1). Как минимум про 4.7 я точно могу сказать, что работало как надо. Привыкли, расслабились, и получили.
Re[3]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Erop Россия  
Дата: 17.10.13 05:06
Оценка: +2
Здравствуйте, SkyDance, Вы писали:

SD>Не то чтобы меняли... В C++98 допускалось для list иметь O(n) или O(1) (implementation defined), начиная, кажется, с C++03 уже было O(1). Как минимум про 4.7 я точно могу сказать, что работало как надо. Привыкли, расслабились, и получили.


Ну это всё демонстрирует одно из свойств стандарта, из-за чего юзать stl опасно. Там слишком много неопределённостей...

Если мне не изменяет память, то в 03 ещё было можно и так и сяк. Но какая разница в какой момент комитет поменял правила? Вот я например, придерживаюсь политики "поменял семантику -- поменяй и идентификатор", а они, похоже, нет...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Грабли: gcc 4.8.1 не соответствует C++ 11
От: zaufi Земля  
Дата: 17.10.13 05:54
Оценка: 1 (1) +7
Здравствуйте, SkyDance, Вы писали:

SD>Товарищи после обновления с 4.7 на 4.8.1 получили жуткие тормоза и набор всяческих странных спецэффектов. Причем в 4.7 все работало отлично. Пришлось вооружиться профайлером и дебаггером, убить пару часов чтобы найти постине феерический баг [C++0x] std::list::size complexity


SD>Оказывается, эти гении в GNU специально сломали совместимость со стандартом C++ 11, где английским по пустому листу сказано — size() должен иметь complexity O(1), на что у нас во многих местах заложена логика. Причина, которую там называют — мол, с C++98 size() был O(n), и чтобы не ломать бинарную совместимость (и не добавлять счетчик в std::list) эти гении тупо считают size() как distance(being(), end())


ну насколько я помню там терки были о все сводилось к тому, что или size() константный и splice() становится линейным вместо константного, каким он был когда size() был O(n), ... или одно из двух...

мне лично "по душе", как все было -- т.е. size() у листа O(n) как и положено иметь list'у (будучи тупой структурой данных), но вот константность splice() мне лично кучу раз пригоджалась (в отличии от size(), о котором я с универа не думал как о константной операции), чем и был для меня привлекателен std::list...

а сейчас я испытываю ровно противоположные эмоции про комитетчиков, которые, наверное ориентируясь на стоны java программистов сделали size() константным (в ущерб splice())...

SD>Не знаю, кто там у них такой умный, но на всякий случай предупрежу об этом занятном эффекте. Все-таки, обычно принято ругать MSVC, а тут мне очень хочется оторвать кому-то руки и пришить таки к плечам.


аналогично, но применительно к комитетчикам

@#$-слова удалены
Re[2]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: slava_phirsov Россия  
Дата: 17.10.13 09:05
Оценка: -5
Здравствуйте, Erop, Вы писали:

E>В любом случае это повод заменить list на самописный...


Скорее это повод работать на кондовом C++ обр. 98 г., предоставляя более молодым и нетерпеливым пасюкам почетное право первыми сунуть башку в крысоловку
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 17.10.13 10:05
Оценка: +1
Здравствуйте, SkyDance, Вы писали:

SD> на что у нас во многих местах заложена логика.


Что за логика, если не секрет?

SD> Причина, которую там называют — мол, с C++98 size() был O(n), и чтобы не ломать бинарную совместимость (и не добавлять счетчик в std::list) эти гении тупо считают size() как distance(being(), end())


Я согласен со Степановым:

size() used to be linear time in case of STL lists. It was the right decision since if a user wants to keep a count it is easy to do, but usually you do not need it and it makes splice linear time (it used to be constant). But the standard committee insisted that I change it to constant time. I had no choice. We will have to change this requirement eventually.

splice у списка должен быть константным. У std::forward_list вообще нет size().

SD>Не знаю, кто там у них такой умный, но на всякий случай предупрежу об этом занятном эффекте. Все-таки, обычно принято ругать MSVC, а тут мне очень хочется оторвать кому-то руки и пришить таки к плечам.


Там где важно, я использую Boost.Container — получается единообразно на всех платформах.
Re[4]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 17.10.13 10:14
Оценка:
Здравствуйте, Erop, Вы писали:

E>Если мне не изменяет память, то в 03 ещё было можно и так и сяк. Но какая разница в какой момент комитет поменял правила? Вот я например, придерживаюсь политики "поменял семантику -- поменяй и идентификатор", а они, похоже, нет...


Не вижу смысла в этом конкретном случае — сам же говоришь что можно было и O(1) и O(N).
В C++11 просто сузили допустимые варианты, причём новые варианты являются подмножеством старых
Тот кто в старых версиях закладывался на одно из проведений — ССЗБ.
В этой ситуации формально виновата libstdc++
Re[5]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Erop Россия  
Дата: 17.10.13 10:24
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>В C++11 просто сузили допустимые варианты, причём новые варианты являются подмножеством старых

Ну и вынудили авторов популярных обеих двух реализаций поменять семантику не меняя идентификаторов...

EP>Тот кто в старых версиях закладывался на одно из проведений — ССЗБ.

Ну, я бы даже расширил, тот, кто вообе закладывался на какие-то особенности поведения stl -- ССЗБ.
Только беда тут в том, что в большом проекте единственный путь не закладываться состоит в том, что не надо вообще в подобных алгоритмах контейнеры из stl юзать...
EP>В этой ситуации формально виновата libstdc++
Либа виновата быть не может, всегда виноваты люди...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[6]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 17.10.13 10:55
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>В C++11 просто сузили допустимые варианты, причём новые варианты являются подмножеством старых

E>Ну и вынудили авторов популярных обеих двух реализаций поменять семантику не меняя идентификаторов...

А какая вторая?

EP>>Тот кто в старых версиях закладывался на одно из проведений — ССЗБ.

E>Ну, я бы даже расширил, тот, кто вообе закладывался на какие-то особенности поведения stl -- ССЗБ.

На особенности _реализации_.

E>Только беда тут в том, что в большом проекте единственный путь не закладываться состоит в том, что не надо вообще в подобных алгоритмах контейнеры из stl юзать...


Если нужны какие-то особенности, то нужно использовать одну конкретную реализацию. Помимо тех что идут из коробки, есть и stand-alone.
Re[7]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Erop Россия  
Дата: 17.10.13 12:15
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:


EP>Помимо тех что идут из коробки, есть и stand-alone.


Тут тоже легко нарваться, особенно в библиотечном коде...
ей-ей, намного проще написать свой какой-то список, не std::list, а что-то_другое::list и не морочится
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 17.10.13 12:47
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Помимо тех что идут из коробки, есть и stand-alone.

E>Тут тоже легко нарваться

Но это ведь не проблема STL, а вообще любых сторонних библиотек, на любую тему

E>ей-ей, намного проще написать свой какой-то список, не std::list, а что-то_другое::list и не морочится


1. Всё-таки есть уже готовые: boost::container::list, boost::intrusive::list
2. Если свой, то лучше не list, а что-то типа list_pool — когда несколько list'ов живут в одном vector'е: например.
Re[9]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Erop Россия  
Дата: 17.10.13 12:50
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>Но это ведь не проблема STL, а вообще любых сторонних библиотек, на любую тему


Нет, тут легко нарваться на то, что код вокруг использует один stl, а ты другой. Ну, сажем, что-то может не так слинковаться, например...


EP>1. Всё-таки есть уже готовые: boost::container::list, boost::intrusive::list

Ну если буст вообще юзать, то можно и их взять.

EP>2. Если свой, то лучше не list, а что-то типа list_pool — когда несколько list'ов живут в одном vector'е: например.


Ну это уже от целей и задач зависит, однако.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[3]: минусующим
От: slava_phirsov Россия  
Дата: 17.10.13 15:49
Оценка: -4 :))
Мда, вот и выросло поколение, не помнящее кипеж вокруг std::vector<bool> , спецификаций исключений, перегрузки по целому и указателю, экспорта шаблонов и прочих ныне легендарных гоч "старого" C++. А сколько новых граблей рассыпано по новому стандарту щедрой, так сказать, рукой, отцов, так сказать, основателей
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Re: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Шахтер Интернет  
Дата: 17.10.13 18:17
Оценка: +2
Здравствуйте, SkyDance, Вы писали:

SD>Товарищи после обновления с 4.7 на 4.8.1 получили жуткие тормоза и набор всяческих странных спецэффектов. Причем в 4.7 все работало отлично. Пришлось вооружиться профайлером и дебаггером, убить пару часов чтобы найти постине феерический баг [C++0x] std::list::size complexity


SD>Оказывается, эти гении в GNU специально сломали совместимость со стандартом C++ 11, где английским по пустому листу сказано — size() должен иметь complexity O(1), на что у нас во многих местах заложена логика. Причина, которую там называют — мол, с C++98 size() был O(n), и чтобы не ломать бинарную совместимость (и не добавлять счетчик в std::list) эти гении тупо считают size() как distance(being(), end())


SD>Не знаю, кто там у них такой умный, но на всякий случай предупрежу об этом занятном эффекте. Все-таки, обычно принято ругать MSVC, а тут мне очень хочется оторвать кому-то руки и пришить таки к плечам.


Отрывать руки надо тем, кто бездумно пользуется стандартной библиотекой в серьёзных проектах.
В XXI век с CCore.
Копай Нео, копай -- летать научишься. © Matrix. Парадоксы
Re: Грабли: gcc 4.8.1 не соответствует C++ 11
От: niXman Ниоткуда https://github.com/niXman
Дата: 17.10.13 19:09
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Товарищи после обновления с 4.7 на 4.8.1 получили жуткие тормоза и набор всяческих странных спецэффектов. Причем в 4.7 все работало отлично. Пришлось вооружиться профайлером и дебаггером, убить пару часов чтобы найти постине феерический баг [C++0x] std::list::size complexity


SD>Оказывается, эти гении в GNU специально сломали совместимость со стандартом C++ 11, где английским по пустому листу сказано — size() должен иметь complexity O(1), на что у нас во многих местах заложена логика. Причина, которую там называют — мол, с C++98 size() был O(n), и чтобы не ломать бинарную совместимость (и не добавлять счетчик в std::list) эти гении тупо считают size() как distance(being(), end())


SD>Не знаю, кто там у них такой умный, но на всякий случай предупрежу об этом занятном эффекте. Все-таки, обычно принято ругать MSVC, а тут мне очень хочется оторвать кому-то руки и пришить таки к плечам.


т.е. все грабли и тормоза, из-за единственного list::size()? я все правильно понял?
да, это действительно ужасно.
пачка бумаги А4 стОит 2000 р, в ней 500 листов. получается, лист обычной бумаги стОит дороже имперского рубля =)
Re[2]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: SkyDance Земля  
Дата: 17.10.13 22:10
Оценка: 18 (2) :)
EP>Что за логика, если не секрет?

Не секрет — есть (большой) пул соединений, и в паре мест брался их size(), чтобы определить, надо еще запуливать, или пока хватит.

boost по ограничениям нетехнического характера использовать в этом проекте не можем.
Re[6]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: SkyDance Земля  
Дата: 17.10.13 22:11
Оценка:
E>Ну, я бы даже расширил, тот, кто вообе закладывался на какие-то особенности поведения stl -- ССЗБ.

Это не особенности поведения STL, а таки стандарт C++. Как раз на implementation defined особенности у нас нигде не было закладок.
Re[3]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 17.10.13 23:52
Оценка: +1
Здравствуйте, SkyDance, Вы писали:

EP>>Что за логика, если не секрет?

SD>Не секрет — есть (большой) пул соединений, и в паре мест брался их size(), чтобы определить, надо еще запуливать, или пока хватит.

А подробней. Может там достаточно vector'а или deque'и?
Re[4]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: SkyDance Земля  
Дата: 18.10.13 00:18
Оценка:
EP>А подробней. Может там достаточно vector'а или deque'и?

vector при insert/erase инвалидирует все итераторы и ссылки.
аналогично, deque инвалидирует итераторы при erase не из начала/конца.

Попробую объяснить, почему это важно. Пул соединений реализован следующим образом: внутри есть два списка, pending и connected. Новые соединения (которые находятся в неблокирующем connect()) попадают в первый список, и по завершению handshake соединение двигается из pending в connected, где и сидит в ожидании, когда его вынут для совершения полезной работы.

Примерно вот так:
pending_.emplace_back(new connection(service_, endpoint_));
connection_list_t::iterator it = pending_.end();

(*it)->async_connect([this, it](int _errno)
    {
        std::unique_ptr<connection> c = std::move(*it);
        pending_connect_.erase(it);

        if (_errno == 0)
            ready_conn = ready_.insert(ready_.end(), std::move(c));
    });


Конечно, код на самом деле чуть сложнее, но общую идею должно быть видно. Не исключаю, что есть способы сделать это более удобно или эффективно. Если вы их знаете, подсказывайте.
Re[5]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 18.10.13 00:35
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Примерно вот так:

[...]
SD>Конечно, код на самом деле чуть сложнее, но общую идею должно быть видно. Не исключаю, что есть способы сделать это более удобно или эффективно. Если вы их знаете, подсказывайте.

Во-первых, как я понял, ready_ может быть vector'ом (если нельзя ивалидировать ссылки то либо не выходить за пределы capacity сделав предварительный reserve, либо тогда уже deque).
Далее — .size() ведь у ready_ оценивается?

Во-вторых, я не вижу смысла в pending_ — соединение во время handshake может жить в лямбде (если оно как shared_ptr, то просто захватить, если только movable — то move'нуть через bind, если есть C++1y то generalized capture).
Re[6]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 18.10.13 00:52
Оценка:
EP>Во-вторых, я не вижу смысла в pending_ — соединение во время handshake может жить в лямбде (если оно как shared_ptr, то просто захватить, если только movable — то move'нуть через bind, если есть C++1y то generalized capture).

То есть получится как-то так:
auto &&conn = make_unique<connection>(service_, endpoint_);
auto &conn_ref = *conn;

conn_ref.async_connect
(
    bind
    (
        [this](decltype(conn) &c, int _errno)
        {
            if (_errno == 0)
                ready_conn = ready_.insert(ready_.end(), std::move(c));
        }, move(conn), _1
    )
);
Re[6]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: SkyDance Земля  
Дата: 18.10.13 00:59
Оценка:
EP>Во-первых, как я понял, ready_ может быть vector'ом (если нельзя ивалидировать ссылки то либо не выходить за пределы capacity сделав предварительный reserve, либо тогда уже deque).

Cоединение одноразовое. Когда оно забирается (borrow) из пула, его следует удалить из ready_. Обратно оно уже не возвращается (на самом деле, попадает в список requested_ тем же макаром).

EP>Далее — .size() ведь у ready_ оценивается?


И у pending_ тоже, когда выясняется, надо ли еще добавлять в пул (у пула непростой алгоритм autoscaling'а, спецификации этого места менять мы не можем).

EP>Во-вторых, я не вижу смысла в pending_ — соединение во время handshake может жить в лямбде (если оно как shared_ptr, то просто захватить, если только movable — то move'нуть через bind, если есть C++1y то generalized capture).


Соединение не shared_ptr, только movable. C++1y нет, есть только C++ 11. Но даже если бы был, все равно pending_ нужно иметь для того, чтобы при закрытии пула немедленно закрыть все pending соединения.
Re[7]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 18.10.13 01:55
Оценка:
Здравствуйте, SkyDance, Вы писали:

EP>>Во-первых, как я понял, ready_ может быть vector'ом (если нельзя ивалидировать ссылки то либо не выходить за пределы capacity сделав предварительный reserve, либо тогда уже deque).

SD>Cоединение одноразовое. Когда оно забирается (borrow) из пула, его следует удалить из ready_. Обратно оно уже не возвращается (на самом деле, попадает в список requested_ тем же макаром).

Насколько я понял, ready_ всё ещё может быть вектором.

EP>>Во-вторых, я не вижу смысла в pending_ — соединение во время handshake может жить в лямбде (если оно как shared_ptr, то просто захватить, если только movable — то move'нуть через bind, если есть C++1y то generalized capture).

SD>Соединение не shared_ptr, только movable. C++1y нет, есть только C++ 11.

Для C++11 я показал вариант выше.

SD>Но даже если бы был, все равно pending_ нужно иметь для того, чтобы при закрытии пула немедленно закрыть все pending соединения.


Если pending_ список нужен в любом случае — тот тут особо ничего не поделать. + использовать отдельный счётчик из-за O(N) .size()

Далее, если важна производительность, можно попробовать избавится от аллокаций:
1. Убрать unique_ptr — использовать просто connection — оно должно быть movable
2. Попытаться избежать аллокации узлов в pending_. Нужно не удалять узел, а делать splice в empty list (нужная версия splice — O(1)).
Другой вариант: сделать простой list_pool: узлы разных списков будут плотно хранится в одном векторе/дэке.
Re[8]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: SkyDance Земля  
Дата: 18.10.13 02:43
Оценка:
EP>Насколько я понял, ready_ всё ещё может быть вектором.

Не совсем понимаю, как. Соеднение в ready_ может быть закрыто со стороны peer, в этот момент его нужно удалить из ready_, притом это легко может быть и в середине вектора, что инвалидирует все итераторы за искомым.

EP>Если pending_ список нужен в любом случае — тот тут особо ничего не поделать. + использовать отдельный счётчик из-за O(N) .size()


Так и поступили.

EP>1. Убрать unique_ptr — использовать просто connection — оно должно быть movable


connection — интерфейс, за которым прячутся полиморфные реализации разных вариантов соединений, т.е. нужно иметь его по указателю. Кроме того, unique_ptr оверхеда практически не добавляет.

EP>2. Попытаться избежать аллокации узлов в pending_. Нужно не удалять узел, а делать splice в empty list (нужная версия splice — O(1)).


Примерно так и сделано behind the scenes с помощью custom allocator, которые, к счастью, в C++ 11 уже более-менее рабочие.

EP>Другой вариант: сделать простой list_pool: узлы разных списков будут плотно хранится в одном векторе/дэке.


Да, такую реализацию рассматривали, в ней придется самостоятельно заниматься менеджментом указателей left/right, не хотелось. Возможно, вернемся к этому варианту, если профайлер докажет такую необходимость. Пока вроде ее нет.
Re[7]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Erop Россия  
Дата: 18.10.13 07:15
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Это не особенности поведения STL, а таки стандарт C++. Как раз на implementation defined особенности у нас нигде не было закладок.



А как вы это проверяли?..

В общем я бы не рискнул на такую спорную, тем более недавно изменившуюся тонкость закладываться, а вы рискнули. Ретроспективно я как бы оказался прав, а вы нет. Но задним умом всякий умён же.

Конструктивный вывод может состоять в том, что бы, подумать, а не заложились лы вы ещё на какое-нибудь тонкое место, и написать тестик, который се такие места тестит.
Сильно сэкономит время на детект ситуации не только при смене версии gcc, но и при порте, если вдруг понадобится...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[9]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 18.10.13 08:32
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Не совсем понимаю, как. Соеднение в ready_ может быть закрыто со стороны peer, в этот момент его нужно удалить из ready_,


Согласен, этот момент пропустил.

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


Да, удалять из середины вектора — не самая лучшая затея (инвалидация и O(N)).

EP>>1. Убрать unique_ptr — использовать просто connection — оно должно быть movable


SD>connection — интерфейс, за которым прячутся полиморфные реализации разных вариантов соединений, т.е. нужно иметь его по указателю. Кроме того, unique_ptr оверхеда практически не добавляет.


Сам unique_ptr не добавляет, а добавляет сопутствующая аллокация + индерекции при использовании. Если нужно полиморфное поведение, то есть несколько вариантов:
1. boost::varaint, или если нет, то самописный велик. Уберёт аллокацию и индерекцию, но размер будет максимальным из всех соединений + sizeof(tag) + alignment, и набор типов прибит гвоздями во время компиляции.
2. использовать простой per-thread аллокатор, либо pool'ы для соединений разных типов — существенно удешевит аллокацию.

EP>>2. Попытаться избежать аллокации узлов в pending_. Нужно не удалять узел, а делать splice в empty list (нужная версия splice — O(1)).

SD>Примерно так и сделано behind the scenes с помощью custom allocator, которые, к счастью, в C++ 11 уже более-менее рабочие.

Я имел ввиду, что вместо
pending_connect_.erase(it);
ready_.insert(ready_.end(), std::move(c));

можно просто сделать
ready_.splice(ready_.end(), pending_connect_, it);

Конечно свой аллокатор для узлов списка будет полезен сам по себе, и существенно сократит стоимость erase+insert, но splice всё-таки намного дешевле. То есть можно использовать и аллокатор и splice.

EP>>Другой вариант: сделать простой list_pool: узлы разных списков будут плотно хранится в одном векторе/дэке.


SD>Да, такую реализацию рассматривали, в ней придется самостоятельно заниматься менеджментом указателей left/right, не хотелось. Возможно, вернемся к этому варианту, если профайлер докажет такую необходимость. Пока вроде ее нет.


Насколько я вижу, в этом случае ручной доступ к left-right будет только в трёх случаях, два из которых тривиальные:
1. создание нового узла — установка left и right на nil
2. получение следующего узла из текущего (например для обхода всех pending)
3. splice — переброс узла из одного списка, в другой (причём достаточно переброски в начало, а не в любую позицию)

Handle'ом к узлу/список может быть простой uint16 (дешевле указателя на x32), либо если соединений больше 65536, то тогда уже uint32 (дешевле указателя на x64):
template <typename T, typename NodeHandle = uint16_t>
class ListPool
{
    struct Node
    {
        T value; 
        NodeHandle left, right; 
    };
    vector<Node> pool; 
    NodeHandle free_list;
// ...
};
Re: Грабли: gcc 4.8.1 не соответствует C++ 11
От: jazzer Россия Skype: enerjazzer
Дата: 18.10.13 15:42
Оценка:
Здравствуйте, SkyDance, Вы писали:

SD>Не знаю, кто там у них такой умный, но на всякий случай предупрежу об этом занятном эффекте. Все-таки, обычно принято ругать MSVC, а тут мне очень хочется оторвать кому-то руки и пришить таки к плечам.


Я знаю, что у тебя буст нельзя, а вот у тех, кому можно, проблем нет — они юзают boost::size и boost::distance — первый для std::list просто-напросто не определен, потому что нельзя (было) гарантировать O(1)
Так что и напарываться не на что.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[10]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 19.10.13 22:16
Оценка:
Здравствуйте, Erop, Вы писали:

EP>>Но это ведь не проблема STL, а вообще любых сторонних библиотек, на любую тему

E>Нет, тут легко нарваться на то, что код вокруг использует один stl, а ты другой. Ну, сажем, что-то может не так слинковаться, например...

Я вот, например, в статической библиотеке спокойно использую Boost такой версии, какой мне удобно. А клиенты могут использовать любой другой.
Одно из решений проблемы в переименовании namespace'а (влияет на mangling) + как синтаксический сахар namespace alias (не влияет на mangling).
Утилита Boost.BCP умеет автоматически переименовывать namespace и вставлять namespace alias. Например так:
// replaces
namespace boost {
// with
namespace any_name_you_wish {} namespace boost = any_name_you_wish; namespace any_name_you_wish {


AFAIK, в STL библиотеках типа SGI STL или STLPort используется не прибитое гвоздями имя namespace'а, а что-то типа макроса, то есть ещё легче чем в Boost.
Re[10]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: SkyDance Земля  
Дата: 20.10.13 22:49
Оценка:
EP>1. boost::varaint, или если нет, то самописный велик. Уберёт аллокацию и индерекцию, но размер будет максимальным из всех соединений + sizeof(tag) + alignment, и набор типов прибит гвоздями во время компиляции.

Не можем, тип соединения — runtime, берется из конфигурации.

EP>2. использовать простой per-thread аллокатор, либо pool'ы для соединений разных типов — существенно удешевит аллокацию.


А это уже сделано, опять же behind the scenes, я про аллокаторы просто не стал писать, ибо оно не относится к теме.

EP>Конечно свой аллокатор для узлов списка будет полезен сам по себе, и существенно сократит стоимость erase+insert, но splice всё-таки намного дешевле. То есть можно использовать и аллокатор и splice.


Можно, но arena allocator в моём случае один atomic fetch and add, освобождается всё вместе с самим пулом. Можно, конечно, посмотреть, будет ли со splice еще быстрее, но, во-первых, вряд ли, во-вторых, даже если будет, весь этот выигрыш будет околонулевым в сравнении с единственным вызовом функции socket connect()

EP>Насколько я вижу, в этом случае ручной доступ к left-right будет только в трёх случаях, два из которых тривиальные:


Да, я понимаю. Просто пока не видно необходимости, если верить профайлеру, в этом месте проблем больше нет.
Re[11]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Erop Россия  
Дата: 21.10.13 05:37
Оценка:
Здравствуйте, Evgeny.Panasyuk, Вы писали:

EP>AFAIK, в STL библиотеках типа SGI STL или STLPort используется не прибитое гвоздями имя namespace'а, а что-то типа макроса, то есть ещё легче чем в Boost.


Ну что-то такое да, можно, но это уже почти совсем тоже самое, что просты выдрать нужную версию листа и положить в другой наймспей, жа и юзать
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[11]: Грабли: gcc 4.8.1 не соответствует C++ 11
От: Evgeny.Panasyuk Россия  
Дата: 21.10.13 12:40
Оценка:
Здравствуйте, SkyDance, Вы писали:

EP>>1. boost::varaint, или если нет, то самописный велик. Уберёт аллокацию и индерекцию, но размер будет максимальным из всех соединений + sizeof(tag) + alignment, и набор типов прибит гвоздями во время компиляции.

SD>Не можем, тип соединения — runtime, берется из конфигурации.

boost::variant это как раз runtime dispatch по tag'у.

Кстати, если после считывания конфигурации используется только один тип соединения, и этот тип соединения не подгружается из внешних динамических библиотек, то этот runtime dispatch можно вынести на самый вверх call stack'а, а не делать при работе с каждым отдельным соединением.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.