在omp循环中填充已知大小的矩阵。条目大小未知

时间:2017-03-07 09:41:04

标签: c++ multithreading matrix memory-management openmp

我需要填充一个矩阵(std::vector< std::vector<T> >),其行和列的数量已知且已使用vector::resize( ... )函数设置。

每个矩阵元素的对象是class T的对象,它们应该具有std::set<int>作为成员。没有办法提前知道这些套装的大小。

为了填充矩阵,我的目标是使用如下的循环,其中m,n是已知数字:

std::vector< T > innerVector;
innerVector.resize( n, T() );

std::vector< std::vector< T > > myMatrix;
myMatrix.resize( m, innerVector );

#pragma omp parallel for
for( size_t i = 0; i < myMatrix.size(); ++i )
{
    for( size_t j = 0; j < myMatrix.at(0).size(); ++j )
    {
        fillMatrix( myMatrix, i, j);
    }
}

fillMatrix( ... )函数仅使用已知信息来构建每个矩阵元素中包含的数字集,因此不存在数据依赖性。

像这样填充矩阵我们不会遇到由多个线程一次访问矩阵元素引起的竞争条件。我的问题是,在不使用fillMatrix( ... )环境的情况下调用omp critical函数是否安全。

关键是我不知道vector::resize( ... )函数的工作原理。不知怎的,它为myMatrix分配了一些内存,但由于class T元素的大小未知,我可以想象遇到这样的情况,其中最初为矩阵元素分配的内存不够。那么会发生什么?是否有可能多个线程(即填充不同矩阵条目的线程)尝试使用相同的地址扩展分配的内存?

1 个答案:

答案 0 :(得分:1)

许多复杂对象(例如std::vectorstd::set)由对象本身的内存(即sizeof(std::vector)字节)和其后的附加动态分配内存组成。对于您的示例,set元素通常存储在树的动态分配节点中。因此,如果执行std::vector<T>::resize(n),向量将确保它自己动态分配的内存可以包含n类型T的对象,每个对象都包含一个集合。它将 default-construct 使用这些元素当你稍后将元素添加到T内的集合时,将为集合的树节点分配新的内存块。

你描述它的方式,你应该在正确性方面做得很好。但是,我强烈建议您在界面中澄清这一点:

void fillMatrix(T&, size_t, size_t);
...
fillMatrix(myMatrix[i][j], i, j);

甚至:

T fillMatrix(size_t, size_t);
...
myMatrix[i][j] = fillMatrix(i, j);

这样,fillMatrix不会混淆其他线程的数据就更清楚了。

共享内存编程的一般建议是在稍后可能与该内存一起使用的线程上分配和初始化内存。因此,如果您在parallel for上执行更多myMatrix,请考虑以下事项:

std::vector< std::vector< T > > myMatrix;
myMatrix.resize( m );

#pragma omp parallel for
for( size_t i = 0; i < myMatrix.size(); ++i )
{
    myMatrix.at( i ).resize( n ); // Ommiting T() is a bit more efficient
    for( size_t j = 0; j < n; ++j )
    {
        fillMatrix( myMatrix, i, j);
    }
}

但是,在任何情况下,代码都可能受到内存分配的限制(用于向std::setstd::vector::resize添加元素)。因此,除非fillMatrix执行大量额外计算,否则您不应期望代码的特定部分会出现并行性能提升。但是,数据可能位于靠近元素上的线程计算的高速缓存/ NUMA节点中,因此您的并行代码的其余部分可以有效地计算。