编写自定义的,高度专业的,专用的,符合标准的C ++分配器

时间:2019-03-20 00:17:00

标签: c++ memory-management allocator

简要序言

我认识到,与标准兼容的分配器有许多细微差别和要求。这里有很多问题,涉及与分配器相关的一系列主题。我意识到该标准提出的要求对于确保分配器在所有情况下均能正常运行,不会泄漏内存,不会导致 undefined-behaviour 等问题至关重要。这尤其正确分配器旨在(或至少可以使用)在各种用例中使用,具有各种底层类型和不同的标准容器,对象大小等。

相比之下,我有一个非常特殊的用例,其中我个人严格控制了与使用相关的所有条件,如下所述。因此,鉴于我要实施的工作具有高度特定性,我相信我所做的工作完全可以接受。

我希望比我有更多经验和了解的人可以确认以下描述是可以接受的,也可以指出问题(并且最好是也可以解决这些问题)。

概述/特定要求

简而言之,我试图编写一个分配器,该分配器将在我自己的代码中使用,并用于单个特定目的:

  • 我需要“几个” std::vector(可能是uint16_t),并具有固定数量(在运行时)的元素。我正在进行基准测试,以确定确切整数类型的性能/空间的最佳折衷方案[1]
  • 如前所述,元素的数量始终是相同的,但这取决于传递给应用程序的某些运行时配置数据
  • 向量的数量也是固定的或至少是有界的。确切的数字由提供parallel::for(execution::par_unseq, ...)
  • 实现的库处理
  • 向量是由我构造的(即,我可以肯定地知道它们将始终由N个元素构成)

[1]向量的值用于将浮点数从2个向量之一复制到目标:c[i] = rand_vec[i] < threshold ? a[i] : b[i],其中a, b, cfloat,{{ 1}}是我要在此处找出的rand_vec,而std::vector是类型为threshold的单个变量。该代码作为SSE ​​SIMD操作进行编译。我不记得它的细节,但是我相信,如果int小于float,则需要附加的移位指令。

在此基础上,我编写了一个非常简单的分配器,将单个静态boost :: lockfree :: queue作为空闲列表。鉴于我将自己构造向量,并且在完成向量后将超出范围,因此我可以肯定地知道,对alloc :: deallocate(T *,size_t)的所有调用将始终返回相同大小的向量,因此,我相信我可以将它们重新推入队列,而不必担心将大小不同的分配推入空闲列表的指针。

正如下面的代码中所指出的,我现在已经在运行时测试中添加了分配和释放函数,同时我一直在为自己确认这些情况不会发生,也不会发生。同样,我相信删除这些运行时测试无疑是安全的。尽管在这里也可以得到一些建议-考虑到周围的代码,但我认为分支预测程序应适当地处理它们,这样它们就不会产生可观的运行时成本(尽管没有进行检测,很难确定100%肯定)。

简而言之,据我所知,这里的一切完全在我的控制范围内,行为完全确定,因此绝对安全。在典型条件下运行代码时也建议这样做-没有段错误等。我还没有尝试过使用消毒剂运行-我希望在这样做之前能得到一些反馈和指导。

我应该指出,与使用integer_tbd相比,我的代码运行速度快了至少两倍,这在质量上是可以预期的。

CR_Vector_Allocator.hpp

std::allocator

CR_Vector_Allocator.cc

class CR_Vector_Allocator {

  using T = CR_Range_t; // probably uint16_t or uint32_t, set elsewhere.

private:
  using free_list_type = boost::lockfree::queue>;

  static free_list_type free_list;

public:
  T* allocate(size_t);
  void deallocate(T* p, size_t) noexcept;

  using value_type = T;
  using pointer = T*;
  using reference = T&;

  template  struct rebind { using other = CR_Vector_Allocator;};
};

它以以下方式使用:

CR_Vector_Allocator::T* CR_Vector_Allocator::allocate(size_t n) {

  if (n <= 1)
    throw std::runtime_error("Unexpected number of elements to initialize: " +
                         std::to_string(n));

  T* addr_;
  if (free_list.pop(addr_)) return addr_;

  addr_ = reinterpret_cast<T*>(std::malloc(n * sizeof(T)));
  return addr_;
}

void CR_Vector_Allocator::deallocate(T* p, size_t n) noexcept {
  if (n <= 1) // should never happen. but just in case, I don't want to leak
    free(p);
  else
    free_list.push(p);
}

CR_Vector_Allocator::free_list_type CR_Vector_Allocator::free_list;

任何反馈,指导或斥责将不胜感激。
谢谢!!

0 个答案:

没有答案