Сообщение Re[2]: unorderd_map внутри unordered_map и emplace() от 02.08.2021 9:52
Изменено 02.08.2021 15:02 watchmaker
Re[2]: unorderd_map внутри unordered_map и emplace()
Здравствуйте, rg45, Вы писали:
R> использование метода find в данном случае нецелесообразно, поскольку метод emplace, так же точно как и метод insert, выполняет вставку только в том случае, если элемент с данным ключом отсутствует в контейнере. Если элемент с таким ключом уже существует, то оба эти метода действуют подобно find.
Но с точки зрения эффективности разница есть и эти методы не подобны find по скорости.
Вызов find(key) проверит наличие ключа в контейнере. А вызов emplace(key, value) сначала выделит память под новый узел, потом сконструирует в нём пару ключ-значение, только потом запустит поиск в контейнере, и в случае успешного поиска разрушит этот временный узел.
То есть, по сравнению с find, всегда будет выделение памяти и конструирование копий ключа и значения(!) в нём.
И если ожидается, что ключ должен найтись в ассоциативном контейнере, то может оказаться выгоднее делать два захода: сначала более лёгкий find и лишь в случае его неудачи тяжёлый emplace. Даже несмотря на то, что из-за этого придётся сделать поиск ещё раз. В случае, когда в map'е кешируются какие-то тяжёлые объекты, разница становится очень заметна.
(Тут ещё можно огорчится, что к несчастью интерфейс emplace_hint оказался тоже криво спроектированным в раннем С++ и не может ускорить вставку для хеш-таблиц, убрав необходимость повторного поиска).
А сделать реализацию emplace без безусловного выделения памяти и создания временных узлов для разных типов либо невозможно, либо настолько сложно, что авторы реализаций STL тоже этого не делают и делать не будут.
Конечно, этот дефект в дизайне unordered_map::emplace давно известен. И поэтому в С++17 появился вызов try_emplace c другой семантикой, который как раз позволяет не делать лишней работы, которую делает emplace, если ожидается, что ключ будет найден.
И уж если заменять пару find+emplace, то именно на try_emplace.
R> использование метода find в данном случае нецелесообразно, поскольку метод emplace, так же точно как и метод insert, выполняет вставку только в том случае, если элемент с данным ключом отсутствует в контейнере. Если элемент с таким ключом уже существует, то оба эти метода действуют подобно find.
Но с точки зрения эффективности разница есть и эти методы не подобны find по скорости.
Вызов find(key) проверит наличие ключа в контейнере. А вызов emplace(key, value) сначала выделит память под новый узел, потом сконструирует в нём пару ключ-значение, только потом запустит поиск в контейнере, и в случае успешного поиска разрушит этот временный узел.
То есть, по сравнению с find, всегда будет выделение памяти и конструирование копий ключа и значения(!) в нём.
И если ожидается, что ключ должен найтись в ассоциативном контейнере, то может оказаться выгоднее делать два захода: сначала более лёгкий find и лишь в случае его неудачи тяжёлый emplace. Даже несмотря на то, что из-за этого придётся сделать поиск ещё раз. В случае, когда в map'е кешируются какие-то тяжёлые объекты, разница становится очень заметна.
(Тут ещё можно огорчится, что к несчастью интерфейс emplace_hint оказался тоже криво спроектированным в раннем С++ и не может ускорить вставку для хеш-таблиц, убрав необходимость повторного поиска).
А сделать реализацию emplace без безусловного выделения памяти и создания временных узлов для разных типов либо невозможно, либо настолько сложно, что авторы реализаций STL тоже этого не делают и делать не будут.
Конечно, этот дефект в дизайне unordered_map::emplace давно известен. И поэтому в С++17 появился вызов try_emplace c другой семантикой, который как раз позволяет не делать лишней работы, которую делает emplace, если ожидается, что ключ будет найден.
И уж если заменять пару find+emplace, то именно на try_emplace.
Re[2]: unorderd_map внутри unordered_map и emplace()
Здравствуйте, rg45, Вы писали:
R> использование метода find в данном случае нецелесообразно, поскольку метод emplace, так же точно как и метод insert, выполняет вставку только в том случае, если элемент с данным ключом отсутствует в контейнере. Если элемент с таким ключом уже существует, то оба эти метода действуют подобно find.
В целом согласен, но хочется заострить внимание, что
с точки зрения эффективности разница есть и эти методы не подобны find по скорости.
Вызов find(key) проверит наличие ключа в контейнере. А вызов emplace(key, value) сначала выделит память под новый узел, потом сконструирует в нём пару ключ-значение, только потом запустит поиск в контейнере, и в случае успешного поиска разрушит этот временный узел.
То есть, по сравнению с find, всегда будет выделение памяти и конструирование копий ключа и значения(!) в нём.
И если ожидается, что ключ должен найтись в ассоциативном контейнере, то может оказаться выгоднее делать два захода: сначала более лёгкий find и лишь в случае его неудачи тяжёлый emplace. Даже несмотря на то, что из-за этого придётся сделать поиск ещё раз. В случае, когда в map'е кешируются какие-то тяжёлые объекты, разница становится очень заметна.
(Тут ещё можно огорчится, что к несчастью интерфейс emplace_hint оказался тоже криво спроектированным в раннем С++ и не может ускорить вставку для хеш-таблиц, убрав необходимость повторного поиска).
А сделать реализацию emplace без безусловного выделения памяти и создания временных узлов для разных типов либо невозможно, либо настолько сложно, что авторы реализаций STL тоже этого не делают и делать не будут.
Конечно, этот дефект в дизайне emplace для ассоциативных контейнеров давно известен. И поэтому в С++17 появился вызов try_emplace c другой семантикой, который как раз позволяет не делать лишней работы, которую делает emplace, если ожидается, что ключ будет найден.
И уж если заменять пару find+emplace, то именно на try_emplace или на operator[] (который теперь определён как тонкая обёртка над try_emplace).
R> использование метода find в данном случае нецелесообразно, поскольку метод emplace, так же точно как и метод insert, выполняет вставку только в том случае, если элемент с данным ключом отсутствует в контейнере. Если элемент с таким ключом уже существует, то оба эти метода действуют подобно find.
В целом согласен, но хочется заострить внимание, что
с точки зрения эффективности разница есть и эти методы не подобны find по скорости.
Вызов find(key) проверит наличие ключа в контейнере. А вызов emplace(key, value) сначала выделит память под новый узел, потом сконструирует в нём пару ключ-значение, только потом запустит поиск в контейнере, и в случае успешного поиска разрушит этот временный узел.
То есть, по сравнению с find, всегда будет выделение памяти и конструирование копий ключа и значения(!) в нём.
И если ожидается, что ключ должен найтись в ассоциативном контейнере, то может оказаться выгоднее делать два захода: сначала более лёгкий find и лишь в случае его неудачи тяжёлый emplace. Даже несмотря на то, что из-за этого придётся сделать поиск ещё раз. В случае, когда в map'е кешируются какие-то тяжёлые объекты, разница становится очень заметна.
(Тут ещё можно огорчится, что к несчастью интерфейс emplace_hint оказался тоже криво спроектированным в раннем С++ и не может ускорить вставку для хеш-таблиц, убрав необходимость повторного поиска).
А сделать реализацию emplace без безусловного выделения памяти и создания временных узлов для разных типов либо невозможно, либо настолько сложно, что авторы реализаций STL тоже этого не делают и делать не будут.
Конечно, этот дефект в дизайне emplace для ассоциативных контейнеров давно известен. И поэтому в С++17 появился вызов try_emplace c другой семантикой, который как раз позволяет не делать лишней работы, которую делает emplace, если ожидается, что ключ будет найден.
И уж если заменять пару find+emplace, то именно на try_emplace или на operator[] (который теперь определён как тонкая обёртка над try_emplace).