Является ли замена элемента Rcpp :: List внутри функции Rcpp безопасной для памяти? - PullRequest
3 голосов
/ 18 апреля 2019

Мне нужно перезаписать элемент объекта Rcpp::List, переданный в качестве параметра в функцию Rcpp. Меня беспокоит безопасность памяти. Это тот случай, когда, переназначая непустой элемент списка, я эффективно перезаписываю указатель на исходный контент, но никогда не освобождаю память, в которой хранится исходный контент? Если да, то как это решить?

Я знаю, что могу легко изменить объект Rcpp (например, Rcpp::NumericVector), который является элементом Rcpp::List, так как Rcpp::NumericVector делает поверхностную копию. Однако это не удовлетворяет моему требованию - полностью заменить элемент чем-то другим.

Ниже приведен фрагмент кода C ++, в котором показан сценарий, на который я ссылаюсь.

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
void replaceListElement(List l)
{
    std::vector<int> v;
    v.push_back(4);
    v.push_back(5);
    v.push_back(6);
    l["a"] = v;
}

/*** R
l <- list()
l$a <- c(1,2,3)
replaceListElement(l)
print(l)
*/

При получении через Rcpp в RStudio команда print(l) выдает следующее

$a
[1] 4 5 6

что является желаемым результатом, поэтому мой вопрос касается только безопасности памяти.

1 Ответ

5 голосов
/ 18 апреля 2019

A Rcpp::List - это Vector<VECSXP>, то есть вектор указателей на другие векторы.Если вы назначаете новый вектор некоторому элементу в этом списке, вы действительно просто изменяете указатель, не освобождая память, на которую указатель использовал для указания.Однако R все еще знает об этой памяти и освобождает ее через сборщик мусора.Мы можем увидеть это в действии с помощью простого эксперимента, в котором я использую ваш код C ++ с небольшим изменением кода R:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]
void replaceListElement(List l)
{
  std::vector<int> v;
  v.push_back(4);
  v.push_back(5);
  v.push_back(6);
  l["a"] = v;
}

/*** R
l <- list()
l$a <- runif(1e7)
replaceListElement(l)
print(l)
gc() # optional
*/

Здесь более крупный вектор используется, чтобы сделать эффект более заметным.Если я теперь использую R -d valgrind -e 'Rcpp::sourceCpp("<filename>")', я получу следующий результат с вызовом gc()

==13827==
==13827== HEAP SUMMARY:
==13827==     in use at exit: 48,125,775 bytes in 9,425 blocks
==13827==   total heap usage: 34,139 allocs, 24,714 frees, 173,261,724 bytes allocated
==13827==
==13827== LEAK SUMMARY:
==13827==    definitely lost: 0 bytes in 0 blocks
==13827==    indirectly lost: 0 bytes in 0 blocks
==13827==      possibly lost: 0 bytes in 0 blocks
==13827==    still reachable: 48,125,775 bytes in 9,425 blocks
==13827==                       of which reachable via heuristic:
==13827==                         newarray           : 4,264 bytes in 1 blocks
==13827==         suppressed: 0 bytes in 0 blocks
==13827== Rerun with --leak-check=full to see details of leaked memory
==13827==
==13827== For counts of detected and suppressed errors, rerun with: -v
==13827== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

И без вызова gc():

==13761==
==13761== HEAP SUMMARY:
==13761==     in use at exit: 132,713,314 bytes in 10,009 blocks
==13761==   total heap usage: 34,086 allocs, 24,077 frees, 173,212,886 bytes allocated
==13761==
==13761== LEAK SUMMARY:
==13761==    definitely lost: 0 bytes in 0 blocks
==13761==    indirectly lost: 0 bytes in 0 blocks
==13761==      possibly lost: 0 bytes in 0 blocks
==13761==    still reachable: 132,713,314 bytes in 10,009 blocks
==13761==                       of which reachable via heuristic:
==13761==                         newarray           : 4,264 bytes in 1 blocks
==13761==         suppressed: 0 bytes in 0 blocks
==13761== Rerun with --leak-check=full to see details of leaked memory
==13761==
==13761== For counts of detected and suppressed errors, rerun with: -v
==13761== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Так что в обоих случаях valgrind не обнаруживает утечки памяти.Объем все еще достижимой памяти отличается примерно на 8x10 ^ 7 байт, то есть размер исходного вектора в l$a.Это демонстрирует, что R действительно знает об исходном векторе и освобождает его, когда ему говорят об этом, но это также происходит, когда R сам решает запустить сборщик мусора.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...