设置无内存开销的Eigen::SparseMatrix的稀疏模式

2022-04-13 00:00:00 sparse-matrix memory c++ eigen3

我需要设置我已经知道的Eigen::SparseMatrix的稀疏模式(我有唯一排序的列索引和行偏移量)。显然,可以通过setFromTriplets实现,但遗憾的是,setFromTriplets需要大量额外内存(至少在我的情况下是这样)

我写了一个小例子

const long nRows = 5000000;
const long nCols = 100000;
const long nCols2Skip = 1000;
//It's quite big!
const long nTriplets2Reserve = nRows * (nCols / nCols2Skip) * 1.1;
Eigen::SparseMatrix<double, Eigen::RowMajor, long> mat(nRows, nCols);

std::vector<Eigen::Triplet<double, long>> triplets;

triplets.reserve(nTriplets2Reserve);
for(long row = 0; row < nRows; ++row){
    for(long col = 0; col < nCols; col += nCols2Skip){
        triplets.push_back(Eigen::Triplet<double, long>(row, col, 1));
    }
}
std::cout << "filling mat" << std::endl << std::flush;
mat.setFromTriplets(triplets.begin(), triplets.end());

std::cout << "Finished! nnz " << mat.nonZeros() << std::endl;
//Stupid way to check memory consumption
std::cin.get();

在我的例子中,这个示例在峰值时(在"填充垫子"和"已完成"行之间)消耗了大约26 GB,最终消耗了18 GB。(我通过htop进行了所有检查)。~8 GB的开销对我来说相当大(在我的"现实世界"任务中,我有更大的开销)。

所以我有两个问题:

  1. 如何以尽可能少的开销填充Eigen::SparseMatrix的稀疏模式
  2. 为什么setFromTriplets需要这么多内存?

如果我的示例有误,请让我知道。

我的本征版本是3.3.2

PS为我的英语道歉

编辑: 它看起来像inserting(使用preallocation),每个三元组手动工作更快,并且在峰值时需要更少的内存。但我还是想知道是否可以手动设置稀疏模式


解决方案

广告1:如果您能保证按词典顺序插入元素,使用内部函数startVecinsertBack甚至可以比普通的insert更高效。

ad2:如果您使用setFromTriplets,您需要大约两倍于矩阵的最终大小(加上三元组容器的大小),因为元素首先被插入到矩阵的转置版本中,然后被转置到最终的矩阵中,以确保所有内部向量都被排序。如果您知道前面矩阵的结构,这显然是相当浪费内存,但它的目的是处理任意输入数据。

在您的示例中,您有5000000*100000/1000=5e8元素。ATriplet需要8+8+8=24个字节(对于vector大约12 GB),稀疏矩阵的每个元素需要8+8=16个字节(一个double用于值,一个long用于内部索引),即每个矩阵大约8 GB,所以总共需要大约28 GB,大约26 Gib。

奖金: 如果您的矩阵有一些特殊的结构,可以更高效地存储,并且您愿意更深入地挖掘Eigen的内部结构,那么您也可以考虑实现一个继承自Eigen::SparseBase<>的新类型(但我不会对此进行重新注释,除非内存/性能对您非常重要,并且您愿意经历大量"稀疏的"文档记录的内部Eigen代码...)。但是,在这种情况下,考虑您打算如何处理矩阵并尝试仅对其执行特殊操作可能更容易。

相关文章