c++ 设置Eigen::SparseMatrix的稀疏模式而不增加内存开销

vmdwslir  于 2023-05-20  发布在  其他
关注(0)|答案(1)|浏览(220)

我需要设置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的稀疏模式?
1.为什么setFromTriplets需要这么多内存?
如果我的例子是错的,请告诉我。
我的Eigen版本是3.3.2
PS对不起我的英语
编辑:看起来手动插入(使用预分配)每个三元组的工作速度更快,峰值时需要的内存更少。但我还是想知道是否可以手动设置稀疏模式。

ljsrvy3e

ljsrvy3e1#

广告1:如果你能保证按字典顺序插入元素,你甚至可以通过使用内部函数startVecinsertBack比普通的insert更高效。
Ad 2:如果你使用setFromTriplets,你需要大约是最终矩阵大小的两倍(加上你的Triplet容器的大小),因为元素首先被插入到矩阵的转置版本中,然后被转置到最终矩阵中,以确保所有内部向量都被排序。如果你事先知道矩阵的结构,这显然是相当浪费内存的,但它的目的是处理任意的输入数据。
在你的例子中,你有5000000 * 100000 / 1000 = 5e 8个元素。一个Triplet需要8+8+8 = 24个字节(对于vector来说大约是12 Gb),稀疏矩阵的每个元素需要8+8=16个字节(一个double用于值,一个long用于内部索引),即每个矩阵大约8 Gb,所以总共需要大约28 Gb,大约是26 Gib。
奖励:如果你的矩阵有一些特殊的结构,可以更有效地存储,并且你愿意深入挖掘Eigen内部,你也可以考虑实现一个从Eigen::SparseBase<>继承的新类型(但我不重新评论这个,除非内存/性能对你来说非常关键,并且你愿意阅读大量“稀疏”文档的内部Eigen代码...)。然而,在这种情况下,可能更容易考虑您打算用矩阵做什么,并尝试只实现特殊操作。

相关问题