修改矩阵后,通过内存指针(memptr)直接访问犰狳矩阵的条目不起作用

Posted

技术标签:

【中文标题】修改矩阵后,通过内存指针(memptr)直接访问犰狳矩阵的条目不起作用【英文标题】:Directly accessing entries of armadillo-matrix by memory-pointer (memptr) does not work after matrix is modified 【发布时间】:2021-04-21 11:39:01 【问题描述】:

我有一个问题,我想通过结构(或类)中的指针访问犰狳矩阵“M”的某些条目。在初始化 M 之后,我在结构中设置了指针。通过取消引用指针,我可以看到它具有正确的值(M 的第一个条目 - 或 M(0,0))。

然后我将 M 更改为 M * M。但是现在取消引用指针不再给我正确的值了。奇怪的是:如果我有一个小矩阵,即 3x3 或 4x4(请参阅我的代码中的“matrixSize”),则不会发生错误。对于小矩阵,取消引用指针会给出 RIGHT 值。 更大的矩阵虽然会导致错误的值(5x5 类似于“2.76282e-320”,这可能是内存中的某个随机位置)。

我在这里做错了什么?我该如何解决这个问题?

如果有帮助,我想解释一下我想要实现的目标: 我有一个延迟耦合节点网络,每个节点都有某种动态行为。 (想想延迟耦合微分方程 DDE - 延迟耦合振荡器)。由于它们是延迟耦合的,我需要存储它们过去的状态(它们的历史数组)。每个振荡器也有一些 LOCAL 动态与不影响其他节点的动态变量,这意味着我不必存储它们的历史。 该矩阵用于保存节点的某些变量的过去状态。我想把它们放在一个矩阵中,因为我想对它们使用向量运算(矩阵的一个索引代表时间,而另一个是节点索引)。但我也想单独访问它们以计算每个节点(振荡器)的一些局部动态。所以我想更新单个节点,还要更新全局状态。 这就是为什么同时拥有两种表示会有所帮助的原因:对于局部动态,我通过指向矩阵的指针访问延迟状态。对于全局动力学,我通过矩阵中用作历史数组的行来访问耦合变量。

#include <iostream>
#include <armadillo>

struct someStruct 
    const double *currentStateInMatrix;
    void printvalue() std::cout << "*currentState (pointer to first enty of matrix) = " << *currentStateInMatrix << std::endl;
;

int main(int argc, char* argv[])

    uint M_size = 4;

    arma::Mat<double> M(M_size, M_size, arma::fill::randu);


    double* pointerIntoMatrix = M.memptr();
    std::cout << "pointer [ " << pointerIntoMatrix << " ] has value: " << *pointerIntoMatrix << std::endl;

    someStruct myStruct;
    myStruct.currentStateInMatrix = pointerIntoMatrix;

    std::cout << "original matrix M: \n" << M;
    myStruct.printvalue();
    std::cout << "\n+++changing contents of matrix M+++\n\n";

    M = M * M;

    std::cout << "M * M: \n" << M;
    myStruct.printvalue();

我使用以下方法编译它: g++ file.cpp -std=c++17 -larmadillo -o main

【问题讨论】:

myMatrix = myMatrix * myMatrix 可能会导致arma::Mat&lt;double&gt; 实例内的内部重新分配。因此,您保存的指针值不再有效。 有什么办法可以防止这种情况发生吗? M*M 创建一个新的临时对象,然后将其分配给M。因此,先前的指针不再有效。对于这种情况,您可以将 M=M*M 替换为 M*=M 并且它可以工作,但即便如此我也不确定它是否会得到保证。但是,如果您使用 mat::fixed&lt;n_rows, n_cols&gt; 而不是 arma::Mat&lt;double&gt;,则在编译时为 M 分配内存,并且保证 M*=M 不会使指针无效并将结果保存在同一内存中。 【参考方案1】:

Armadillo 中的操作M = M * M 是矩阵乘法(不是逐个元素的乘法)。所以将M * M 的中间计算直接存储到M 是有问题的。它将覆盖M 中的现有数据,而这些数据仍然需要完成M * M 操作。

假设 Armadillo 检测到此问题并将M * M 的结果存储到单独的内存块中,然后将该块分配给M,这可能是安全的。

有办法解决这个问题。使用固定大小的矩阵,如 cmets 中提到的 darcamo。使用Mat&lt;double&gt;::fixed&lt;4, 4&gt; M; 声明矩阵。

另一种方法是手动管理矩阵元素的内存并告诉M 矩阵始终使用该内存。有advanced constructors 可以做到这一点:

mat(ptr_aux_mem, n_rows, n_cols, copy_aux_mem = true, strict = false)

所以我们可以这样做:

double* my_mem = (double*)std::malloc(M_size * M_size * sizeof(double));
// or use new[]

Mat<double> M(my_mem, M_size, M_size, false, true);

// don't forget to call std::free(my_mem) or delete[] when done!

请注意。上述两种解决方法(固定大小的矩阵和手动内存管理)可能会有轻微的性能损失。无法避免为M = M * M 操作使用单独的内存块。使用这些变通方法时,Armadillo 会将M * M 的结果存储到临时缓冲区中,然后将缓冲区的内容复制到M 使用的内存中。

【讨论】:

哦,谢谢!看来我将不得不在这里踏入犰狳的深处。这里有很多诡计。但这真的很有帮助!我将尝试其中一些高级构造方法。另外:我刚刚对为什么 M 更改其位置(从而使指针无效)有了另一个想法:如果 M 不是平方的,则操作 M * M 返回一个不同大小的矩阵。在最好的情况下,返回的矩阵更小(并且适合相同的内存 - 具有不同的界限)。在最坏的情况下,它会更大。 M 为正方形是大小保持不变的一种情况。

以上是关于修改矩阵后,通过内存指针(memptr)直接访问犰狳矩阵的条目不起作用的主要内容,如果未能解决你的问题,请参考以下文章

犰狳向量 vec/fvec 的内存对齐

MATLAB——cell数组用法

三指针

OPENCV - 直接使用指针访问过滤图像并使用内核矩阵过滤

内存对象管理器(基于数组和链表实现)

通过指针访问矩阵数据