Эффективность функции копирования STL

Я пытаюсь построить набор уникальных слов из списка записей, каждый из которых имеет вектор строк.

поэтому я сделал функцию Insert, которая вызывается для каждой из записей следующим образом:

for( auto & e : _Entries )
    _Dictionary.Insert( begin( e.getNameWords( ) ), end( e.getNameWords( ) ) );

класс _Dictionary внутренне имеет набор (контейнер STL), и я написал функцию Insert следующим образом:

template< typename InputIterator >
void Insert( InputIterator first, InputIterator last )
{
    for( auto it = first ; it != last ; ++it )
        _AllWords.insert( *it );
 }

в моем случае вызов Insert для всех записей в _Entries занял в среднем 570 миллисекунды.

тогда я подумал, что я должен использовать функции, которые STL уже должен делать то же самое, что и цикл for in Insert, поэтому я изменил функцию Insert на следующее:

template< typename InputIterator >
void Insert( InputIterator first, InputIterator last )
{
    copy( first, last, inserter( _AllWords, begin( _AllWords ) ) );

}

Я ожидал, что это

  1. быть более правильным, и
  2. будьте, по крайней мере, так же быстро, если не больше

(руководствуясь философией позволяя STL сделать для вас столько, сколько вы можете). Тем не менее, я был удивлен обратите внимание, что эта реализация на самом деле заняла больше времени; не намного больше, но последовательное 200 миллисекунд больше, чем предыдущая реализация на основе цикла.

Я знаю, что это по существу тривиальная разница в скорости, но я все еще удивлен.

Итак, мой вопрос: почему моя реализация быстрее?

Примечание: я компилирую это с версией 3.5.2 clang со стандартной библиотекой libc++ и с флагом-O3 под Ubuntu 14.04.

1 ответов


проблема такая:

copy( first, last, inserter( _AllWords, begin( _AllWords ) ) );

заканчивается вызовом этой версии insert:

iterator insert( iterator hint, const value_type& value );

С begin() как намек. То есть, как правило, не там, где вы хотите вставить каждое значение. В результате вы просто заставляете контейнер делать больше работы, пытаясь выяснить, где добавить свои значения, так как ваш hint is как можно хуже.

но обратите внимание, что существует также эта перегрузка insert:

template< class InputIt >
void insert( InputIt first, InputIt last );

что вы должны просто использовать:

template< typename InputIterator >
void Insert( InputIterator first, InputIterator last )
{
    _AllWords.insert(first, last);
}

и к слову, _AllWords является зарезервированным идентификатором.


хотя на основе этого Примечание:

перегрузки (5-6) часто реализуются как цикл, который вызывает перегрузку (3) с помощью end() как подсказка; они оптимизированы для добавления отсортированной последовательности (например, другого набора), наименьший элемент которой больше, чем последний элемент *this

это похоже на действительно конкретную цель для оптимизации, которую вы можете или не можете удовлетворить, поэтому, вероятно, вы не должны использовать эту перегрузку, и ваш начальный цикл в порядке.