在 R 中使用自定义内存分配函数

Posted

技术标签:

【中文标题】在 R 中使用自定义内存分配函数【英文标题】:Using a custom memory allocation function in R 【发布时间】:2014-10-21 10:08:08 【问题描述】:

我希望能够对 R 中的某些数据结构(实值向量和数组)使用我自己的内存分配函数。这样做的原因是我需要我的数据是 64 位对齐的,我想使用numa library 用于控制使用哪个内存节点(我正在处理具有四个 12 核 AMD Opteron 6174 CPU 的计算节点)。

现在我有两个用于分配和释放内存的函数:numa_alloc_onnodenuma_free(由 this thread 提供)。我使用的是 R 版本 3.1.1,所以我可以访问函数 allocVector3 (src/main/memory.c),这在我看来是添加自定义内存分配器的预期方式。我还在src/include/R_ext中找到了结构R_allocator

但是,我不清楚如何将这些部分组合在一起。假设,在 R 中,我想要一个评估的结果 res,例如

res <- Y - mean(Y)

要保存在使用我自己的函数分配的内存区域中,我该怎么做?我可以直接在 R 级别集成allocVector3 吗?我假设我必须通过 R-C 接口。据我所知,我不能只返回一个指向分配区域的指针,而是必须将结果作为参数传递。所以在 R 中我称之为

n <- length(Y)
res <- numeric(length=1)
.Call("R_allocate_using_myalloc", n, res)
res <- Y - mean(Y)

在 C 中

#include <R.h>
#include <Rinternals.h>
#include <numa.h>

SEXP R_allocate_using_myalloc(SEXP R_n, SEXP R_res)
  PROTECT(R_n = coerceVector(R_n, INTSXP));
  PROTECT(R_res = coerceVector(R_res, REALSXP));
  int *restrict n = INTEGER(R_n);

  R_allocator_t myAllocator;
  myAllocator.mem_alloc = numa_alloc_onnode;
  myAllocator.mem_free = numa_free;
  myAllocator.res = NULL;
  myAllocator.data = ???;

  R_res = allocVector3(REALSXP, n, myAllocator);

  UNPROTECT(2);

不幸的是,我无法超越 variable has incomplete type 'R_allocator_t' 编译错误(我不得不删除 .data 行,因为我不知道应该放什么)。上述任何代码是否有意义?有没有更简单的方法来实现我想要的?必须在 R 中分配一个小向量并在 C 中更改其位置只是为了能够控制内存分配并让向量在 R 中可用,这似乎有点奇怪...

我试图避免使用 Rcpp,因为我正在修改一个相当大的包,并且不想转换所有 C 调用,并且认为混合不同的 C 接口可能会表现不佳。

非常感谢任何帮助。

【问题讨论】:

这是一个猜想:“我试图避免使用 Rcpp,因为我正在修改一个相当大的包并且不想转换所有 C 调用并认为混合不同的 C接口的性能可能不是最优的。” 请凭经验证明 Rcpp 会使您的代码变慢。 对不起,我不想冒犯任何人,也不想暗示在这种情况下使用 Rcpp 是个坏主意。如果有人知道如何使用 Rcpp 解决我的问题,我很乐意尝试一下。或许最好将最后一部分表述为:“我没有看过 Rcpp,因为我正在修改一个不使用 Rcpp 的相当大的包。” 变化是增量的。您可以只添加一个(新)功能,而无需对包的其余部分进行任何更改。 我尝试使用 Rcpp 解决我的问题,但收到了 long vectors not supported yet 错误。我正在使用Rcpp_0.11.3。我做错了什么还是实际上还不支持长向量? 【参考方案1】:

我在解决我的问题方面取得了一些进展,我想分享一下,以防其他人遇到类似情况。感谢凯文的评论。我错过了他提到的包含声明。不幸的是,这只是众多问题之一。

dyn.load("myAlloc.so")

size <- 3e9
myBigmat <- .Call("myAllocC", size)
print(object.size(myBigmat), units = "auto")

rm(myBigmat)
#include <R.h>
#include <Rinternals.h>
#include <R_ext/Rallocators.h>
#include <numa.h>

typedef struct allocator_data 
  size_t size;
 allocator_data;

void* my_alloc(R_allocator_t *allocator, size_t size) 
  ((allocator_data*)allocator->data)->size = size;
  return (void*) numa_alloc_local(size);


void my_free(R_allocator_t *allocator, void * addr) 
  size_t size = ((allocator_data*)allocator->data)->size;
  numa_free(addr, size);


SEXP myAllocC(SEXP a) 
  allocator_data* my_allocator_data = malloc(sizeof(allocator_data));
  my_allocator_data->size = 0;

  R_allocator_t* my_allocator = malloc(sizeof(R_allocator_t));
  my_allocator->mem_alloc = &my_alloc;
  my_allocator->mem_free = &my_free;
  my_allocator->res = NULL;
  my_allocator->data = my_allocator_data;

  R_xlen_t n = asReal(a);
  SEXP result = PROTECT(allocVector3(REALSXP, n, my_allocator));
  UNPROTECT(1);
  return result;

为了编译c代码,我使用R CMD SHLIB -std=c99 -L/usr/lib64 -lnuma myAlloc.c。据我所知,这很好用。如果有人可以提供改进/更正,我很乐意提供。

原始问题中尚未解决的一个要求是对齐问题。 numa_alloc_local 返回的内存块正确对齐,但新的VECTOR_SEXPREC 的其他字段(例如sxpinfo_struct 标头)推回数据数组的开头。是否可以以某种方式对齐这个起点(REAL() 返回的地址)?

【讨论】:

【参考方案2】:

R 有,在memory.c:

main/memory.c
84:#include <R_ext/Rallocators.h> /* for R_allocator_t structure */

所以我认为您还需要包含该标头以获取自定义分配器(RInternals.h 仅声明它,未定义 struct 或包含该标头)

【讨论】:

以上是关于在 R 中使用自定义内存分配函数的主要内容,如果未能解决你的问题,请参考以下文章

STL容器自定义内存分配器

STL容器自定义内存分配器

STL容器自定义内存分配器

C中动态内存分配器的自定义实现

自定义堆栈分配器中的 C++ 内存对齐

C 语言二级指针作为输入 ( 自定义二级指针内存 | 二级指针排序 | 抽象业务逻辑函数 )