【问题标题】:C++ Eigen Matrix Operations vs. Memory Allocation PerformanceC++ 特征矩阵运算与内存分配性能
【发布时间】:2026-01-27 14:05:01
【问题描述】:

我有一个算法,它需要在一个函数内构建一个 NxN 矩阵,该函数将返回这个矩阵与一个 Nx1 向量的乘积,该向量也是动态构建的。 (N 通常为 8 或 9,但必须对大于该值的值进行泛化)。

我正在使用 Eigen 库来执行更复杂的代数运算(最小二乘和其他几个约束问题),因此不能切换它。

我已经对函数进行了基准测试,但由于内存密集,存在巨大的瓶颈 分配。我的目标是构建一个线程安全的应用程序,因此,在某些情况下,我将这些矩阵和向量替换为对全局向量中元素的引用,该向量用作无法存储在堆栈上的对象的提供者。这避免了调用 Eigen 矩阵和向量的构造函数/析构函数,但它不是一个优雅的解决方案,如果不小心,它可能会导致巨大的问题。

因此,Eigen 是否提供了一种解决方法,因为我没有看到将分配器作为模板参数传递给这些对象的选项,或者是否有更明显的事情要做?

【问题讨论】:

  • N 在通话之间是否发生变化?如果是,那么当你不知道N 会是什么时,你是如何准备这个“全局对象”的?
  • N 在调用之间不会改变。它取决于创建正在处理的对象的输入数据模型。
  • 那么也许你可以在你的函数中让你的“资源管理器”成为static。否则,全球性的很难避免 IMO。我认为这应该不是很难。你应该做一些简单的事情,即没有垃圾收集或任何清理。
  • 由于多线程限制,最好在线程基础上为这些矩阵提供一个外部/全局提供程序。
  • 您可以在资源管理器中拥有锁。是否为static 无关紧要。当然,如果您的应用程序分配垃圾邮件,它将在这些调用中被序列化。但它不认为这在正常用例中是一个真正的问题。另外我不认为eigen 暴露了它的内存分配。为了使用 SSE,他们做了一些魔术来获得内存对齐。所以最好的办法是像你一样将 eigen 对象本身包装在某种资源管理器中。

标签: c++ performance memory-management matrix eigen


【解决方案1】:

您可以根据自己的需要管理自己的内存,并使用Eigen::Map 而不是Eigen::Matrix 来执行计算。只需确保数据正确对齐,否则通知 Eigen。

详情请参阅参考Eigen::Map

这是一个简短的例子:

#include <iostream>
#include <Eigen/Core>


int main() {
    int mydata[3 * 4]; // Manage your own memory as you see fit
    int* data_ptr = mydata;

    Eigen::Map<Eigen::MatrixXi, Eigen::Unaligned> mymatrix(data_ptr, 3, 4);

    // use mymatrix like you would any another matrix
    mymatrix = Eigen::MatrixXi::Zero(3, 4);
    std::cout << mymatrix << '\n';

    // This line will trigger a failed assertion in debug mode
    // To change it see
    // http://eigen.tuxfamily.org/dox-devel/TopicAssertions.html
    mymatrix = Eigen::MatrixXi::Ones(3, 6);


    std::cout << mymatrix << '\n';
}

【讨论】:

    【解决方案2】:

    将我的 cmets 收集到一个完整的想法中。以下是我会尝试的方法。

    因为 eigen 中的内存分配是 IMO 非常先进的东西,而且它们并没有暴露太多可以利用它的地方。最好的办法是将 eigen 对象本身包装到某种资源管理器中,就像 OP 所做的那样。

    我会把它做成一个简单的 bin,用来存放 Matrix&lt; Scalar, Dynamic, Dynamic&gt; 对象。这样您就可以模板化 Scalar 类型并拥有一个通用大小矩阵的管理器。

    当你调用一个对象时,你检查你是否有一个所需大小的空闲对象,你返回对它的引用。如果没有,你分配一个新的。简单的。当您想释放对象时,您可以在资源管理器中将其标记为空闲。我认为不需要任何更复杂的东西,但当然可以实现一些更复杂的逻辑。

    为了确保线程安全,我会在管理器中加锁。如果需要,在构造函数中初始化它。当然需要锁定freeallocate

    但是取决于工作时间表。如果线程在它们自己的数组上工作,我会考虑为每个线程创建一个资源管理器实例,这样它们就不会互相计时。问题是,如果您有 12 个核心在分配/解除分配上工作繁重,并且通过这一个锁有效地序列化您的应用程序,那么全局锁或全局管理器可能会耗尽。

    【讨论】:

      【解决方案3】:

      您可以尝试用jemalloctcmalloc 替换您的默认内存分配器。借助LD_PRELOAD 机制,很容易尝试。

      我认为它也适用于大多数 C++ 项目。

      【讨论】:

        【解决方案4】:

        您可以为一些常见的矩阵大小分配内存使用operator newoperator new[] 调用该函数之前,将void* 指针存储在某处并让函数本身使用合适的尺码。之后,您可以使用placement new 进行矩阵构造。 更有效的 C++,第 8 项中给出了详细信息。

        【讨论】:

        • 如果适用的话,可能没那么容易。因为eigen 使用其内部分配器来处理内存对齐。我也不认为有一个函数可以检索特征 object 占用的内存量。