2015-07-15 2 views
5

Я начал использовать Rcpp. Мне это очень нравится. Я довольно новичок в программировании. У меня вопрос относительно использования памяти. Ниже воспроизводимая проблема:RcppArmadillo: проблема с использованием памяти

library(RcppArmadillo) 
library(inline) 
code <- " 
    Rcpp::NumericVector input_(input); 
    arma::cube disturb(input_.begin(), 2, 2, 50000000, false); 
    return wrap(2); 
" 
Test <- cxxfunction(signature(input = "numeric"), plugin = "RcppArmadillo", body = code) 
input <- array(rnorm(2 * 2 * 50000000), dim = c(2, 2, 50000000)) 
Test(input) 

Я понимаю, что в задаче выше только использование памяти, когда я присвоить массив переменной ввода в R. Таким образом, я должен использовать только около 1,6 ГБ (2 * 2 * 50 * 8 = 1600). Когда я перехожу к Rcpp, я инициализирую переменную input_ с использованием объекта SEXP, который является указателем. Поэтому это не должно использовать дополнительную память. Затем, когда я инициализирую переменную , нарушите, я также использую указатель и задаю copy_aux = FALSE. Поэтому я не должен использовать память. Поэтому, если мое понимание правильное, я должен использовать только 1,6 ГБ, когда я запускаю код. Это верно?

Однако, когда я запускаю код, использование памяти (судя по просмотру системного монитора в Ubuntu), прыгает выше 10 ГБ (от 1 ГБ), прежде чем опускаться до 4 ГБ. Я не понимаю, что происходит. Я неправильно использовал Rcpp?

Ваша помощь приветствуется. Большое спасибо.

+0

Я тестировал ту же проблему, используя ** sourceCpp() ** и как R-пакет. У меня такая же проблема с большим непредвиденным использованием памяти, когда я передаю большие объекты на C++. –

+2

Это, похоже, не проблема для 'arma :: mat', но я получаю точно такую ​​же проблему с' arma :: cube'. Может ли это относиться к отсутствию надлежащего класса 3d-массива внутри Rcpp? –

+0

Да. Он работает для arma :: mat. Но так как ** input _ ** является Rcpp :: NumericVector, и я использую указатель на инициализацию переменной arma :: cube, должно ли это иметь значение? Я, вероятно, ошибаюсь. –

ответ

4

Edit после новой версии Armadillo (5.300)

После этого начального Q/A на StackOverflow, Конрад Sanderson и я занимался в некоторой электронной дискуссии об этой проблеме. По дизайну объекты arma::cube создают arma::mat для каждого фрагмента (третьего измерения) cube. Это делается во время создания cube, даже если данные копируются из существующей памяти (как в исходном вопросе). Поскольку это не всегда необходимо, я предположил, что должна быть возможность отключить предварительное выделение матриц для срезов. Что касается текущей версии Armadillo (5.300.4), то теперь есть. Это можно установить из CRAN.

Пример кода:

library(RcppArmadillo) 
library(inline) 
code <- " 
    Rcpp::NumericVector input_(input); 
    arma::cube disturb(input_.begin(), 2, 2, 50000000, false, true, false); 
    return wrap(2); 
" 
Test <- cxxfunction(signature(input = "numeric"), plugin = "RcppArmadillo", body = code) 
input <- array(rnorm(2 * 2 * 50000000), dim = c(2, 2, 50000000)) 
Test(input) 

Ключевым моментом здесь является то, что cube конструктор теперь называется использованием arma::cube disturb(input.begin(), 2, 2, 50000000, false, true, false);. Конечным false здесь является новый параметр prealloc_mat, который определяет, следует ли предварительно выделить матрицы. Метод slice будет по-прежнему работать нормально на cube без предварительно выделенных матриц - матрица будет выделена по требованию. Однако, если вы напрямую обращаетесь к члену mat_ptrscube, он будет заполнен указателями NULL. The help has also been updated.

Огромное спасибо Конраду Сандерсону за то, что он так быстро выполнил эту дополнительную функцию, а Дирк Эддельбуэттель за всю свою работу над Rcpp и RcppArmadillo!

Оригинальный ответ

Это немного странно один. Я попытался с диапазоном различных размеров массивов, а проблема возникает только с массивами, где третье измерение намного больше, чем другой 2. Вот воспроизводимый пример:

library("RcppArmadillo") 
library("inline") 
code <- " 
Rcpp::NumericVector input_(input); 
IntegerVector dim = input_.attr(\"dim\"); 
arma::cube disturb(input_.begin(), dim[0], dim[1], dim[2], false); 
disturb[0, 0, 0] = 45; 
return wrap(2); 
" 
Test <- cxxfunction(signature(input = "numeric"), plugin = "RcppArmadillo", body = code) 
input <- array(0, c(1e7, 2, 2)) 
Test(input) 
# no change in memory usage 

dim(input) <- c(2, 1e7, 2) 
gc() 
Test(input) 
# no change in memory usage 

dim(input) <- c(2, 2, 1e7) 
gc() 
Test(input) 
# spike in memory usage 

dim(input) <- c(20, 2, 1e6) 
gc() 
Test(input) 
# no change in memory usage 

Это говорит о том, что это что-то о том, как что реализована библиотека Aramadillo (или, возможно, RcppArmadillo). Это, конечно, не похоже на то, что вы делаете неправильно.

Примечание. Я включил некоторую модификацию вместо данных (установка первого элемента на 45), и вы можете подтвердить, что в каждом случае данные заменяются на, что означает, что копия отсутствует на.

В настоящее время я предлагаю, если возможно, организовать ваши 3D-массивы таким образом, чтобы наибольшее измерение не было третьим.

EDIT После этого еще немного покопаться, это выглядит так, как будто является выделение памяти при создании arma::cube. В Cube_meat.hpp, в методе create_mat, есть следующий код:

if(n_slices <= Cube_prealloc::mat_ptrs_size) 
    { 
    access::rw(mat_ptrs) = const_cast< const Mat<eT>** >(mat_ptrs_local); 
    } 
else 
    { 
    access::rw(mat_ptrs) = new(std::nothrow) const Mat<eT>*[n_slices]; 

    arma_check_bad_alloc((mat_ptrs == 0), "Cube::create_mat(): out of memory"); 
    } 
} 

Cube_prealloc::mat_ptrs_size, кажется, 4, так что на самом деле является проблемой для любого массива с более чем 4 ломтика.

Я разместил issue on github.

EDIT2 Однако, это определенно проблема с базовым кодом Armadillo. Вот воспроизводимый пример, который вообще не использует Rcpp. Это только linux-only - он использует код от How to get memory usage at run time in c++?, чтобы извлечь текущее использование памяти в текущем процессе.

#include <iostream> 
#include <armadillo> 
#include <unistd.h> 
#include <ios> 
#include <fstream> 
#include <string> 

////////////////////////////////////////////////////////////////////////////// 
// 
// process_mem_usage(double &, double &) - takes two doubles by reference, 
// attempts to read the system-dependent data for a process' virtual memory 
// size and resident set size, and return the results in KB. 
// 
// On failure, returns 0.0, 0.0 

void process_mem_usage(double& vm_usage, double& resident_set) 
{ 
    using std::ios_base; 
    using std::ifstream; 
    using std::string; 

    vm_usage  = 0.0; 
    resident_set = 0.0; 

    // 'file' stat seems to give the most reliable results 
    // 
    ifstream stat_stream("/proc/self/stat",ios_base::in); 

    // dummy vars for leading entries in stat that we don't care about 
    // 
    string pid, comm, state, ppid, pgrp, session, tty_nr; 
    string tpgid, flags, minflt, cminflt, majflt, cmajflt; 
    string utime, stime, cutime, cstime, priority, nice; 
    string O, itrealvalue, starttime; 

    // the two fields we want 
    // 
    unsigned long vsize; 
    long rss; 

    stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr 
       >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt 
       >> utime >> stime >> cutime >> cstime >> priority >> nice 
       >> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest 

    stat_stream.close(); 

    long page_size_kb = sysconf(_SC_PAGE_SIZE)/1024; // in case x86-64 is configured to use 2MB pages 
    vm_usage  = vsize/1024.0; 
    resident_set = rss * page_size_kb; 
} 

using namespace std; 
using namespace arma; 

void test_cube(double* numvec, int dim1, int dim2, int dim3) { 
    double vm, rss; 

    cout << "Press enter to continue"; 
    cin.get(); 

    process_mem_usage(vm, rss); 
    cout << "Before:- VM: " << vm << "; RSS: " << rss << endl; 

    cout << "cube c1(numvec, " << dim1 << ", " << dim2 << ", " << dim3 << ", false)" << endl; 
    cube c1(numvec, dim1, dim2, dim3, false); 

    process_mem_usage(vm, rss); 
    cout << "After:- VM: " << vm << "; RSS: " << rss << endl << endl; 
} 

int 
main(int argc, char** argv) 
    { 
    double* numvec = new double[40000000]; 

    test_cube(numvec, 10000000, 2, 2); 
    test_cube(numvec, 2, 10000000, 2); 
    test_cube(numvec, 2, 2, 1000000); 
    test_cube(numvec, 2, 2, 2000000); 
    test_cube(numvec, 4, 2, 2000000); 
    test_cube(numvec, 2, 4, 2000000); 
    test_cube(numvec, 4, 4, 2000000); 
    test_cube(numvec, 2, 2, 10000000); 

    cout << "Press enter to finish"; 
    cin.get(); 

    return 0; 
    } 

EDIT 3 согласно create_mat кода выше, arma::mat создается для каждого среза куба. На моей 64-разрядной машине это приводит к 184 байтам служебных данных для каждого фрагмента. Для куба с 5e7 срезами, равным 8,6 гигабайт накладных расходов, даже если базовые числовые данные занимают всего 1,5 гигабайта. Я отправил по электронной почте Конраду Сандерсону, чтобы узнать, является ли это основополагающим для того, как Армадилло работает или может быть изменен, но на данный момент определенно кажется, что вы хотите, чтобы ваш размер (третий) был самым маленьким из трех, если вообще возможное. Также стоит отметить, что это относится к всемcube s, а не только к тем, которые созданы из существующей памяти. Использование конструктора arma::cube(dim1, dim2, dim3) приводит к тому же использованию памяти.

+1

Только что опубликовал проблему на странице [RcppArmadillo github] (https://github.com/RcppCore/RcppArmadillo/issues/47) –

+0

Благодарим за помощь. –

+1

Это проблема Армадилло, а не проблема RcppArmadillo. Я позволю Конраду Сандерсону узнать. –

Смежные вопросы