я написал класс, чтобы действовать в качестве обертки вокруг последовательного контейнера (std::vector
/std::queue
/std::list
), чтобы иметь интерфейс с std::map
, для при использовании небольшого количества небольших объектов. Кодирование было чрезвычайно простым с учетом уже существующих алгоритмов. Этот код, очевидно, высокий урезанный из моего полного кода, но показывает проблему.станд :: LOWER_BOUND медленнее станд :: вектор, чем станд :: карта :: найти
template <class key_,
class mapped_,
class traits_ = std::less<key_>,
class undertype_ = std::vector<std::pair<key_,mapped_> >
>
class associative
{
public:
typedef traits_ key_compare;
typedef key_ key_type;
typedef mapped_ mapped_type;
typedef std::pair<const key_type, mapped_type> value_type;
typedef typename undertype_::allocator_type allocator_type;
typedef typename allocator_type::template rebind<value_type>::other value_allocator_type;
typedef typename undertype_::const_iterator const_iterator;
class value_compare {
key_compare pred_;
public:
inline value_compare(key_compare pred=key_compare()) : pred_(pred) {}
inline bool operator()(const value_type& left, const value_type& right) const {return pred_(left.first,right.first);}
inline bool operator()(const value_type& left, const key_type& right) const {return pred_(left.first,right);}
inline bool operator()(const key_type& left, const value_type& right) const {return pred_(left,right.first);}
inline bool operator()(const key_type& left, const key_type& right) const {return pred_(left,right);}
inline key_compare key_comp() const {return pred_;}
};
class iterator {
public:
typedef typename value_allocator_type::difference_type difference_type;
typedef typename value_allocator_type::value_type value_type;
typedef typename value_allocator_type::reference reference;
typedef typename value_allocator_type::pointer pointer;
typedef std::bidirectional_iterator_tag iterator_category;
inline iterator(const typename undertype_::iterator& rhs) : data(rhs) {}
inline reference operator*() const { return reinterpret_cast<reference>(*data);}
inline pointer operator->() const {return reinterpret_cast<pointer>(structure_dereference_operator(data));}
operator const_iterator&() const {return data;}
protected:
typename undertype_::iterator data;
};
template<class input_iterator>
inline associative(input_iterator first, input_iterator last) : internal_(first, last), comp_()
{if (std::is_sorted(internal_.begin(), internal_.end())==false) std::sort(internal_.begin(), internal_.end(), comp_);}
inline iterator find(const key_type& key) {
iterator i = std::lower_bound(internal_.begin(), internal_.end(), key, comp_);
return (comp_(key,*i) ? internal_.end() : i);
}
protected:
undertype_ internal_;
value_compare comp_;
};
SSCCE в http://ideone.com/Ufn7r, полный код на http://ideone.com/MQr0Z (примечание: в результате раз IdeOne весьма беспорядочным, вероятно, из-за нагрузки на сервер, и не ясно показывают результаты в вопросе)
Я протестированные с std::string
, и POD от 4 до 128 байтов, от 8 до 2000 элементов с MSVC10.
Я ожидал более высокой производительности для (1) создания из диапазона для небольших объектов, (2) случайного ввода/стирания для небольшого числа мелких объектов и (3) поиска для всех объектов. Удивительно, но вектор был значительно быстрее для создания из диапазона для всех тестов и быстрее для случайного стирания в зависимости от размера до примерно 2048 байт (512 4-байтовых объектов или 128 16-байтовых объектов и т. Д.). Однако самым шокирующим было то, что std::vector
с использованием std::lower_bound
был медленнее, чем std::map::find
для всех POD. Разница была минимальной для 4 и 8-байтовых POD, но для 128-байтовых POD std::vector
был на 36% медленнее! Однако, для std::string
, std::vector
был в среднем на 6% быстрее.
Я чувствую std::lower_bound
на отсортированный std::vector
должен обогнал std::map
из-за лучший кэш локальности/меньший объем памяти, а так как map
может быть несовершенным сбалансирован, или в худшем случае он должен матчstd::map
, но не может для жизни меня думать о какой-либо причине, что std::map
должен быть быстрее. Моя единственная мысль - предикат как-то замедляет его, но я не могу понять, как это сделать. Так что вопрос: Как могло случиться так, что std::lower_bound
на сортированном std::vector
превзойдет std::map
(в MSVC10)?
[EDIT] Я подтвердил, что std::lower_bound
на std::vector<std::pair<4BYTEPOD,4BYTEPOD>>
использует меньше сравнений чем в среднем std::map<4BYTEPOD,4BYTEPOD>::find
(по 0-0,25), но моя реализация еще до 26% медленнее.
[ПОСТ-ОТВЕТ-EDIT] Я сделал SSCCE в http://ideone.com/41iKt, который удаляет весь ненужный пух, и ясно показывает, что find
на отсортированном vector
медленнее, чем у map
, на ~ 15%.
Я заметил, что он возвращал неправильное значение для ключа, который не найден после того, как я разместил вопрос, но это не относится к проблеме. –
Обратите внимание, что вы изобретаете колесо здесь - Boost содержит эту точную функциональность в [Boost.Container] (http://www.boost.org/libs/container/), а именно: boost: container :: flat_map < > '. По крайней мере, вы можете сравнить свою реализацию с этим, чтобы проверить любые заметные отличия. – ildjarn
@ildjarn: когда-нибудь я научусь повышать. Я сравню, хотя в этом я уверен, что это предикат. –