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

时间:2017-07-25 10:58:52

标签: c++ memory sparse-matrix 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();

在我的情况下,这个例子在峰值时消耗了大约26Gb的东西(在行&#34;填充垫&#34;和#34;完成&#34;之间)和18Gb。 (我通过htop进行了所有检查)。 ~8Gb的开销对我来说相当大(在我的&#34;现实世界&#34;任务我有更大的开销)。

所以我有两个问题:

  1. 如何以尽可能少的开销填充Eigen :: SparseMatrix的稀疏模式
  2. 为什么setFromTriplets需要这么多内存?
  3. 如果我的例子有误,请告诉我。

    我的Eigen版本是3.3.2

    PS抱歉我的英文

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

1 个答案:

答案 0 :(得分:2)

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

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

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

加成: 如果你的矩阵有一些特殊的结构,可以更有效地存储,并且你愿意深入研究Eigen内部,你也可以考虑实现一个继承自Eigen::SparseBase<>的新类型(但我不推荐这个,除非记忆/表现对你非常关键,并且你愿意经历许多“稀疏”记录的内部本征代码......)。但是,在这种情况下,可能更容易考虑您打算对矩阵做什么,并尝试仅为此实现特殊操作。