benchmark silo added
[c11concurrency-benchmarks.git] / silo / static_unordered_map.h
diff --git a/silo/static_unordered_map.h b/silo/static_unordered_map.h
new file mode 100644 (file)
index 0000000..e3919c7
--- /dev/null
@@ -0,0 +1,345 @@
+#ifndef _STATIC_UNORDERED_MAP_H_
+#define _STATIC_UNORDERED_MAP_H_
+
+#include "small_unordered_map.h"
+
+/**
+ * XXX(stephentu): allow custom allocator
+ */
+template <typename Key,
+          typename T,
+          size_t StaticSize = SMALL_SIZE_MAP,
+          typename Hash = private_::myhash<Key>>
+class static_unordered_map {
+public:
+  typedef Key key_type;
+  typedef T mapped_type;
+  typedef std::pair<const key_type, mapped_type> value_type;
+  typedef Hash hasher;
+  typedef T & reference;
+  typedef const T & const_reference;
+
+private:
+  typedef std::unordered_map<Key, T, Hash> large_table_type;
+  typedef std::pair<key_type, mapped_type> bucket_value_type;
+
+  static const bool is_trivially_destructible =
+    private_::is_trivially_destructible<bucket_value_type>::value;
+
+  static const size_t TableSize = private_::TableSize(StaticSize);
+  static_assert(StaticSize >= 1, "XXX");
+  static_assert(TableSize >= 1, "XXX");
+
+  struct bucket {
+    inline ALWAYS_INLINE bucket_value_type *
+    ptr()
+    {
+      return reinterpret_cast<bucket_value_type *>(&buf[0]);
+    }
+
+    inline ALWAYS_INLINE const bucket_value_type *
+    ptr() const
+    {
+      return reinterpret_cast<const bucket_value_type *>(&buf[0]);
+    }
+
+    inline ALWAYS_INLINE bucket_value_type &
+    ref()
+    {
+      return *ptr();
+    }
+
+    inline ALWAYS_INLINE const bucket_value_type &
+    ref() const
+    {
+      return *ptr();
+    }
+
+    template <class... Args>
+    inline ALWAYS_INLINE void
+    construct(size_t hash, Args &&... args)
+    {
+      h = hash;
+      new (&ref()) bucket_value_type(std::forward<Args>(args)...);
+    }
+
+    inline ALWAYS_INLINE void
+    destroy()
+    {
+      if (!is_trivially_destructible)
+        ref().~bucket_value_type();
+    }
+
+    struct bucket *bnext;
+    size_t h;
+    char buf[sizeof(value_type)];
+  };
+
+  // iterators are not stable across mutation
+  template <typename BucketType, typename ValueType>
+  class iterator_ : public std::iterator<std::forward_iterator_tag, ValueType> {
+    friend class static_unordered_map;
+  public:
+    inline iterator_() : b(0) {}
+
+    template <typename B, typename V>
+    inline iterator_(const iterator_<B, V> &other)
+      : b(other.b)
+    {}
+
+    inline ValueType &
+    operator*() const
+    {
+      return reinterpret_cast<ValueType &>(b->ref());
+    }
+
+    inline ValueType *
+    operator->() const
+    {
+      return reinterpret_cast<ValueType *>(b->ptr());
+    }
+
+    inline bool
+    operator==(const iterator_ &o) const
+    {
+      return b == o.b;
+    }
+
+    inline bool
+    operator!=(const iterator_ &o) const
+    {
+      return !operator==(o);
+    }
+
+    inline iterator_ &
+    operator++()
+    {
+      b++;
+      return *this;
+    }
+
+    inline iterator_
+    operator++(int)
+    {
+      iterator_ cur = *this;
+      ++(*this);
+      return cur;
+    }
+
+  protected:
+    inline iterator_(BucketType *b)
+      : b(b)
+    {
+    }
+
+  private:
+    BucketType *b;
+  };
+
+  static size_t
+  chain_length(bucket *b)
+  {
+    size_t ret = 0;
+    while (b) {
+      ret++;
+      b = b->bnext;
+    }
+    return ret;
+  }
+
+public:
+
+  typedef iterator_<bucket, value_type> iterator;
+  typedef iterator_<const bucket, const value_type> const_iterator;
+
+  static_unordered_map()
+    : n(0)
+  {
+    NDB_MEMSET(&table[0], 0, sizeof(table));
+  }
+
+  ~static_unordered_map()
+  {
+    for (size_t i = 0; i < n; i++)
+      elems[i].destroy();
+#ifdef ENABLE_EVENT_COUNTERS
+    size_t ml = 0;
+    for (size_t i = 0; i < TableSize; i++) {
+      const size_t l = chain_length(table[i]);
+      ml = std::max(ml, l);
+
+    }
+    if (ml)
+      private_::evt_avg_max_unordered_map_chain_length.offer(ml);
+#endif
+  }
+
+  static_unordered_map(const static_unordered_map &other)
+    : n(0)
+  {
+    NDB_MEMSET(&table[0], 0, sizeof(table));
+    assignFrom(other);
+  }
+
+  static_unordered_map &
+  operator=(const static_unordered_map &other)
+  {
+    // self assignment
+    if (unlikely(this == &other))
+      return *this;
+    assignFrom(other);
+    return *this;
+  }
+
+private:
+  bucket *
+  find_bucket(const key_type &k, size_t *hash_value)
+  {
+    const size_t h = Hash()(k);
+    if (hash_value)
+      *hash_value = h;
+    const size_t i = h % TableSize;
+    bucket *b = table[i];
+    while (b) {
+      const bool check_hash = private_::is_eq_expensive<key_type>::value;
+      if ((!check_hash || b->h == h) && b->ref().first == k)
+        return b;
+      b = b->bnext;
+    }
+    return 0;
+  }
+
+  inline ALWAYS_INLINE const bucket *
+  find_bucket(const key_type &k, size_t *hash_value) const
+  {
+    return const_cast<static_unordered_map *>(this)->find_bucket(k, hash_value);
+  }
+
+public:
+
+  // XXX(stephentu): template away this stuff
+
+  mapped_type &
+  operator[](const key_type &k)
+  {
+    size_t h;
+    bucket *b = find_bucket(k, &h);
+    if (b)
+      return b->ref().second;
+    INVARIANT(n < StaticSize);
+    b = &elems[n++];
+    b->construct(h, k, mapped_type());
+    const size_t i = h % TableSize;
+    b->bnext = table[i];
+    table[i] = b;
+    return b->ref().second;
+  }
+
+  mapped_type &
+  operator[](key_type &&k)
+  {
+    size_t h;
+    bucket *b = find_bucket(k, &h);
+    if (b)
+      return b->ref().second;
+    INVARIANT(n < StaticSize);
+    b = &elems[n++];
+    b->construct(h, std::move(k), mapped_type());
+    const size_t i = h % TableSize;
+    b->bnext = table[i];
+    table[i] = b;
+    return b->ref().second;
+  }
+
+  inline size_t
+  size() const
+  {
+    return n;
+  }
+
+  inline bool
+  empty() const
+  {
+    return !n;
+  }
+
+  iterator
+  begin()
+  {
+    return iterator(&elems[0]);
+  }
+
+  const_iterator
+  begin() const
+  {
+    return const_iterator(&elems[0]);
+  }
+
+  inline iterator
+  end()
+  {
+    return iterator(&elems[n]);
+  }
+
+  inline const_iterator
+  end() const
+  {
+    return const_iterator(&elems[n]);
+  }
+
+  iterator
+  find(const key_type &k)
+  {
+    bucket * const b = find_bucket(k, 0);
+    if (b)
+      return iterator(b);
+    return end();
+  }
+
+  const_iterator
+  find(const key_type &k) const
+  {
+    const bucket * const b = find_bucket(k, 0);
+    if (b)
+      return const_iterator(b);
+    return end();
+  }
+
+  void
+  clear()
+  {
+    if (!n)
+      return;
+    NDB_MEMSET(&table[0], 0, sizeof(table));
+    for (size_t i = 0; i < n; i++)
+      elems[i].destroy();
+    n = 0;
+  }
+
+public:
+  // non-standard API
+  inline bool is_small_type() const { return true; }
+
+private:
+
+  // doesn't check for self assignment
+  inline void
+  assignFrom(const static_unordered_map &that)
+  {
+    clear();
+    for (size_t i = 0; i < that.n; i++) {
+      bucket * const b = &elems[n++];
+      const bucket * const that_b = &that.elems[i];
+      b->construct(that_b->h, that_b->ref().first, that_b->ref().second);
+      const size_t idx = b->h % TableSize;
+      b->bnext = table[idx];
+      table[idx] = b;
+    }
+  }
+
+  size_t n;
+  bucket elems[StaticSize];
+  bucket *table[TableSize];
+};
+
+#endif /* _STATIC_UNORDERED_MAP_H_ */