Search code examples
c++rrcppgperftools

Getting gperftools to work with Rcpp


I've read related posts here and here, and looked at Dirk Eddelbuettel's talk here and yet I'm failing to even get a .log file from gperftools. Here is my R file, called Rcpp_practice.R:

library(Rcpp)

Sys.setenv("PKG_LIBS"="-lprofiler")
sourceCpp('eigen.cpp')

a <- matrix(rnorm(300^2), 300, 300)
getEigenValues(a)

Here are the contents of eigen.cpp:

#include <RcppArmadillo.h>
#include <gperftools/profiler.h>
// [[Rcpp::depends(RcppArmadillo)]]

// [[Rcpp::export]]
arma::vec getEigenValues(arma::mat M) {
    return arma::eig_sym(M);
}

Then, on Terminal (I'm using OSX):

CPUPROFILE="eigenprof.log" R -f "Rcpp_practice.R"

I was hoping to see eigenprof.log sitting in my working directory but I don't. Also, I don't get the message that I've seen in other posts of the form PROFILE: interrupts/evictions/bytes = 012/34/567891

I verified that I have the latest version of gperftools installed. ($ brew upgrade google-perftools gives Error: gperftools 2.5 already installed).

What am I missing?

Update

After I modified my code to match @nrussell's I get this error message:

Error in dyn.load("/private/var/folders/6k/ffcchdq52kbb7d631b5vsqcw0000gn/T/RtmpCIdHkG/sourceCpp-x86_64-apple-darwin13.4.0-0.12.7/sourcecpp_6c3253d60384/sourceCpp_2.so") : 
unable to load shared object '/private/var/folders/6k/ffcchdq52kbb7d631b5vsqcw0000gn/T/RtmpCIdHkG/sourceCpp-x86_64-apple-darwin13.4.0-0.12.7/sourcecpp_6c3253d60384/sourceCpp_2.so':
dlopen(/private/var/folders/6k/ffcchdq52kbb7d631b5vsqcw0000gn/T/RtmpCIdHkG/sourceCpp-x86_64-apple-darwin13.4.0-0.12.7/sourcecpp_6c3253d60384/sourceCpp_2.so, 6): Symbol not found: _ProfilerStart
Referenced from: /private/var/folders/6k/ffcchdq52kbb7d631b5vsqcw0000gn/T/RtmpCIdHkG/sourceCpp-x86_64-apple-darwin13.4.0-0.12.7/sourcecpp_6c3253d60384/sourceCpp_2.so
Expected in: flat namespace
in /private/var/folders/6k/ffcchdq52kbb7d631b5vsqcw0000gn/T/RtmpCIdHkG/sourceCpp-x86_64-apple-darwin13.4.0-0.12.7/sourcecpp_6c3253d60384/sourceCpp_2.so
Calls: sourceCpp -> source -> withVisible -> eval -> eval -> dyn.load
Execution halted

This appears on the line with sourceCpp if I run the script interactively.


Solution

  • I don't have access to an OS X machine, but the following worked on Debian 8, where I've just added in the ProfilerStart / ProfilerStop calls to your C++ code (and not changed your R code at all¹):

    // [[Rcpp::depends(RcppArmadillo)]]
    #include <RcppArmadillo.h>
    #include <gperftools/profiler.h>
    
    // [[Rcpp::export]]
    arma::vec getEigenValues(arma::mat M) {
        ProfilerStart("./eigen-prof.log");
        return arma::eig_sym(M);
        ProfilerStop();
    }
    

    ¹See update below.


    Then from a terminal,

    R -f eigen-prof.R > /dev/null && google-pprof --text $(which R) eigen-prof.log 
    
    # PROFILE: interrupts/evictions/bytes = 2/0/264
    # Using local file /usr/local/bin/R.
    # Using local file eigen-prof.log.
    # /usr/bin/addr2line: /usr/local/bin/R: File format not recognized
    # Total: 2 samples
    #        1  50.0%  50.0%        1  50.0% dlamch_
    #        1  50.0% 100.0%        1  50.0% dsymv_
    #        0   0.0% 100.0%        1  50.0% 00000000004007ca
    #        0   0.0% 100.0%        1  50.0% 00000000004007fa
    #        0   0.0% 100.0%        1  50.0% 00007f84002bf0bd
    #        0   0.0% 100.0%        1  50.0% 00007f84002bf2a8
    #        0   0.0% 100.0%        1  50.0% 00007f84002c14c8
    #        0   0.0% 100.0%        1  50.0% R_ReplConsole
    #        0   0.0% 100.0%        1  50.0% Rf_ReplIteration
    #        0   0.0% 100.0%        1  50.0% Rf_applyClosure
    #        0   0.0% 100.0%        1  50.0% Rf_eval
    #        0   0.0% 100.0%        1  50.0% __libc_start_main
    #        0   0.0% 100.0%        1  50.0% dlatrd_
    #        0   0.0% 100.0%        1  50.0% do_dotcall
    #        0   0.0% 100.0%        1  50.0% dsyev_
    #        0   0.0% 100.0%        1  50.0% dsytrd_
    #        0   0.0% 100.0%        1  50.0% frame_dummy
    #        0   0.0% 100.0%        1  50.0% run_Rmainloop 
    

    Regarding your updates, try changing the sourceCpp call to

    sourceCpp(
        'eigen-prof.cpp',
        verbose = TRUE,
        rebuild = TRUE,
        cacheDir = "/tmp/profdir"
    )
    

    where I'm using cacheDir = "/tmp/profdir" to override the default random directory (tempdir()) that sourceCpp uses to build the shared library; and rebuild = TRUE to ensure that a new .so is generated each run.

    Back in your shell, run these commands, substituting the appropriate file & directory names as needed:

    • R -f eigen-prof.R
    • so_file=$(find /tmp/profdir/ -iname '*.so' -printf "%T+\t%p\n" | sort -r | head -n1 | awk '{print $2}')
    • google-pprof --text $so_file eigen-prof.log

    This gives me

    # Using local file /tmp/profdir/sourceCpp-x86_64-pc-linux-gnu-0.12.7/sourcecpp_5dc5531d6f20/sourceCpp_4.so.
    # Using local file eigen-prof.log.
    # Total: 2 samples
    #        1  50.0%  50.0%        1  50.0% dsterf_
    #        1  50.0% 100.0%        1  50.0% dsymv_
    #        0   0.0% 100.0%        2 100.0% 0x00000000004007ca
    #        0   0.0% 100.0%        2 100.0% 0x00000000004007fa
    #        0   0.0% 100.0%        2 100.0% R_ReplConsole
    #        0   0.0% 100.0%        2 100.0% Rf_ReplIteration
    #        0   0.0% 100.0%        2 100.0% Rf_applyClosure
    #        0   0.0% 100.0%        2 100.0% Rf_eval
    #        0   0.0% 100.0%        2 100.0% __libc_start_main
    #        0   0.0% 100.0%        2 100.0% arma::auxlib::eig_sym
    #        0   0.0% 100.0%        1  50.0% dlatrd_
    #        0   0.0% 100.0%        2 100.0% do_dotcall
    #        0   0.0% 100.0%        2 100.0% dsyev_
    #        0   0.0% 100.0%        1  50.0% dsytrd_
    #        0   0.0% 100.0%        2 100.0% eig_sym (inline)
    #        0   0.0% 100.0%        2 100.0% getEigenValues
    #        0   0.0% 100.0%        2 100.0% run_Rmainloop
    #        0   0.0% 100.0%        2 100.0% sourceCpp_1_getEigenValues
    #        0   0.0% 100.0%        2 100.0% syev (inline)
    

    I threw the second step together on the fly so don't judge me on that; but it's just searching the directory you passed to cacheDir (/tmp/profdir in my case) for the most recently created (modified) .so file. You could also just grab the file name manually using Finder, etc. if your machine is lacking any of those programs.