运行编译的c++代码与Rcpp

r - Running compiled C++ code with Rcpp

本文关键字:Rcpp 代码 c++ 编译 运行      更新时间:2023-10-16

我一直在学习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代码来运行它。谢谢你在这方面的任何帮助,或者让我明白为什么我的建议是不可能的或不推荐的。

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

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

步骤1。我创建了一个名为"c:usersmmiller21myrpackages"的新文件夹,并将文件"logabs2.cpp"粘贴到该新文件夹中。文件'logabs2.cpp'是按照我的原始帖子中的描述创建的。

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

setwd('c:/users/mmiller21/myrpackages/')
library(Rcpp)
Rcpp.package.skeleton("logabs2", example_code = FALSE, cpp_files = c("logabs2.cpp"))

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

步骤3。我在R中安装了新的R包"logabs2",在DOS命令窗口中使用以下行:

C:Program FilesRR-3.0.1binx64>R CMD INSTALL -l c:usersmmiller21documentsrwin-library3.0 c:usersmmiller21myrpackageslogabs2

地点:

rcmd.exe文件的位置为:

C:Program FilesRR-3.0.1binx64>

我的计算机上安装的R包的位置是:

c:usersmmiller21documentsrwin-library3.0

和我的新R包在安装之前的位置是:

c:usersmmiller21myrpackages

DOS命令窗口中使用的语法是通过反复试验发现的,可能不理想。在某个时候,我在"C:Program FilesRR-3.0.1binx64>"中粘贴了一个"logabs2.cpp"的副本,但我认为这无关紧要。

步骤4。在安装了新的R包之后,我使用一个名为"新包使用"的R文件来运行它。r'在'c:/users/mmiller21/myrpackages/'文件夹(虽然我不认为这个文件夹很重要)。"新包用法"的内容。r":

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

输出为:

# [1] 1.609438 1.098612 0.000000 0.000000 1.098612 1.609438

这个文件没有经过我的请求就加载了Rcpp包。

在本例中,假设我正确操作,base 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 

虽然在这种情况下,基础R似乎更快或与*.dll文件一样快,但我毫不怀疑,在大多数情况下,使用*.dll文件与Rcpp将比基础R更快。

这是我第一次尝试创建一个R包或使用Rcpp,毫无疑问,我没有使用最有效的方法。此外,我为这篇文章中的任何排版错误道歉。

编辑

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

#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector logabs(NumericVector x) {
return log(abs(x));
}

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

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)

Base R仍然快一点,或者没有什么不同:

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

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

你说

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

,当且仅当您构建可执行时,这是正确的。这里,我们构建可动态加载的库,这些库根据操作系统的不同有不同的扩展名:windows的。dll, Linux的。so, OS x的。dynlib。

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

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

我们有Rcpp.package.skeleton你可能会发现有用的。

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