C++ vector 避免 fill 0

我们在profiler的时候有的时候发现memset占用热点比较高。而且是std::vector::resize 带来的。这个明显是没必要的, 例如:

std::vector<int> result;
// 这里resize会 fill 0
result.resize(input_rows);

for(int i = 0;i < input_rows; ++i) {
  result[i] = transfer(input[i]);
}

如何消除这个呢:
allocator利用 new T;

#include <memory>
#include <array>
#include <new> // for std::construct_at

// 假设我们有一个元素类型为 T,大小为 N 的 std::array
template<typename T, std::size_t N>
std::unique_ptr<std::array<T, N>> make_array_for_overwrite() {
    // 分配未初始化内存
    auto raw_ptr = new T[N];

    // 使用 std::unique_ptr 管理内存,避免内存泄漏
    auto deleter = [](T* ptr) {
        delete[] ptr;
    };
    std::unique_ptr<std::array<T, N>, decltype(deleter)> array_ptr(reinterpret_cast<std::array<T, N>*>(raw_ptr), deleter);

    return array_ptr;
}

// 使用示例
int main() {
    auto my_array_ptr = make_array_for_overwrite<int, 10>();
    // 现在 my_array_ptr 指向一个未初始化的 std::array<int, 10>
    // 在使用前必须逐个元素进行初始化
    for (int& element : *my_array_ptr) {
        std::construct_at(&element);
    }
    // 使用 my_array_ptr...
}

或者可以利用自定义allocator

template <typename T, size_t trailing = 16, typename A = std::allocator<T>>
class RawAllocator : public A {
    static_assert(std::is_trivially_destructible_v<T>, "not trivially destructible type");
    typedef std::allocator_traits<A> a_t;

public:
    template <typename U>
    struct rebind {
        using other = RawAllocator<U, trailing, typename a_t::template rebind_alloc<U>>;
    };

    using A::A;

    // allocate more than caller required
    T* allocate(size_t n) {
        T* x = A::allocate(n + RawAllocator::_trailing);
        return x;
    }

    // deallocate the storage referenced by the pointer p
    void deallocate(T* p, size_t n) { A::deallocate(p, (n + RawAllocator::_trailing)); }

    // do not initialized allocated.
    // 注意这里是 new (placed) U 而不是 new (placed) U();
    template <typename U>
    void construct(U* ptr) noexcept(std::is_nothrow_default_constructible<U>::value) {
        ::new (static_cast<void*>(ptr)) U;
    }
    template <typename U, typename... Args>
    void construct(U* ptr, Args&&... args) {
        a_t::construct(static_cast<A&>(*this), ptr, std::forward<Args>(args)...);
    }

private:
    static const size_t _trailing = trailing;
};
posted @ 2024-05-31 15:51  stdpain  阅读(18)  评论(0编辑  收藏  举报