如何在R中高效使用Rprof?
我想知道是否可以通过类似于 matlab
的 Profiler 的方式从 R-Code 获取配置文件。也就是说,要知道哪些行号是特别慢的。
到目前为止,我所取得的成绩并不令人满意。我使用 Rprof 制作了一个配置文件。使用 summaryRprof
我得到类似以下内容:
<前><代码>$by.self self.time self.pct 总计.time 总计.pct [.data.frame 0.72 10.1 1.84 25.8 继承 0.50 7.0 1.10 15.4 数据帧 0.48 6.7 4.86 68.3 唯一默认 0.44 6.2 0.48 6.7 稀疏 0.36 5.1 1.18 16.6 结合 0.30 4.2 2.22 31.2 匹配 0.28 3.9 1.38 19.4 [<-.系数 0.28 3.9 0.56 7.9 水平 0.26 3.7 0.34 4.8 下一步方法 0.22 3.1 0.82 11.5 ...
和
$by.total 总.时间 总.pct 自我.时间 自我.pct 数据帧 4.86 68.3 0.48 6.7 绑定 2.22 31.2 0.30 4.2 调用 2.22 31.2 0.00 0.0 [ 1.98 27.8 0.16 2.2 [.data.frame 1.84 25.8 0.72 10.1 匹配 1.38 19.4 0.28 3.9 %% 1.26 17.7 0.14 2.0 系数 1.20 16.9 0.10 1.4 稀疏 1.18 16.6 0.36 5.1 ...
老实说,从这个输出中我不知道我的瓶颈在哪里,因为(a)我经常使用 data.frame
并且(b)我从不使用例如 deparse
。此外,什么是[
?
所以我尝试了 Hadley Wickham 的 profr
,但考虑到下图,它并没有任何用处:
是否有更方便的方法来查看哪些行号和特定函数调用速度慢?
或者,是否有一些我应该查阅的文献?
任何提示表示赞赏。
编辑1:
根据哈德利的评论,我将粘贴下面的脚本代码和绘图的基本图形版本。但请注意,我的问题与这个特定的脚本无关。这只是我最近写的一个随机脚本。 我正在寻找一种通用方法来查找瓶颈并加速 R
代码。
数据 (x
) 如下所示:
键入单词响应 N 分类 classN 摘要愤怒苦涩 1 3a 3a 摘要愤怒控制 1 1a 1a 摘要愤怒父亲 1 3a 3a 摘要愤怒脸红了 1 3a 3a 抽象愤怒愤怒 1 1c 1c 抽象愤怒帽子 1 3a 3a 摘要愤怒帮助 1 3a 3a 抽象愤怒疯狂 13 3a 3a 摘要 愤怒管理 2 1a 1a ...直到第 1700 行
脚本(带有简短的解释)是这样的:
Rprof("profile1.out") # 生成一个新的数据集,其中 x 的每一行包含 x$N 次 y <- 向量('列表',长度(x[,1])) for (i in 1:length(x[,1])) { y[[i]] <- data.frame(rep(x[i,1],x[i,"N"]),rep(x[i,2],x[i,"N"]) ,rep(x[i,3],x[i,"N"]),rep(x[i,4],x[i,"N"]),rep(x[i,5],x[ i,"N"]),rep(x[i,6],x[i,"N"])) } 所有 <- do.call('rbind',y) 列名(全部)<- 列名(x) # 从单词 x 类表中创建一个数据框 table_all <- 表(全部$word,全部$classN) dataf.all <- as.data.frame(table_all[,1:length(table_all[1,])]) dataf.all$words <- as.factor(rownames(dataf.all)) dataf.all$type <-“否” # 获取单词的类型。 单词 <- 级别(dataf.all$words) for (i in 1:length(words)) { dataf.all$type[i] <- as.character(all[pmatch(words[i],all$word),"type"]) } dataf.all$type <- as.factor(dataf.all$type) dataf.all$typeN <- as.numeric(dataf.all$type) # 聚合响应类别 dataf.all$c1 <- apply(dataf.all[,c("1a","1b","1c","1d","1e","1f")],1,sum) dataf.all$c2 <- apply(dataf.all[,c("2a","2b","2c")],1,sum) dataf.all$c3 <- apply(dataf.all[,c("3a","3b")],1,sum) Rprof(空) 图书馆(教授) ggplot.profr(parse_rprof("profile1.out"))
最终数据如下所示:
1a 1b 1c 1d 1e 1f 2a 2b 2c 3a 3b pa 字类型 typeN c1 c2 c3 pa 3 0 8 0 0 0 0 0 0 24 0 0 愤怒 摘要 1 11 0 24 0 6 0 4 0 1 0 0 11 0 13 0 0 焦虑 摘要 1 11 11 13 0 2 11 1 0 0 0 0 4 0 17 0 0 态度 摘要 1 14 4 17 0 9 18 0 0 0 0 0 0 0 0 8 0 桶 混凝土 2 27 0 8 0 0 1 18 0 0 0 0 4 0 12 0 0 信念 摘要 1 19 4 12 0
基本图:
I would like to know if it is possible to get a profile from R
-Code in a way that is similar to matlab
's Profiler. That is, to get to know which line numbers are the one's that are especially slow.
What I acchieved so far is somehow not satisfactory. I used Rprof
to make me a profile file. Using summaryRprof
I get something like the following:
$by.self self.time self.pct total.time total.pct [.data.frame 0.72 10.1 1.84 25.8 inherits 0.50 7.0 1.10 15.4 data.frame 0.48 6.7 4.86 68.3 unique.default 0.44 6.2 0.48 6.7 deparse 0.36 5.1 1.18 16.6 rbind 0.30 4.2 2.22 31.2 match 0.28 3.9 1.38 19.4 [<-.factor 0.28 3.9 0.56 7.9 levels 0.26 3.7 0.34 4.8 NextMethod 0.22 3.1 0.82 11.5 ...
and
$by.total total.time total.pct self.time self.pct data.frame 4.86 68.3 0.48 6.7 rbind 2.22 31.2 0.30 4.2 do.call 2.22 31.2 0.00 0.0 [ 1.98 27.8 0.16 2.2 [.data.frame 1.84 25.8 0.72 10.1 match 1.38 19.4 0.28 3.9 %in% 1.26 17.7 0.14 2.0 is.factor 1.20 16.9 0.10 1.4 deparse 1.18 16.6 0.36 5.1 ...
To be honest, from this output I don't get where my bottlenecks are because (a) I use data.frame
pretty often and (b) I never use e.g., deparse
. Furthermore, what is [
?
So I tried Hadley Wickham's profr
, but it was not any more useful considering the following graph:
Is there a more convenient way to see which line numbers and particular function calls are slow?
Or, is there some literature that I should consult?
Any hints appreciated.
EDIT 1:
Based on Hadley's comment I will paste the code of my script below and the base graph version of the plot. But note, that my question is not related to this specific script. It is just a random script that I recently wrote. I am looking for a general way of how to find bottlenecks and speed up R
-code.
The data (x
) looks like this:
type word response N Classification classN Abstract ANGER bitter 1 3a 3a Abstract ANGER control 1 1a 1a Abstract ANGER father 1 3a 3a Abstract ANGER flushed 1 3a 3a Abstract ANGER fury 1 1c 1c Abstract ANGER hat 1 3a 3a Abstract ANGER help 1 3a 3a Abstract ANGER mad 13 3a 3a Abstract ANGER management 2 1a 1a ... until row 1700
The script (with short explanations) is this:
Rprof("profile1.out") # A new dataset is produced with each line of x contained x$N times y <- vector('list',length(x[,1])) for (i in 1:length(x[,1])) { y[[i]] <- data.frame(rep(x[i,1],x[i,"N"]),rep(x[i,2],x[i,"N"]),rep(x[i,3],x[i,"N"]),rep(x[i,4],x[i,"N"]),rep(x[i,5],x[i,"N"]),rep(x[i,6],x[i,"N"])) } all <- do.call('rbind',y) colnames(all) <- colnames(x) # create a dataframe out of a word x class table table_all <- table(all$word,all$classN) dataf.all <- as.data.frame(table_all[,1:length(table_all[1,])]) dataf.all$words <- as.factor(rownames(dataf.all)) dataf.all$type <- "no" # get type of the word. words <- levels(dataf.all$words) for (i in 1:length(words)) { dataf.all$type[i] <- as.character(all[pmatch(words[i],all$word),"type"]) } dataf.all$type <- as.factor(dataf.all$type) dataf.all$typeN <- as.numeric(dataf.all$type) # aggregate response categories dataf.all$c1 <- apply(dataf.all[,c("1a","1b","1c","1d","1e","1f")],1,sum) dataf.all$c2 <- apply(dataf.all[,c("2a","2b","2c")],1,sum) dataf.all$c3 <- apply(dataf.all[,c("3a","3b")],1,sum) Rprof(NULL) library(profr) ggplot.profr(parse_rprof("profile1.out"))
Final data looks like this:
1a 1b 1c 1d 1e 1f 2a 2b 2c 3a 3b pa words type typeN c1 c2 c3 pa 3 0 8 0 0 0 0 0 0 24 0 0 ANGER Abstract 1 11 0 24 0 6 0 4 0 1 0 0 11 0 13 0 0 ANXIETY Abstract 1 11 11 13 0 2 11 1 0 0 0 0 4 0 17 0 0 ATTITUDE Abstract 1 14 4 17 0 9 18 0 0 0 0 0 0 0 0 8 0 BARREL Concrete 2 27 0 8 0 0 1 18 0 0 0 0 4 0 12 0 0 BELIEF Abstract 1 19 4 12 0
The base graph plot:
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
提醒读者昨天的突发新闻(
R 3.0.0
终于out)可能已经注意到一些与这个问题直接相关的有趣的事情:事实上,这个新功能回答了我的问题,我将展示如何解决。
比方说,我们想要比较向量化和预分配在计算汇总统计(例如平均值)时是否真的比旧的 for 循环和增量数据构建更好。相对愚蠢的代码如下:
要将此代码与 Rprof 一起使用,我们需要对其进行解析。也就是说,它需要保存在一个文件中,然后从那里调用。因此,我将其上传到 pastebin,但它与本地文件的工作原理完全相同。
现在,我们
eval(parse(..., keep.source = TRUE))
(看似臭名昭著的fortune(106)
在这里不适用,因为我还没有找到其他方法)代码是:
给出:
检查 源代码 告诉我们有问题的行 (#17) 确实是愚蠢的
if< /code>-for 循环中的语句。与使用矢量化代码(第 6 行)基本上没有时间计算相同的结果相比。
我还没有尝试过任何图形输出,但到目前为止我所得到的结果已经给我留下了深刻的印象。
Alert readers of yesterdays breaking news (
R 3.0.0
is finally out) may have noticed something interesting that is directly relevant to this question:And indeed, this new feature answers my question and I will show how.
Let's say, we want to compare whether vectorizing and pre-allocating are really better than good old for-loops and incremental building of data in calculating a summary statistic such as the mean. The, relatively stupid, code is the following:
To use this code with
Rprof
, we need toparse
it. That is, it needs to be saved in a file and then called from there. Hence, I uploaded it to pastebin, but it works exactly the same with local files.Now, we
eval(parse(..., keep.source = TRUE))
(seemingly the infamousfortune(106)
does not apply here, as I haven't found another way)The code is:
Which gives:
Checking the source code tells us that the problematic line (#17) is indeed the stupid
if
-statement in the for-loop. Compared with basically no time for calculating the same using vectorized code (line #6).I haven't tried it with any graphical output, but I am already very impressed by what I got so far.
更新:此函数已被重写以处理行号。它位于 github 此处。
我编写了这个函数来解析 Rprof 中的文件,并输出一个比 summaryRprof 更清晰的结果表。它显示完整的函数堆栈(如果
line.profiling=TRUE
则显示行号),以及它们对运行时间的相对贡献:在 Henrik 的示例文件上运行此文件,我得到以下结果:
请注意,“ “父调用”适用于表中表示的所有堆栈。当您的 IDE 或任何调用您的代码将其包装在一堆函数中时,这非常有用。
Update: This function has been re-written to deal with line numbers. It's on github here.
I wrote this function to parse the file from
Rprof
and output a table of somewhat clearer results thansummaryRprof
. It displays the full stack of functions (and line numbers ifline.profiling=TRUE
), and their relative contribution to run time:Running this on the Henrik's example file, I get this:
Note that the "Parent Call" applies to all the stacks represented on the table. This makes is useful when your IDE or whatever calls your code wraps it in a bunch of functions.
我目前在这里卸载了 R,但在 SPlus 中,您可以使用 Escape 键中断执行,然后执行
traceback()
,这将显示调用堆栈。这应该使您能够使用这个方便的方法。以下是为什么工具基于与gprof<相同的概念构建的一些原因/strong> 不太擅长定位性能问题。
I currently have R uninstalled here, but in SPlus you can interrupt the execution with the Escape key, and then do
traceback()
, which will show you the call stack. That should enable you to use this handy method.Here are some reasons why tools built on the same concepts as gprof are not very good at locating performance problems.
不同的解决方案来自不同的问题:如何有效地使用
R 中的库(profr)
:例如:(
至少对我来说),这些图在这里根本没有帮助(例如
plot(a)
)。但数据结构本身似乎确实提出了一个解决方案:单次迭代解决方案:
即数据结构建议使用
tapply
来汇总数据。对于单次运行profr::profr
来说,这可以非常简单地完成。从这里,我可以看到,最大的时间用户是
kernlab::kernelMatrix
以及kernlab::kernelMatrix
的开销R 用于 S4 类和泛型。首选:
我注意到,考虑到采样过程的随机性,我更喜欢使用平均值来获得更可靠的时间概况:
删除不寻常的重复并转换为
data.frame
s:合并复制(几乎肯定可以更快)并检查结果:
结果
从结果中,出现了与单个案例类似但更可靠的情况。也就是说,R 产生了大量开销,而且
library(kernlab)
也拖慢了我的速度。值得注意的是,由于kernlab
是在 S4 中实现的,因此 R 中的开销是相关的,因为 S4 类比 S3 类慢得多。我还要指出的是,我个人的观点是,它的清理版本可能是一个有用的拉取请求,作为
A different solution comes from a different question: how to effectively use
library(profr)
in R:For example:
It doesn't seem (to me at least), like the plots are at all helpful here (eg
plot(a)
). But the data structure itself does seem to suggest a solution:Single iteration solution:
That is the data structure suggests the use of
tapply
to summarize the data. This can be done quite simply for a single run ofprofr::profr
From this, I can see that the biggest time users are
kernlab::kernelMatrix
and the overhead from R for S4 classes and generics.Preferred:
I note that, given the stochastic nature of the sampling process, I prefer to use averages to get a more robust picture of the time profile:
Removing the unusual replications and converting to
data.frame
s:Merge replications (almost certainly could be faster) and examine results:
Results
From the results, a similar but more robust picture emerges as with a single case. Namely, there is a lot of overhead from R and also that
library(kernlab)
is slowing me down. Of note, sincekernlab
is implemented in S4, the overhead in R is related since S4 classes are substantially slower than S3 classes.I'd also note that my personal opinion is that a cleaned up version of this might be a useful pull request as a summary method for profr. Although I'd be interested to see others' suggestions!