![](/img/trans.png)
[英]Why are std::allocator::construct and std::allocator::destroy templated on element type?
[英]Custom STL Allocator: Construct/Destroy called for each element
我有这个代码片段
auto start = high_resolution_clock::now();
std::vector<char> myBuffer(20e6);
std::cout << "StandardAlloc Time:" << duration_cast<milliseconds>(high_resolution_clock::now() - start).count() << std::endl;
start = high_resolution_clock::now();
std::vector<char, HeapAllocator<char>>myCustomBuffer(20e6);
std::cout << "CustomAlloc Time:" << duration_cast<milliseconds>(high_resolution_clock::now() - start).count() << " CC: " << HeapAllocator<char>::constructCount << std::endl;
Output:
StandardAlloc Time:6
CustomAlloc Time:124 CC: 20000000
有了这个分配器
template<class T>
struct HeapAllocator
{
typedef T value_type;
HeapAllocator(){};
template<class U>
constexpr HeapAllocator(const HeapAllocator<U>&) noexcept {}
[[nodiscard]] T* allocate(std::size_t n)
{
auto p = new T[n];
return p;
}
void deallocate(T* p, std::size_t n) noexcept
{
delete p;
}
template <class U>
void destroy(U* p)
{
destroyCount++;
}
template< class U, class... Args >
void construct(U* p, Args&&... args)
{
constructCount++;
}
static int destroyCount;
static int constructCount;
};
template<class T>
int HeapAllocator<T>::constructCount = 0;
因此很明显,与默认分配器相比,缓冲区的每个 char 元素都会调用构造/销毁,这导致执行时间增加了 20 倍。 我怎样才能防止这种基本类型的这种行为?
您根本不声明它们。 它们将通过std::allocator_traits
默认使用std::construct_at
/ std::destroy_at
(即新布局和(伪)析构函数调用),就像std::allocator
一样。 这适用于 C++11。
这些函数只需要那么多时间,因为您正在修改其中的 static。 此外,您的实施没有执行他们需要做的工作。 construct
,如果你声明它,必须通过一种或另一种方式在 memory 中构造一个T
类型的 object 。 并且destroy
必须调用析构函数,至少对于没有平凡析构函数的类型。
也就是说, std::vector<char> myBuffer(20e6);
和std::vector<char, HeapAllocator<char>>myCustomBuffer(20e6);
仍然需要一些时间,因为它们将分配的 memory 归零。 std::vector
不提供任何接口使其处于未初始化状态,并且在一定程度上依赖编译器识别std::construct_at
循环可以优化为memset
。 (对于std::allocator
,标准库在看到使用std::allocator
时可以直接专门化为memset
。)
但是, std::vector
提供了.reserve
,它将通过allocate
保留足够的 memory 而无需在其中构造任何对象。 然后可以根据需要使用push_back
/ emplace_back
/ resize
创建对象。
您的分配器还有其他一些问题:
auto p = new T[n];
是错的。 allocate
应该分配 memory,而不是构造对象。 在对乘法进行溢出检查后,您应该只分配适当大小的 memory 和 alignment,例如使用::operator new[](n*sizeof(T), std::align_val_t{alignof(T)})
。 类似地, deallocate
应该使用匹配的 deallocation function(例如::operator delete[]
)。 除此之外, delete
和new[]
也与未定义的行为不匹配。 在new[]
之后你必须使用delete[]
,而不是delete
。
您的 class 缺少operator==
(和operator!=
在 C++20 之前)。 它们是必需的,如果您的 class 没有任何非静态数据成员并且没有定义is_always_equal
成员,则operator==
必须始终返回true
并且operator!=
始终返回false
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.