Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Semi-Intrusive unordered associative containers: unordered_set, unordered_multiset

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 17. Boost.Intrusive

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Boost.Intrusive также предлагает хешированные контейнеры, которые могут быть очень полезны для реализации контейнеров быстрого просмотра. Эти контейнеры (unordered_set и unordered_multiset) являются полуинтрузивными контейнерами: им нужна дополнительная память, кроме крючка, хранящегося в value_type. Эта дополнительная память должна быть передана в конструкторе контейнера.

В отличие от неупорядоченных ассоциативных контейнеров C++ TR1 (которые также являются хешированными контейнерами), содержимое этих полуинтрузивных контейнеров не восстанавливается для поддержания коэффициента нагрузки: для этого потребуется управление памятью, а навязчивые контейнеры вообще не реализуют никакого управления памятью. Однако пользователь может запросить явную перетасовку, пропустив новый массив ведра. Это также дает дополнительную гарантию над неупорядоченными ассоциативными контейнерами TR1: при вставке элемента в контейнер не признаются недействительными.

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

Помимо ожидаемых объектов хеш-функции и функции равенства, Boost.Intrusive Конструкторы неупорядоченных ассоциативных контейнеров используют аргумент, определяющий вектор вспомогательного ковша, который будет использоваться контейнером.

Размер накладных расходов для хешированного контейнера является умеренным: 1 указатель на значение плюс массив ведра на контейнер. Размер элемента массива ковша обычно является одним указателем. Для получения хешированного контейнера с хорошей производительностью длина ведра обычно совпадает с количеством элементов, содержащихся в контейнере, поэтому хорошо сбалансированный хешированный контейнер (bucket_count() равен size()) будет иметь эквивалентные накладные расходы в размере двух указателей на элемент.

Пустой, непостоянный размер неупорядоченный_set или неупорядоченный_multiset также имеет размер bucket_count() указателей.

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

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

unordered_set и unordered_multiset используют одни и те же крючки. Это преимущество, поскольку один и тот же тип пользователя может быть вставлен сначала в неупорядоченный_multiset, а затем в неупорядоченный_set без изменения определения класса пользователя.

template <class ...Options>
class unordered_set_base_hook;
template <class ...Options>
class unordered_set_member_hook;

unordered_set_base_hook и unordered_set_member_hook получают те же опции, что и в разделе Как использовать Boost.Intrusive:

  • tag<class Tag> (только для базовых крючков): Этот аргумент служит тегом, поэтому вы можете получить более одного базового крюка. По умолчанию: tag<default_tag>.
  • link_mode<link_mode_type LinkMode>: Политика ссылок. По умолчанию: link_mode<safe_link>.
  • void_pointer<class VoidPointer>: Тип указателя, который должен использоваться внутри крючка и распространяться на контейнер. По умолчанию: void_pointer<void>.

Помимо них, эти крючки предлагают дополнительные варианты:

  • store_hash<bool Enabled>: Эта опция оставляет дополнительное пространство в крючке для хранения хеш-значения объекта после его введения в контейнер. При использовании этой опции неупорядоченный контейнер будет хранить вычисленное значение хэша в крючке, и операциям перетаскивания не нужно будет пересчитывать значение хэша. Этот вариант улучшит производительность неупорядоченных контейнеров при частой перетасовке или медленной операции хеширования. По умолчанию: store_hash<false>.
  • optimize_multikey<bool Enabled>: Эта опция оставляет дополнительное пространство в крючке, которое будет использоваться для группировки равных элементов в неупорядоченных мультисетах, значительно улучшая производительность при вставке в эти контейнеры многих равных значений. По умолчанию: оптимизировать_multikey<ложно>.
template<class T, class ...Options>
class unordered_set;
template<class T, class ...Options>
class unordered_multiset;

Как уже упоминалось, для работы неупорядоченных контейнеров необходим вспомогательный массив. Навязчивые неупорядоченные контейнеры получают этот вспомогательный массив, упакованный в тип bucket_traits (который также может быть настроен с помощью контейнера). Все неупорядоченные контейнеры получают в своих конструкторах объект bucket_traits. Класс по умолчанию bucket_traits инициализируется указателем на массив ведер и его размер:

#include <boost/intrusive/unordered_set.hpp>
using namespace boost::intrusive;
struct MyClass : public unordered_set_base_hook<>
{};
typedef unordered_set<MyClass>::bucket_type     bucket_type;
typedef unordered_set<MyClass>::bucket_traits   bucket_traits;
int main()
{
   bucket_type buckets[100];
   unordered_set<MyClass> uset(bucket_traits(buckets, 100));
   return 0;
}

Каждому хешированному контейнеру нужны его собственные черты ведра, то есть его собственный вектор ведра. Два хешированных контейнера не могут иметь одинаковые элементы bucket_type. Массив ковша должен быть уничтожен после контейнер с его использованием уничтожен, в противном случае результат не определен.

unordered_set и unordered_multiset получают те же опции, что и в разделе Как использовать Boost.Intrusive:

  • base_hookmember_hookHook,T Tvalue_traits: Чтобы указать тип крючка или свойства значения, используемые для настройки контейнера. (Чтобы узнать о ценностных характеристиках, перейдите в раздел Контейнеры с пользовательскими ValueTraits.)
  • constant_time_size<bool Enabled>: Для активации операции постоянного времени size(). По умолчанию: constant_time_size<true>
  • size_type<bool Enabled>: Для указания типа, который будет использоваться для хранения размера контейнера. По умолчанию: size_type<std::size_t>

Также они могут получить дополнительные опции:

  • equal<class Equal>: Функция равенства для объектов, подлежащих вставке в контейнеры. По умолчанию: equalstd::equal_to<>
  • hash<class Hash>: Функция хеширования должна использоваться в контейнере. По умолчанию: hashboost::hash<T>
  • bucket_traits<class BucketTraits>: Тип, который обертывает вектор ведра для использования неупорядоченным контейнером. По умолчанию: тип, инициализированный по адресу и размеру массива ковша, и сохраняет обе переменные внутри.
  • power_2_buckets<bool Enabled>: Пользователь гарантирует, что будут использоваться только ковшовые массивы с мощностью двух длин. Затем контейнер будет использовать маски вместо операций модуля, чтобы получить номер ведра из значения хеширования. Маски быстрее, чем операции по модулю, и для некоторых приложений операции по модулю могут налагать значительные накладные расходы. В режиме отладки будет поднято утверждение, если пользователь предоставляет длину ведра, которая не равна мощности двух. По умолчанию: power_2_buckets<false>.
  • cache_begin<bool Enabled>: Примечание: этот вариант несовместим с auto_unlink hooks. Благодаря своей внутренней структуре нахождение первого элемента неупорядоченного контейнера (начало() операция) амортизируется в постоянное время. Можно ускорить начать() и другие операции, связанные с ним (например, очистить()), если контейнер кэширует внутренне положение первого элемента. Это накладывает накладные расходы одного указателя на размер контейнера. По умолчанию: cache_begin<false>.
  • compare_hash<boolEnabled>: Примечание: этот вариант требует store_hash<> в крючке. Когда функция сравнения является дорогостоящей (например, строки с длинным общим предикатом) иногда (особенно когда коэффициент нагрузки высок или у нас есть много эквивалентных элементов в неупорядоченный_multiset и нет оптимизировать_multikey<> активируется в крючке) функция равенства является проблемой производительности. Два равных значения должны иметь одинаковые хэши, поэтому сравнение хеш-значений двух элементов перед использованием сравнительного функтора может ускорить некоторые реализации.
  • incremental<bool Enabled>: Активирует инкрементное хеширование (также известное как линейное хеширование). Этот вариант подразумевает power_2_buckets<true> и контейнеру потребуется мощность двух ведер. Для получения дополнительной информации о инкрементальном хэшировании см. Linear hash в Википедии По умолчанию: incremental<false>
  • key_of_value<class KeyOfValueFunctionObject>: Объект функции, который будет определять key_type хранимого типа значения. Этот тип позволит использовать картографический интерфейс. См. Карта и многокарточный интерфейс с набором и мультисетом для деталей. По умолчанию: key_type равно value_type (сет-подобный интерфейс).

Теперь рассмотрим небольшой пример с использованием обоих крючков и обоих контейнеров:

#include <boost/intrusive/unordered_set.hpp>
#include <vector>
#include <functional>
#include <boost/functional/hash.hpp>
using namespace boost::intrusive;
class MyClass : public unordered_set_base_hook<>
{               //This is a derivation hook
   int int_;
   public:
   unordered_set_member_hook<> member_hook_; //This is a member hook
   MyClass(int i)
      :  int_(i)
   {}
   friend bool operator== (const MyClass &a, const MyClass &b)
   {  return a.int_ == b.int_;  }
   friend std::size_t hash_value(const MyClass &value)
   {  return std::size_t(value.int_); }
};
//Define an unordered_set that will store MyClass objects using the base hook
typedef unordered_set<MyClass>    BaseSet;
//Define an unordered_multiset that will store MyClass using the member hook
typedef member_hook<MyClass, unordered_set_member_hook<>, &MyClass::member_hook_>
   MemberOption;
typedef unordered_multiset< MyClass, MemberOption>  MemberMultiSet;
int main()
{
   typedef std::vector<MyClass>::iterator VectIt;
   //Create a vector with 100 different MyClass objects
   std::vector<MyClass> values;
   for(int i = 0; i < 100; ++i)  values.push_back(MyClass(i));
   //Create a copy of the vector
   std::vector<MyClass> values2(values);
   //Create a bucket array for base_set
   BaseSet::bucket_type base_buckets[100];
   //Create a bucket array for member_multi_set
   MemberMultiSet::bucket_type member_buckets[200];
   //Create unordered containers taking buckets as arguments
   BaseSet base_set(BaseSet::bucket_traits(base_buckets, 100));
   MemberMultiSet member_multi_set
      (MemberMultiSet::bucket_traits(member_buckets, 200));
   //Now insert values's elements in the unordered_set
   for(VectIt it(values.begin()), itend(values.end()); it != itend; ++it)
      base_set.insert(*it);
   //Now insert values's and values2's elements in the unordered_multiset
   for(VectIt it(values.begin()), itend(values.end()), it2(values2.begin())
      ; it != itend; ++it, ++it2){
      member_multi_set.insert(*it);
      member_multi_set.insert(*it2);
   }
   //Now find every element
   {
      VectIt it(values.begin()), itend(values.end());
      for(; it != itend; ++it){
         //base_set should contain one element for each key
         if(base_set.count(*it) != 1)           return 1;
         //member_multi_set should contain two elements for each key
         if(member_multi_set.count(*it) != 2)   return 1;
      }
   }
   return 0;
}

Вместо использования класса bucket_traits для хранения массива ковша пользователь может определить свой собственный класс для хранения массива ковша с помощью опции bucket_traits<>. Пользовательские характеристики ковша должны соответствовать следующему интерфейсу:

class my_bucket_traits
{
   bucket_ptr        bucket_begin();
   const_bucket_ptr  bucket_begin() const;
   std::size_t bucket_count() const;
};

Следующие признаки ковша просто хранят указатель на массив ковша, но размер является постоянной времени компиляции. Обратите внимание на использование вспомогательных утилит unordered_bucket и unordered_bucket_ptr для получения типа ведра и его указателя перед определением неупорядоченного контейнера:

#include <boost/intrusive/unordered_set.hpp>
#include <boost/functional/hash.hpp>
#include <vector>
using namespace boost::intrusive;
//A class to be inserted in an unordered_set
class MyClass : public unordered_set_base_hook<>
{
   int int_;
   public:
   MyClass(int i = 0) : int_(i)
   {}
   friend bool operator==(const MyClass &l, const MyClass &r)
      {  return l.int_ == r.int_;   }
   friend std::size_t hash_value(const MyClass &v)
      {  return boost::hash_value(v.int_); }
};
//Define the base hook option
typedef base_hook< unordered_set_base_hook<> >     BaseHookOption;
//Obtain the types of the bucket and the bucket pointer
typedef unordered_bucket<BaseHookOption>::type     BucketType;
typedef unordered_bucket_ptr<BaseHookOption>::type BucketPtr;
//The custom bucket traits.
class custom_bucket_traits
{
   public:
   static const int NumBuckets = 100;
   custom_bucket_traits(BucketPtr buckets)
      :  buckets_(buckets)
   {}
   //Functions to be implemented by custom bucket traits
   BucketPtr   bucket_begin() const {  return buckets_;  }
   std::size_t bucket_count() const {  return NumBuckets;}
   private:
   BucketPtr buckets_;
};
//Define the container using the custom bucket traits
typedef unordered_set<MyClass, bucket_traits<custom_bucket_traits> > BucketTraitsUset;
int main()
{
   typedef std::vector<MyClass>::iterator VectIt;
   std::vector<MyClass> values;
   //Fill values
   for(int i = 0; i < 100; ++i)  values.push_back(MyClass(i));
   //Now create the bucket array and the custom bucket traits object
   BucketType buckets[custom_bucket_traits::NumBuckets];
   custom_bucket_traits btraits(buckets);
   //Now create the unordered set
   BucketTraitsUset uset(btraits);
   //Insert the values in the unordered set
   for(VectIt it(values.begin()), itend(values.end()); it != itend; ++it)
      uset.insert(*it);
   return 0;
}

PrevUpHomeNext

Статья Semi-Intrusive unordered associative containers: unordered_set, unordered_multiset раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 17. Boost.Intrusive может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: Chapter 17. Boost.Intrusive ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 16:50:44/0.032814979553223/1