0

当我用 C++ 编写线性代数程序时,我使用 Armadillo 库。它基于模板,它为我提供了一种定义任何长度的向量的方法,这些向量不一定需要额外的内存分配,因为它们在编译时静态分配了适当的内存缓冲区。当我使用arma::Col<double>::fixed<3>编译器时,会即时创建一个“新类型”,以便向量包含正好 3 个双精度的缓冲区。

现在我正在使用 C 语言编写线性代数程序,并且正在使用 GNU 科学库 (GSL)。为了实例化一个 3D 向量,我这样做:gsl_vector_alloc(3)返回一个gsl_vector*. 问题是这个操作会导致一小部分内存的动态分配,并且在程序运行期间会发生数百万次。我的程序正在浪费大量资源来执行数千万malloc/free次操作。

我检查了内部结构gsl_vector

typedef struct
{
   size_t size;
   size_t stride;
   double * data;
   gsl_block * block;
   int owner;
} gsl_vector;

为了使库正常工作,data应该指向向量的第一个元素,通常在这样的gsl_block结构内:

typedef struct
{
   size_t size;
   double * data;
} gsl_block;

其中包含另一个data指针。所以,为了实例化一个简单的 3D 向量,这个mallocs 序列发生:

  1. 一个gsl_vector结构是malloc'd (在 x86_64 上大约 40 个字节)。
  2. 一个gsl_block结构是malloc'd (16 bytes) 并且 gsl_vector 的block指针被设置为刚刚分配的 gsl_block 的内存地址
  3. 3 个双精度数组是malloc'd,其内存地址分配给两个data指针(一个 ingsl_block和一个 in gsl_vector)。

malloc通过删除两个s ,我获得了 40% 的性能提升。我创建了我的自定义gsl_vector创建例程,它分配了一个包含 3 个双精度数的数组,并将data指针设置gsl_vector为该数组的地址。然后我返回一个gsl_vector(不是指针)。
但这样做,我仍然得到数百万次malloc(3 * sizeof(double))操作。

我没有设法将 3 个双精度数组“嵌入”到gsl_vector结构中,因为如果data指针指向结构本身内部的东西(hacky!),那么当向量被复制到别处时指针不再有效!

你有什么想法(除了切换到 C++ 或滚动我自己的线性代数库)?我愿意接受任何建议。

4

2 回答 2

4

在我看来,您好像误解了gls_block数据结构的目的。在我看来,您应该只使用它在数据结构中分配一大块数据gsl_block,然后将该块拆分为多个gsl_vector. 如果您gsl_vector通过分配一组数组来一次性分配所有内容,那么您就快到了。在初始化期间,您只有两次调用malloc和一些簿记。

这将强加给您的是,您必须提前准确地考虑gsl_vector您需要什么。但这是您使用没有内置垃圾收集的语言时要付出的“代价”。如果您对此进行投资,大多数时候它具有构建代码的优势,您可能会学到很多关于如何组织计算的知识。

于 2013-04-13T16:42:14.550 回答
0

C 有点太原始,无法正确执行此操作。

如果您需要使用 GSL 中的函数,您可能仍然可以使用 C++ Armadillo 向量和矩阵。

例如,您可以通过.memptr()成员函数获取指向向量或矩阵使用的内存的指针。这也适用于固定大小的矩阵/向量。

或者,您可以告诉 Armadillo 使用已分配的内存块,方法是在向量​​或矩阵构造期间给它一个指针。

于 2013-04-13T16:43:07.560 回答