11

我一直在阅读 Dirk Eddelbuettel 的Rcpp教程:

http://www.rinfinance.com/agenda/

我已经学会了如何将 C++ 文件保存在目录中并在 R 中调用和运行它。我正在运行的 C++ 文件称为“logabs2.ccp”,其内容直接来自 Dirk 的一张幻灯片:

#include <Rcpp.h>

using namespace Rcpp;

inline double f(double x) { return ::log(::fabs(x)); }

// [[Rcpp::export]]
std::vector<double> logabs2(std::vector<double> x) {
    std::transform(x.begin(), x.end(), x.begin(), f);
    return x;
}

我用这个 R 代码运行它:

library(Rcpp)
sourceCpp("c:/users/mmiller21/simple r programs/logabs2.cpp")
logabs2(seq(-5, 5, by=2))
# [1] 1.609438 1.098612 0.000000 0.000000 1.098612 1.609438

我正在 Windows 7 机器上从默认安装的 R GUI 中运行代码。我还安装了最新版本的Rtools. 上面的 R 代码运行起来似乎需要比较长的时间。我怀疑大部分时间都花在了编译 C++ 代码上,而一旦 C++ 代码编译完成,它就会运行得非常快。 Microbenchmark当然建议Rcpp减少计算时间。

直到现在我从未使用过 C++,但我知道当我编译 C 代码时会得到一个 *.exe 文件。我从一个名为logabs2.exe但找不到的文件中搜索了我的硬盘驱动器。我想知道如果logabs2.exe创建了文件,上述 C++ 代码是否会运行得更快。是否可以创建一个logabs2.exe文件并将其存储在某个文件夹中,然后在我想使用它时让 Rcpp 调用该文件?我不知道这是否有意义。如果我可以将 C++ 函数存储在 *.exe 文件中,那么也许我每次想将它与 Rcpp 一起使用时都不必编译该函数,那么 Rcpp 代码可能会更快。

抱歉,如果这个问题没有意义或重复。如果可以将 C++ 函数存储为 *.exe 文件,我希望有人会告诉我如何修改上面的 R 代码以运行它。感谢您对此提供的任何帮助,或者让我直接了解为什么我的建议是不可能或不推荐的。

我期待着看到德克的新书。

4

3 回答 3

6

感谢 user1981275、Dirk Eddelbuettel 和 Romain Francois 的回复。下面是我如何编译一个 C++ 文件并创建一个 *.dll,然后在 .dll 中调用并使用该 *.dll 文件R

第 1 步。我创建了一个名为“c:\users\mmiller21\myrpackages”的新文件夹,并将文件“logabs2.cpp”粘贴到该新文件夹中。文件“logabs2.cpp”是按照我原来的帖子中的描述创建的。

第 2 步。在新文件夹中,我使用我编写的名为“new package creation.r”R的文件创建了一个名为“logabs2”的新包。R'new package creation.r' 的内容是:

setwd('c:/users/mmiller21/myrpackages/')

library(Rcpp)

Rcpp.package.skeleton("logabs2", example_code = FALSE, cpp_files = c("logabs2.cpp"))

Rcpp.package.skeleton我在 Hadley Wickham 的一个网站上 找到了上述语法: https ://github.com/hadley/devtools/wiki/Rcpp

步骤 3. 我在 DOS 命令窗口中使用以下行安装了新R包“logabs2” :R

C:\Program Files\R\R-3.0.1\bin\x64>R CMD INSTALL -l c:\users\mmiller21\documents\r\win-library\3.0\ c:\users\mmiller21\myrpackages\logabs2

在哪里:

rcmd.exe 文件的位置是:

C:\Program Files\R\R-3.0.1\bin\x64>

R我电脑上安装包的位置是:

c:\users\mmiller21\documents\r\win-library\3.0\

R并且在安装之前我的新软件包的位置是:

c:\users\mmiller21\myrpackages\

DOS 命令窗口中使用的语法是通过反复试验发现的,可能并不理想。在某些时候,我在“C:\Program Files\R\R-3.0.1\bin\x64>”中粘贴了“logabs2.cpp”的副本,但我认为这并不重要。

第 4 步。安装新R包后,我使用R在“c:/users/mmiller21/myrpackages/”文件夹中名为“new package usage.r”的文件运行它(尽管我认为该文件夹并不重要)。'new package usage.r' 的内容是:

library(logabs2)
logabs2(seq(-5, 5, by=2))

输出是:

# [1] 1.609438 1.098612 0.000000 0.000000 1.098612 1.609438

Rcpp这个文件在没有我询问的情况下加载了包。

R在这种情况下,假设我正确地做到了这一点,基地会更快。

#> microbenchmark(logabs2(seq(-5, 5, by=2)), times = 100)
#Unit: microseconds
#                        expr    min     lq  median     uq     max neval
# logabs2(seq(-5, 5, by = 2)) 43.086 44.453 50.6075 69.756 190.803   100

#> microbenchmark(log(abs(seq(-5, 5, by=2))), times=100)
#Unit: microseconds
#                         expr    min     lq median    uq     max neval
# log(abs(seq(-5, 5, by = 2))) 38.298 38.982 39.666 40.35 173.023   100

但是,使用 dll 文件比调用外部 cpp 文件要快:

system.time(

cppFunction("
NumericVector logabs(NumericVector x) {
    return log(abs(x));
}
")

)

#   user  system elapsed 
#   0.06    0.08    5.85 

尽管在这种情况下,base R 似乎更快或与 *.dll 文件一样快,但我毫不怀疑在大多数情况下 使用 *.dll 文件Rcpp会比 base 更快。R

这是我第一次尝试创建 R 包或使用 Rcpp,毫无疑问我没有使用最有效的方法。另外,对于这篇文章中的任何印刷错误,我深表歉意。

编辑

在下面的评论中,我认为 Romain Francois 建议我将 *.cpp 文件修改为以下内容:

#include <Rcpp.h>
using namespace Rcpp;

// [[Rcpp::export]]

NumericVector logabs(NumericVector x) {
return log(abs(x));
}

并重新创建我R现在已经完成的包。R然后,我使用以下代码将 base与我的新包进行了比较:

library(logabs)

logabs(seq(-5, 5, by=2))
log(abs(seq(-5, 5, by=2)))

library(microbenchmark)

microbenchmark(logabs(seq(-5, 5, by=2)), log(abs(seq(-5, 5, by=2))), times = 100000)

BaseR仍然快一点或没有什么不同:

Unit: microseconds
                         expr    min     lq median     uq       max neval
   logabs(seq(-5, 5, by = 2)) 42.401 45.137 46.505 69.073 39754.598 1e+05
 log(abs(seq(-5, 5, by = 2))) 37.614 40.350 41.718 62.234  3422.133 1e+05

也许这是因为 baseR已经矢量化了。我怀疑功能更复杂的基础R会慢得多。或者也许我仍然没有使用最有效的方法,或者我只是在某个地方犯了错误。

于 2013-06-18T07:17:04.363 回答
5

你说

直到现在我从未使用过 C++,但我知道当我编译 C 代码时会得到一个 *.exe 文件

当且仅当您构建可执行文件时,这是真的。在这里,我们构建可动态加载的库,然后根据操作系统具有不同的扩展名:.dll 用于 Windoze,.so 用于 Linux,.dynlib 用于 OS X。

所以这里没有错,你只是有错误的假设。

于 2013-06-17T14:55:42.010 回答
3

如果您想获得一些可以保留的实体,那么您正在寻找的是一个R包。网上有很多资源可以学习如何制作它们(例如Hadley 的幻灯片)。

我们有Rcpp.package.skeleton你可能会觉得有用。

因此,该函数在安装包时编译一次,然后您就可以使用它。

于 2013-06-17T21:58:02.980 回答