Хобрук: Ваш путь к мастерству в программировании

Эффективность вставки карты STL: [] vs. вставка

Есть два способа вставки карты:

m[key] = val;

Or

m.insert(make_pair(key, val));

У меня вопрос, какая операция быстрее? Люди обычно говорят, что первый медленнее, потому что стандарт STL сначала «вставляет» элемент по умолчанию, если «ключ» не существует на карте, а затем назначает «val» элементу по умолчанию.

Но я не считаю, что второй способ лучше из-за make_pair. make_pair на самом деле - удобный способ создать «пару» по сравнению с pair<T1, T2>(key, val). В любом случае, оба они выполняют два присваивания: одно назначает «ключ» для «pair.first», а два - «val» для «pair.second». После создания пары карта вставляет элемент, инициализированный параметром pair.second.

Итак, первый способ - 1. «default construct of typeof(val)» 2. присвоение, второй способ - 1. присвоение 2. «copy construct of typeof(val)»

17.04.2011


Ответы:


1

Оба достигают разных целей.

m[key] = val;

Вставит новую пару "ключ-значение", если key еще не существует, или перезапишет старое значение, сопоставленное с key, если оно уже существует.

m.insert(make_pair(key, val));

Вставит пару только в том случае, если key еще не существует, оно никогда не перезапишет старое значение. Итак, выберите в соответствии с тем, чего вы хотите достичь.
На вопрос, что более эффективно: профиль. : P Наверное, я бы сказал первый способ. Присваивание (или копия) применимо для обоих способов, поэтому единственная разница заключается в конструкции. Как мы все знаем и должны реализовать, конструкция по умолчанию должна в основном не работать и, следовательно, быть очень эффективной. Копия и есть копия. Таким образом, в первом способе мы получаем «запрет на вмешательство» и копию, а в способе два мы получаем две копии.
Редактировать: в конце концов, доверяйте тому, что говорит вам ваше профилирование. Мой анализ был неправильным, как @Matthieu упоминает в своем комментарии, но это было мое предположение. :)


Затем у нас есть C ++ 0x, и двойная копия на втором пути будет нулевой, так как теперь пару можно просто переместить. В конце концов, я думаю, что он возвращается к моему первому пункту: используйте правильный способ для выполнения того, что вы хотите сделать.

17.04.2011
  • +1 за выбор семантики, а не производительности. Я думаю, что анализ немного ошибочен. Первый способ должен быть медленнее (по умолчанию), поскольку в обоих случаях копируются и ключ, и значение. 17.04.2011
  • @Matthieu: Хм, это правда. Я немного отредактирую, но оставлю свой анализ. Это еще раз показывает, что профилирование действительно подскажет, что быстрее. 17.04.2011
  • std::map<>::operator[] не инициализирует значения по умолчанию, он инициализирует значения значений, поэтому что-то вроде std::map<K, std::array<int, 1000>>::operator[] может быть чрезвычайно расточительным. 03.05.2012

  • 2

    В слабо загруженной системе с большим объемом памяти этот код:

    #include <map>
    #include <iostream>
    #include <ctime>
    #include <string>
    
    using namespace std;
    
    typedef map <unsigned int,string> MapType;
    const unsigned int NINSERTS = 1000000;
    
    int main() {
        MapType m1;
        string s = "foobar";
        clock_t t = clock();
        for ( unsigned int i = 0; i < NINSERTS; i++ ) {
            m1[i] = s;
        }
        cout << clock() - t << endl;
        MapType m2;
        t = clock();
        for ( unsigned int i = 0; i < NINSERTS; i++ ) {
            m2.insert( make_pair( i, s ) );
        }
        cout << clock() - t << endl;
    }
    

    производит:

    1547
    1453
    

    или аналогичные значения при повторных запусках. Таким образом, вставка (в данном случае) немного быстрее.

    17.04.2011

    3

    Что касается производительности, я думаю, что в целом они одинаковы. Могут быть некоторые исключения для карты с большими объектами, и в этом случае вы должны использовать [] или, возможно, emplace, который создает меньше временных объектов, чем 'insert'. Подробности см. В обсуждении здесь.

    Однако вы можете получить повышение производительности в особых случаях, если используете функцию подсказки для оператора вставки. Итак, глядя на вариант 2 из здесь:

    iterator insert (const_iterator position, const value_type& val);
    

    операцию «вставить» можно сократить до постоянного времени (из журнала (n)), если вы дадите хорошую подсказку (часто бывает, если вы знаете, что добавляете что-то в конце карты).

    22.04.2015

    4

    Мы должны уточнить анализ, отметив, что относительная производительность зависит также от типа (размера) копируемых объектов.

    Я проделал аналогичный эксперимент (с nbt) с картой (int -> set). Я знаю, что это ужасный поступок, но показательный для этого сценария. «Значение», в данном случае набор целых чисел, состоит из 20 элементов.

    Я выполняю миллион итераций [] = Vs. вставьте операции и выполните RDTSC / iter-count.

    [] = установить | 10731 цикл

    вставить (make_pair ‹>) | 26100 циклов

    Он показывает величину штрафа, добавленного из-за копирования. Конечно, CPP11 изменит картину.

    20.11.2014

    5

    Мой взгляд на это: стоит напомнить, что карты - это сбалансированное двоичное дерево, большинство модификаций и проверок занимают O (logN).

    На самом деле зависит от проблемы, которую вы пытаетесь решить.

    1) если вы просто хотите вставить значение, зная, что его еще нет, то [] будет делать две вещи: а) проверять, есть ли элемент или нет б) если его нет, создаст пару и сделает то, что вставить делает (двойная работа O (logN)), поэтому я бы использовал insert.

    2) если вы не уверены, есть он там или нет, то а) если вы проверили, есть ли элемент, выполнив что-то вроде if (map.find (item) == mp.end ()) парой строк выше где-нибудь, тогда используйте insert, из-за двойной работы [] будет выполнять b) если вы не проверили, тогда это зависит, потому что insert не изменит значение, если оно есть, [] будет, в противном случае они равны

    15.03.2017

    6

    Мой ответ не об эффективности, а о безопасности, которая имеет отношение к выбору алгоритма вставки:

    Вызовы [] и insert() вызовут деструкторы элементов. Это может иметь опасные побочные эффекты, если, скажем, у ваших деструкторов есть критическое поведение внутри.

    После такой опасности я перестал полагаться на неявные функции отложенной вставки STL и всегда использую явные проверки, имеют ли мои объекты поведение в своих ctors / dtors.

    См. Этот вопрос: Деструктор вызвал объект при добавлении это в std :: list

    19.09.2019
    Новые материалы

    Аргументы прогрессивного улучшения почти всегда упускают суть
    В наши дни в кругах веб-разработчиков много болтают о Progressive Enhancement — PE, но на самом деле почти все аргументы с обеих сторон упускают самую фундаментальную причину, по которой PE..

    Введение в Джанго Фреймворк
    Схема «работать умно, а не усердно» В этой и последующих статьях я познакомлю вас с тем, что такое фреймворк Django и как создать свое первое приложение с помощью простых и понятных шагов, а..

    Настольный ПК как «одно кольцо, чтобы править всеми» домашних компьютеров
    Вид после 9 месяцев использования С настольных компьютеров все началось, но в какой-то момент они стали «серверами», и мы все перешли на ноутбуки. В прошлом году я столкнулся с идеей настольных..

    Расширенные методы безопасности для VueJS: реализация аутентификации без пароля
    Руководство, которое поможет вам создавать безопасные приложения в долгосрочной перспективе Безопасность приложений часто упускается из виду в процессе разработки, потому что основная..

    стройный-i18следующий
    Представляем стройную оболочку для i18next. Эта библиотека, основанная на i18next, заключает экземпляр i18next в хранилище svelte и отслеживает события i18next, такие как languageChanged,..

    Обзор 20 основных и современных методов работы с массивами в JavaScript
    Вы знаете их всех? В этом коротком посте я покажу сводку методов, доступных в JavaScript для работы с массивами. Я надеюсь, что вы найдете это полезным! В конце поста вы найдете ссылку на..

    Да, но я чувствую необходимость указать, что это или не единственные два.
    Да, но я чувствую необходимость указать, что это или не единственные два. Обучение с подкреплением (в качестве примера) также является важным.