初始化多线程中的向量

时间:2016-12-04 20:32:54

标签: c++ multithreading c++11 vector

我有一个大的向量std::vector<some_class> some_vector,我需要使用不同的构造函数值some_class(constructor_parameters)进行初始化。正常的做法是:

std::vector<some_class> some_vector
some_vector.reserve(length)

for (...) some_vector.push_back(some_class(constructor_parameters))

但是因为这个向量很大,我想并行执行此操作。有没有办法在向量的q个不同位置拆分向量和push_back,这样每个线程都可以开始初始化向量的不同部分?

我通过拆分/加入矢量阅读了一些答案,并且找不到任何有用的东西。由于我的向量非常大,我必须避免为每个线程创建新向量然后将它们复制到原始向量之类的东西 - 我只能使用一大块内存。

我尝试使用some_vector.at(some_loc) = some_class(constructor_parameters),但这不适用于未初始化的矢量。

我可以将vector初始化为某些转储值,然后使用at将其初始化为正确的值,但效率不高。

所以我的问题 - 如何有效地(在内存消耗和计算时间方面)初始化一个大型向量?

编辑回答评论:

大小 - 容器在程序运行期间不会改变其大小,但在编译时不知道大小。尺寸很大,因为这只是问题的范围 - 我正在进行宇宙学N体模拟,其中粒子/网格单元的数量可以很容易地达到1024 ^ 3甚至更多。

Ctors - 现在他们只是为类成员分配值(3~7个分配)但我计划添加一些计算

会员 - 很容易上手,通常是2 std::vector(3)

为什么载体 - 我最初只使用基本类型数组和new / delete指令。我想使用vector,因为它们具有各种功能,自动内存(de)分配,使用迭代器更容易循环等等。我只是假设它们应该很容易实现到多线程中,具有所有其他良好的属性......

1 个答案:

答案 0 :(得分:0)

我能想到两种解决方案。

一种是使用并发数据结构,例如TBB&#39; concurrent_vector。查看其文件

第二种是编写自定义分配器,其construct成员在没有参数的情况下调用时不会调用默认构造函数。然后分配一次向量,并行地初始化每个元素。需要注意确保这样的construct以后不会给您带来麻烦。如果在构造新元素时可以使用C ++ 11移动语义,那么它将最有效。在这种情况下,你需要在主线程中做的唯一事情是分配内存,这是你几乎无法避免的成本。

这是一个具体的例子,

template <std::size_t n, std::size_t m>
class StirlingMatrix2
{
    public:
    StirlingMatrix2()
    {
        // compute the matrix of Stirling numbers
        // relatively expensive
    }

    double operator()(std::size_t i, std::size_t j) const
    {
        return data_[i * (m + 1) + j];
    }

    private:
    double data_[(n + 1) * (m + 1)];
}; // class StirlingMatrix

template <typename T, bool InvokeConstructor = true>
class Allocator
{
    public:
    // other member functions and types

    template <typename U>
    void construct(U *ptr)
    {
        construct_dispatch(
            ptr, std::integral_constant<bool, InvokeConstructor>());
    }

    template <typename U, typename Arg, typename... Args>
    void constrct(U *ptr, Arg &&arg, Args &&... args)
    {
        std::allocator<T>::construct(
            ptr, std::forward<Arg>(arg), std::forward<Args>(args)...);
    }

    private:
    template <typename U>
    void construct_dispatch(U *ptr, std::true_type)
    {
        std::allocator<T>::construct(ptr);
    }

    template <typename U>
    void construct_dispatch(U *, std::false_type)
    {
    }
}; // class Allocator

// specialization for void type is needed

int main()
{
    constexpr n = 100;
    constexpr m = 100;

    using T = StirlingMatrix2<n, m>;

    // each element will be constructed, very expensive
    std::vector<T> svec1(N);

    // allocate memory only
    std::vector<T, Allocator<T, false>> svec2(N);

    // within each thread
    // invoke inplace new to construct elements
}

为了使其工作,不能有指针成员或类成员不是微不足道的。否则就无法保证异常安全。如果您不必使用容器类,并且通过malloc等手动管理内存,则可以使用类似的技术。