如何在R中高效使用Rprof?

70
我想知道是否可能以类似于matlab的Profiler的方式从R-Code中获取配置文件。也就是说,了解哪些行号特别慢。
到目前为止,我所取得的成果并不令人满意。我使用Rprof制作了一个配置文件。使用summaryRprof,我得到了以下内容:
$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
...
并且。
$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
...
坦白地说,从这个输出中,我不知道我的瓶颈在哪里,因为(a)我经常使用data.frame,(b)我从未使用例如deparse。此外,[是什么?
所以我尝试了Hadley Wickham的profr,但是考虑到以下图表,它并没有更有用: alt text 有没有更方便的方法可以查看哪些行号和特定函数调用很慢? 或者,有没有一些文献应该参考?
任何提示都会受到赞赏。
编辑1: 根据Hadley的评论,我将在下面粘贴我的脚本代码和绘图的基本版本。但请注意,我的问题与此特定脚本无关。这只是我最近编写的一个随机脚本。我正在寻找一般的方法来查找瓶颈并加速R代码。
数据(x)看起来像这样:
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
脚本(附有简短的解释)如下所示:
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"))
最终数据如下:
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
基本的图形绘制: alt text 今天运行脚本还稍微改变了ggplot2图表(基本上只有标签),请看这里。

你可以尝试在 profr 中使用 plot 而不是 ggplot 吗?同时看到你的原始代码会很有用。 - hadley
3
我很疲倦地一再指出这一点。基于与gprof相同思想的分析器也有同样的缺陷。所有关于自身时间、函数而不是行、图形和测量等方面的东西,都只是同样无用的陈词滥调。有简单的替代方法:https://dev59.com/xHI-5IYBdhLWcg3wlpeW#1779343 - Mike Dunlavey
1
@hadely:看看我的修改。 @Mike:我明白你的意思,找问题和不测量某事基本上是无关的。这听起来正是我想要的。但这在R中是否有实现呢? - Henrik
@Henrik:有人刚给了我一票,让我回到这里。事实上,我使用过Rprof,但只是为了取样(在大间隔内),而不是“分析”它们。样本最终会保存在文件中,然后我只是查看它们。虽然它们不包含行号信息,但它们有效。如果函数A在两个地方调用函数B,我会改为让A调用B1和B2,然后这些家伙再调用B。这样我就可以知道调用来自A的哪个位置。虽然有点笨拙,但它完成了工作。 - Mike Dunlavey
4个回答

52

昨天警惕的读者们可能已经注意到一些与这个问题直接相关的有趣信息,这是从最新消息(R 3.0.0终于上线了)中获得的:

 
     
  • 通过Rprof()进行分析现在可以选择在语句级别而非仅在函数级别记录信息。
  •  

事实上,这个新特性回答了我的问题,我将展示如何解决它。


假设我们想比较向量化和预分配是否真的比传统的for循环和增量构建数据更好地计算诸如平均值之类的摘要统计量。相对愚蠢的代码如下:

# create big data frame:
n <- 1000
x <- data.frame(group = sample(letters[1:4], n, replace=TRUE), condition = sample(LETTERS[1:10], n, replace = TRUE), data = rnorm(n))

# reasonable operations:
marginal.means.1 <- aggregate(data ~ group + condition, data = x, FUN=mean)

# unreasonable operations:
marginal.means.2 <- marginal.means.1[NULL,]

row.counter <- 1
for (condition in levels(x$condition)) {
  for (group in levels(x$group)) {  
    tmp.value <- 0
    tmp.length <- 0
    for (c in 1:nrow(x)) {
      if ((x[c,"group"] == group) & (x[c,"condition"] == condition)) {
        tmp.value <- tmp.value + x[c,"data"]
        tmp.length <- tmp.length + 1
      }
    }
    marginal.means.2[row.counter,"group"] <- group 
    marginal.means.2[row.counter,"condition"] <- condition
    marginal.means.2[row.counter,"data"] <- tmp.value / tmp.length
    row.counter <- row.counter + 1
  }
}

# does it produce the same results?
all.equal(marginal.means.1, marginal.means.2)
为了使用 Rprof,我们需要对代码进行 parse,也就是将其保存在文件中,然后从那里调用。因此,我将其上传到 pastebin,但在本地文件中也完全可以工作。
现在,我们只需:
  • 创建一个配置文件并指定要保存行号的位置,
  • 使用惊人的组合 eval(parse(..., keep.source = TRUE)) 对代码进行源码评估(貌似臭名昭著的 fortune(106) 在这里不适用,因为我没有找到其他方法),
  • 停止分析,并指示我们希望基于行号输出结果。
代码如下:
Rprof("profile1.out", line.profiling=TRUE)
eval(parse(file = "http://pastebin.com/download.php?i=KjdkSVZq", keep.source=TRUE))
Rprof(NULL)

summaryRprof("profile1.out", lines = "show")

得到的结果是:

$by.self
                           self.time self.pct total.time total.pct
download.php?i=KjdkSVZq#17      8.04    64.11       8.04     64.11
<no location>                   4.38    34.93       4.38     34.93
download.php?i=KjdkSVZq#16      0.06     0.48       0.06      0.48
download.php?i=KjdkSVZq#18      0.02     0.16       0.02      0.16
download.php?i=KjdkSVZq#23      0.02     0.16       0.02      0.16
download.php?i=KjdkSVZq#6       0.02     0.16       0.02      0.16

$by.total
                           total.time total.pct self.time self.pct
download.php?i=KjdkSVZq#17       8.04     64.11      8.04    64.11
<no location>                    4.38     34.93      4.38    34.93
download.php?i=KjdkSVZq#16       0.06      0.48      0.06     0.48
download.php?i=KjdkSVZq#18       0.02      0.16      0.02     0.16
download.php?i=KjdkSVZq#23       0.02      0.16      0.02     0.16
download.php?i=KjdkSVZq#6        0.02      0.16      0.02     0.16

$by.line
                           self.time self.pct total.time total.pct
<no location>                   4.38    34.93       4.38     34.93
download.php?i=KjdkSVZq#6       0.02     0.16       0.02      0.16
download.php?i=KjdkSVZq#16      0.06     0.48       0.06      0.48
download.php?i=KjdkSVZq#17      8.04    64.11       8.04     64.11
download.php?i=KjdkSVZq#18      0.02     0.16       0.02      0.16
download.php?i=KjdkSVZq#23      0.02     0.16       0.02      0.16

$sample.interval
[1] 0.02

$sampling.time
[1] 12.54

查看源代码后发现问题在第17行愚蠢的if语句中的for循环。相比于使用向量化代码计算(第6行)几乎不需要时间。

我还没有尝试过任何图形输出,但到目前为止我已经非常满意所得到的结果。


6
为什么不使用source("http://pastebin.com/download.php?i=KjdkSVZq")而使用eval(parse(..., keep.source = TRUE)) - flodel
需要解析吗?我不能直接在Rprof行之间直接提供R表达式吗? - Avinash
1
@Avinash 不行,看看上面flodel的评论。你可以简单地引用它。如果其他版本也可以工作,你需要自己尝试一下。 - Henrik
我对类似问题的解决方案(在发布我的问题后,我找到了这个问题),也许也会有所帮助:如何解释profr :: profr的输出? - alexwhitworth
@Henrik,这两个问题略有不同。这一个是关于base::Rprof的,我的问题是关于profr::profr的...但如果您愿意,我可以在这里发布我的解决方案。 - alexwhitworth
显示剩余3条评论

11

更新:这个函数已经被重新编写以处理行号。它在 github 上这里

我编写了这个函数来解析来自 Rprof 的文件,并输出比 summaryRprof 更清晰结果的表格。它显示了函数的完整堆栈(如果 line.profiling=TRUE,则包括行号),以及它们对运行时间的相对贡献:

proftable <- function(file, lines=10) {
# require(plyr)
  interval <- as.numeric(strsplit(readLines(file, 1), "=")[[1L]][2L])/1e+06
  profdata <- read.table(file, header=FALSE, sep=" ", comment.char = "",
                         colClasses="character", skip=1, fill=TRUE,
                         na.strings="")
  filelines <- grep("#File", profdata[,1])
  files <- aaply(as.matrix(profdata[filelines,]), 1, function(x) {
                        paste(na.omit(x), collapse = " ") })
  profdata <- profdata[-filelines,]
  total.time <- interval*nrow(profdata)
  profdata <- as.matrix(profdata[,ncol(profdata):1])
  profdata <- aaply(profdata, 1, function(x) {
                      c(x[(sum(is.na(x))+1):length(x)],
                        x[seq(from=1,by=1,length=sum(is.na(x)))])
              })
  stringtable <- table(apply(profdata, 1, paste, collapse=" "))
  uniquerows <- strsplit(names(stringtable), " ")
  uniquerows <- llply(uniquerows, function(x) replace(x, which(x=="NA"), NA))
  dimnames(stringtable) <- NULL
  stacktable <- ldply(uniquerows, function(x) x)
  stringtable <- stringtable/sum(stringtable)*100
  stacktable <- data.frame(PctTime=stringtable[], stacktable)
  stacktable <- stacktable[order(stringtable, decreasing=TRUE),]
  rownames(stacktable) <- NULL
  stacktable <- head(stacktable, lines)
  na.cols <- which(sapply(stacktable, function(x) all(is.na(x))))
  stacktable <- stacktable[-na.cols]
  parent.cols <- which(sapply(stacktable, function(x) length(unique(x)))==1)
  parent.call <- paste0(paste(stacktable[1,parent.cols], collapse = " > ")," >")
  stacktable <- stacktable[,-parent.cols]
  calls <- aaply(as.matrix(stacktable[2:ncol(stacktable)]), 1, function(x) {
                   paste(na.omit(x), collapse= " > ")
                     })
  stacktable <- data.frame(PctTime=stacktable$PctTime, Call=calls)
  frac <- sum(stacktable$PctTime)
  attr(stacktable, "total.time") <- total.time
  attr(stacktable, "parent.call") <- parent.call
  attr(stacktable, "files") <- files
  attr(stacktable, "total.pct.time") <- frac
  cat("\n")
  print(stacktable, row.names=FALSE, right=FALSE, digits=3)
  cat("\n")
  cat(paste(files, collapse="\n"))
  cat("\n")
  cat(paste("\nParent Call:", parent.call))
  cat(paste("\n\nTotal Time:", total.time, "seconds\n"))
  cat(paste0("Percent of run time represented: ", format(frac, digits=3)), "%")

  invisible(stacktable)
}

在 Henrik 的示例文件上运行,我得到了这个结果:

> Rprof("profile1.out", line.profiling=TRUE)
> source("http://pastebin.com/download.php?i=KjdkSVZq")
> Rprof(NULL)
> proftable("profile1.out", lines=10)

 PctTime Call                                                      
 20.47   1#17 > [ > 1#17 > [.data.frame                            
  9.73   1#17 > [ > 1#17 > [.data.frame > [ > [.factor             
  8.72   1#17 > [ > 1#17 > [.data.frame > [ > [.factor > NextMethod
  8.39   == > Ops.factor                                           
  5.37   ==                                                        
  5.03   == > Ops.factor > noNA.levels > levels                    
  4.70   == > Ops.factor > NextMethod                              
  4.03   1#17 > [ > 1#17 > [.data.frame > [ > [.factor > levels    
  4.03   1#17 > [ > 1#17 > [.data.frame > dim                      
  3.36   1#17 > [ > 1#17 > [.data.frame > length                   

#File 1: http://pastebin.com/download.php?i=KjdkSVZq

Parent Call: source > withVisible > eval > eval >

Total Time: 5.96 seconds
Percent of run time represented: 73.8 %
请注意,“父调用”适用于表格上表示的所有堆栈。这在您的IDE或其他调用代码的包装了一堆函数时非常有用。

看起来不错。但是有没有可能获取我们所在的行信息(即从哪一行调用了堆栈)? - Henrik
这是一些好消息。你应该知道,当前实现中仍然存在一个错误(但可能不在R devel中)。 - Henrik
我已经重写了这个函数,以处理行号,并提高长堆栈的可读性。在此处获取代码:https://github.com/noamross/noamtools/blob/master/R/proftable.R - Noam Ross
  • @Noam:刚看到你的回答。我也用过rprof,但我没有进行后处理,我只是随机选择了一些原始堆栈样本进行查看。我的版本在堆栈样本中没有行号(或者可能有,但我没有意识到)。无论如何,它完成了工作。
- Mike Dunlavey
有一个拼写错误:“aaply” - 不能仅仅为了这个而编辑它,但这会导致复制粘贴无法正常工作 - 最好修复一下。 - naught101
2
@naught101 这不是一个错误。实际上,它是plyr::aaply函数。您可以在函数顶部取消注释require(plyr),或者安装包含此函数的软件包,网址为http://github.com/noamross/noamtools。 - Noam Ross

3
一个不同的解决方案来自于一个不同的问题:如何有效地使用 R 中的 library(profr)
例如:
install.packages("profr")
devtools::install_github("alexwhitworth/imputation")

x <- matrix(rnorm(1000), 100)
x[x>1] <- NA
library(imputation)
library(profr)
a <- profr(kNN_impute(x, k=5, q=2), interval= 0.005)

在这里,绘图似乎对解决问题没有什么帮助(例如plot(a))。但数据结构本身似乎提供了一个解决方案:

R> head(a, 10)
   level g_id t_id                f start   end n  leaf  time     source
9      1    1    1       kNN_impute 0.005 0.190 1 FALSE 0.185 imputation
10     2    1    1        var_tests 0.005 0.010 1 FALSE 0.005       <NA>
11     2    2    1            apply 0.010 0.190 1 FALSE 0.180       base
12     3    1    1         var.test 0.005 0.010 1 FALSE 0.005      stats
13     3    2    1              FUN 0.010 0.110 1 FALSE 0.100       <NA>
14     3    2    2              FUN 0.115 0.190 1 FALSE 0.075       <NA>
15     4    1    1 var.test.default 0.005 0.010 1 FALSE 0.005       <NA>
16     4    2    1           sapply 0.010 0.040 1 FALSE 0.030       base
17     4    3    1    dist_q.matrix 0.040 0.045 1 FALSE 0.005 imputation
18     4    4    1           sapply 0.045 0.075 1 FALSE 0.030       base

单次迭代解决方案:

这个数据结构建议使用tapply来总结数据。对于profr::profr的单次运行,可以很简单地完成此操作。

t <- tapply(a$time, paste(a$source, a$f, sep= "::"), sum)
t[order(t)] # time / function
R> round(t[order(t)] / sum(t), 4) # percentage of total time / function

base::!                    base::%in%                       base::|           base::anyDuplicated 
                       0.0015                        0.0015                        0.0015                        0.0015 
                      base::c                 base::deparse                     base::get                   base::match 
                       0.0015                        0.0015                        0.0015                        0.0015 
                   base::mget                     base::min                       base::t                   methods::el 
                       0.0015                        0.0015                        0.0015                        0.0015 
          methods::getGeneric        NA::.findMethodInTable               NA::.getGeneric      NA::.getGenericFromCache 
                       0.0015                        0.0015                        0.0015                        0.0015 
NA::.getGenericFromCacheTable                   NA::.identC             NA::.newSignature        NA::.quickCoerceSelect 
                       0.0015                        0.0015                        0.0015                        0.0015 
                NA::.sigLabel          NA::var.test.default                 NA::var_tests               stats::var.test 
                       0.0015                        0.0015                        0.0015                        0.0015 
                  base::paste                 methods::as<-     NA::.findInheritedMethods        NA::.getClassFromCache 
                       0.0030                        0.0030                        0.0030                        0.0030 
               NA::doTryCatch              NA::tryCatchList               NA::tryCatchOne               base::crossprod 
                       0.0030                        0.0030                        0.0030                        0.0045 
                    base::try                base::tryCatch          methods::getClassDef      methods::possibleExtends 
                       0.0045                        0.0045                        0.0045                        0.0045 
          methods::loadMethod                   methods::is     imputation::dist_q.matrix          methods::validObject 
                       0.0075                        0.0090                        0.0120                        0.0136 
       NA::.findNextFromTable        methods::addNextMethod               NA::.nextMethod                  base::lapply 
                       0.0166                        0.0346                        0.0361                        0.0392 
                 base::sapply     imputation::impute_fn_knn                  methods::new        imputation::kNN_impute 
                       0.0392                        0.0392                        0.0437                        0.0557 
      methods::callNextMethod      kernlab::as.kernelMatrix                   base::apply         kernlab::kernelMatrix 
                       0.0572                        0.0633                        0.0663                        0.0753 
          methods::initialize                       NA::FUN         base::standardGeneric 
                       0.0798                        0.0994                        0.1325 

通过这个,我可以看到最耗时的是kernlab::kernelMatrix以及由于R中S4类和泛型而产生的开销。

首选:

考虑到采样过程的随机性,我更倾向于使用平均值来获得更稳健的时间分布图:

prof_list <- replicate(100, profr(kNN_impute(x, k=5, q=2), 
    interval= 0.005), simplify = FALSE)

fun_timing <- vector("list", length= 100)
for (i in 1:100) {
  fun_timing[[i]] <- tapply(prof_list[[i]]$time, paste(prof_list[[i]]$source, prof_list[[i]]$f, sep= "::"), sum)
}

# Here is where the stochastic nature of the profiler complicates things.
# Because of randomness, each replication may have slightly different 
# functions called during profiling
sapply(fun_timing, function(x) {length(names(x))})

# we can also see some clearly odd replications (at least in my attempt)
> sapply(fun_timing, sum)
[1]    2.820    5.605    2.325    2.895    3.195    2.695    2.495    2.315    2.005    2.475    4.110    2.705    2.180    2.760
 [15] 3130.240    3.435    7.675    7.155    5.205    3.760    7.335    7.545    8.155    8.175    6.965    5.820    8.760    7.345
 [29]    9.815    7.965    6.370    4.900    5.720    4.530    6.220    3.345    4.055    3.170    3.725    7.780    7.090    7.670
 [43]    5.400    7.635    7.125    6.905    6.545    6.855    7.185    7.610    2.965    3.865    3.875    3.480    7.770    7.055
 [57]    8.870    8.940   10.130    9.730    5.205    5.645    3.045    2.535    2.675    2.695    2.730    2.555    2.675    2.270
 [71]    9.515    4.700    7.270    2.950    6.630    8.370    9.070    7.950    3.250    4.405    3.475    6.420 2948.265    3.470
 [85]    3.320    3.640    2.855    3.315    2.560    2.355    2.300    2.685    2.855    2.540    2.480    2.570    3.345    2.145
 [99]    2.620    3.650

删除异常复制并转换为 data.frame

fun_timing <- fun_timing[-c(15,83)]
fun_timing2 <- lapply(fun_timing, function(x) {
  ret <- data.frame(fun= names(x), time= x)
  dimnames(ret)[[1]] <- 1:nrow(ret)
  return(ret)
})

合并复制(很可能会更快),并检查结果:
# function for merging DF's in a list
merge_recursive <- function(list, ...) {
  n <- length(list)
  df <- data.frame(list[[1]])
  for (i in 2:n) {
    df <- merge(df, list[[i]], ... = ...)
  }
  return(df)
}

# merge
fun_time <- merge_recursive(fun_timing2, by= "fun", all= FALSE)
# do some munging
fun_time2 <- data.frame(fun=fun_time[,1], avg_time=apply(fun_time[,-1], 1, mean, na.rm=T))
fun_time2$avg_pct <- fun_time2$avg_time / sum(fun_time2$avg_time)
fun_time2 <- fun_time2[order(fun_time2$avg_time, decreasing=TRUE),]
# examine results
R> head(fun_time2, 15)
                         fun  avg_time    avg_pct
4      base::standardGeneric 0.6760714 0.14745123
20                   NA::FUN 0.4666327 0.10177262
12       methods::initialize 0.4488776 0.09790023
9      kernlab::kernelMatrix 0.3522449 0.07682464
8   kernlab::as.kernelMatrix 0.3215816 0.07013698
11   methods::callNextMethod 0.2986224 0.06512958
1                base::apply 0.2893367 0.06310437
7     imputation::kNN_impute 0.2433163 0.05306731
14              methods::new 0.2309184 0.05036331
10    methods::addNextMethod 0.2012245 0.04388708
3               base::sapply 0.1875000 0.04089377
2               base::lapply 0.1865306 0.04068234
6  imputation::impute_fn_knn 0.1827551 0.03985890
19           NA::.nextMethod 0.1790816 0.03905772
18    NA::.findNextFromTable 0.1003571 0.02188790

结果

从结果来看,与单个案例类似但更为稳健的情况已经浮现。也就是说,R 有很多开销,而且 library(kernlab) 也使我速度变慢。值得注意的是,由于 kernlab 是在 S4 中实现的,因此 R 中的开销与 S4 类相对应,因为 S4 类比 S3 类慢得多。

我还想指出,我个人认为这个清理后的版本可能会成为 profr 的一种有用的汇总方法。虽然我很想看到别人的建议!


3
我目前在这里没有安装R,但在SPlus中,您可以使用Escape键中断执行,然后执行traceback(),它将显示调用堆栈。那应该使您能够使用这个方便的方法以下是一些原因,为什么基于与gprof相同概念构建的工具不太适合定位性能问题。

看起来那个问题已经被删除了。你知道关于这个话题的其他信息来源吗(就像你在上面的评论中所说的那些“陈词滥调”)? - naught101
1
@naught101:这篇帖子并没有消失,你只需要足够的声望就可以看到。我是这个主题上的主要“喷火者”,但我真的在努力不这样做。此处的另一个链接“这个方便的方法”详细说明了而不会过度喷火。简而言之,任何瓶颈值得修复时,任何分析器都无法像人类那样快速找到,并且修复每个瓶颈使其他瓶颈更容易被找到,因此您可以不断前进。“CPU分析”不会捕获IO。递归不是问题。测量的准确性不重要,也不重要“自身时间”,调用计数等等。 - Mike Dunlavey
听起来很有用,但是当涉及到分析和相关活动时,我有点新手。如果有安装R的人能够将这个答案翻译成我可以在R中使用的方法,那就太好了。 - naught101
2
@naught101:运行rprof(您可能需要稍微查找一下文档)。当我运行它时,我将采样率设置得非常低,这样我就不会得到大量的样本。它会生成一个堆栈样本的文本文件。我所做的就是看看那个文件。如果你在10个堆栈样本中看到它在5个样本上执行某些操作,那么这意味着如果你能加速它正在执行的操作,你可能可以节省大约50%的时间,多少有点不确定。这是一个很大的节省。 - Mike Dunlavey
这篇文章并没有消失:https://web.archive.org/web/20130817143313/https://dev59.com/xHI-5IYBdhLWcg3wlpeW#1779343 - leogama

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接