是否有可变长度数组的标准C ++模板类,具有最大大小以获得良好的缓存局部性?

时间:2018-03-30 16:17:49

标签: c++ arrays templates boost stl

我正在开发一个缓存位置为王的软件。它在具有#define MAX_ARRAY_1_SIZE 1000类定义的复杂数据结构中使用大约10个不同的C数组。此示例1数组中的元素数可以在0-1000之间。结构数组中有结构数组。使用的内存量约为163兆字节。

但是,我注意到C qsort很慢,所以我切换到C++,由于内联比较器函数调用,std::sort速度更快std::vector。模板元编程的奇迹。

现在我想知道我是否应该使用C ++更高级的功能。我在大约10个数组中的两个中尝试了std::vector,但发现它杀死了我的缓存局部性,当只有大约10个阵列中的两个被std::array替换时,性能降低了15%。是的,我确实发现这是由于缓存局部性:在关键代码路径期间,数组大小保持不变,并且仅在程序启动时填充。

然后我试着看看是否有内容存储元素,而不是单独的动态分配块。我找到了std::array。但是,std::sort是一个固定大小的数组,对C数组的好处是值得怀疑的。

我正在寻找一个具有最大大小和内联元素存储的可变大小的数组。标准C ++中有这样的东西吗?还是在Boost?

如果可变大小的数组实际上可以具有无限的最大大小,那么它就没有坏处,因此如果数据适合那么它将数据存储在内联数组中,并且如果数据没有,则转向使用分配的数组。 t适合在线阵列。

现在,一位经验丰富的C ++程序员可以在几个小时内编写这样的模板类,并且由于在大约10年前使用C ++而在几天之内我可以做同样的事情,然后才能获得struct baz { int a; int b; }; struct bar { struct baz baz[100]; }; struct foo { struct bar bar[100]; }; 性能现在增加。但我不想重新发明轮子,所以我正在寻找现有的解决方案。

修改

为了清楚明白,让我举个例子:

struct foo

现在struct baz { int a; int b; }; struct bar { std::vector<struct baz> baz; }; struct foo { std::vector<struct bar> bar; }; 是一个连续的内存块。

def bisec_left(lst, item):
    low, high = 0, len(lst)

    while low < high:
        mid = (low + high) // 2
        mid_val = lst[mid]

        if item > mid_val:
            low = mid + 1
        else:
            high = mid
    return low


def bisec_2d(lsts, x):
    meta = [lst[-1] for lst in lsts]
    i1 = bisec_left(meta, x)

    if i1 != len(meta):
        lst = lsts[i1]
        i2 = bisec_left(lst, x)
        if i2 != len(lst) and lst[i2] == x:
            return i1, i2
    raise ValueError


if __name__ == '__main__':
    v = 9
    lsts = [[1, 4, 5], [7, 8, 9, 10], [11, 12, 14, 15]]
    print("{} => {}".format(v, bisec_2d(lsts, v)))

......现在不是。

2 个答案:

答案 0 :(得分:1)

那里有许多小型矢量类。 Boost有一些。试试http://www.boost.org/doc/libs/master/doc/html/container/non_standard_containers.html#container.non_standard_containers.small_vector

一个只有一点开销的简单的手动滚动的solition将是附加到vector和std数组变体的gsl span。几个字节的最佳解决方案存在开销。使用未知容器上的跨度进行交互。

答案 1 :(得分:0)

我决定实施自己的:

template<class C, class sz_t, sz_t maxsz> class inlinearray {
  private:
    typedef C value_type;
    typedef value_type *pointer;
    typedef const value_type *const_pointer;
    typedef value_type &reference;
    typedef const value_type &const_reference;
    typedef value_type *iterator;
    typedef const value_type *const_iterator;
    typedef sz_t size_type;
    typedef std::ptrdiff_t difference_type;
    typedef std::reverse_iterator<iterator> reverse_iterator;
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
    sz_t sz;
    union {
      C realarray[maxsz]; // for correct alignment
      char array[maxsz*sizeof(C)];
    };
  public:
    inlinearray()
    {
      sz = 0;
    }
    ~inlinearray(void)
    {
      clear();
    }
    void clear(void)
    {
      sz_t i;
      for (i = 0; i < sz; i++)
      {
        data()[i].~C();
      }
      sz = 0;
    }
    template<class sz2_t, sz2_t maxsz2> inlinearray(inlinearray<C,sz2_t,maxsz2> that)
    {
      size_t i;
      sz = that.sz;
      for (i = 0; i < sz; i++)
      {
        push_back(that[i]);
      }
    }
    template<class sz2_t, sz2_t maxsz2> void operator=(inlinearray<C,sz2_t, maxsz2> val2)
    {
      swap(val2);
    }
    void fill(const C& val)
    {
      std::fill_n(begin(), size(), val);
    }
    C &operator[](sz_t i) noexcept
    {
      return data()[i];
    }
    constexpr const C &operator[](sz_t i) const noexcept
    {
      return data()[i];
    }
    C at(sz_t i)
    {
      if (i >= sz)
      {
        throw std::out_of_range("inlinerray::at() out of range");
      }
      return data()[i];
    }
    constexpr const C at(sz_t i) const
    {
      if (i >= sz)
      {
        throw std::out_of_range("inlinerray::at() out of range");
      }
      return data()[i];
    }
    void push_back(const C &c)
    {
      if (sz >= maxsz)
      {
        abort();
      }
      new (data()+sz) C(c);
      sz++;
    }
    void pop_back() noexcept
    {
      data()[sz-1].~C();
      sz--;
    }
    template <class sz2_t, sz2_t maxsz2> void swap(inlinearray<C, sz2_t, maxsz2> &that)
    {
      if (that.sz > maxsz)
      {
        abort();
      }
      if (sz > that.maxsz)
      {
        abort();
      }
      std::swap_ranges(begin(), end(), that.begin());
      std::swap(sz, that.sz);
    }
    constexpr sz_t size(void) const noexcept { return sz; }
    constexpr sz_t max_size(void) const noexcept { return maxsz; }
    constexpr bool empty() const noexcept { return sz == 0; }
    C *begin() noexcept { return data(); }
    C &front() noexcept { return data()[0]; }
    C &back() noexcept { return sz == 0 ? data()[0] : data()[sz - 1]; }
    constexpr const C &back() const noexcept { return sz == 0 ? data()[0] : data()[sz - 1]; }
    C *end() noexcept { return data() + sz; }
    C* data() noexcept { return reinterpret_cast<C*>(array); }
    const C* data() const noexcept { return reinterpret_cast<const C*>(array); }
    const C *begin() const noexcept { return data(); }
    const C *end() const noexcept { return data() + sz; }
    const C *cbegin() const noexcept { return data(); }
    const C *cend() const noexcept { return data() + sz; }
    reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
    reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
    const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); }
    const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); }
    const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); }
    const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); }
};

代码的使用应该很简单。