代码的行分析器是不是需要解析树,这是不是足够?
Posted
技术标签:
【中文标题】代码的行分析器是不是需要解析树,这是不是足够?【英文标题】:Does a line profiler for code require a parse tree and is that sufficient?代码的行分析器是否需要解析树,这是否足够? 【发布时间】:2011-09-05 15:12:14 【问题描述】:我正在尝试确定为一种语言编写行分析器所需的条件,例如那些可用于 Python 和 Matlab 的语言。
解释“行分析器”的一种天真的方法是假设可以在每一行周围插入时间记录,但行的定义取决于解析器如何处理空白,这只是第一个问题。似乎需要使用解析树并在各个节点周围插入时间。
这个结论正确吗?线路分析器是否需要解析树,是否只需要这些(除了时间记录)?
更新 1:提供赏金,因为问题仍未解决。
更新 2:这是众所周知的Python line profiler 的链接,以防它有助于回答这个问题。我还不能确定它相对于解析的行为。恐怕 Matlab 分析器的代码无法访问。
另请注意,可以说手动装饰输入代码将消除对解析树的需求,但这不是自动分析器。
更新 3:虽然这个问题与语言无关,但之所以出现这个问题,是因为我正在考虑为 R 创建这样的工具(除非它存在并且我还没有找到它)。
更新 4:关于使用行分析器与调用堆栈分析器 - this post 与使用调用堆栈分析器(在本例中为Rprof()
)举例说明了为什么使用调用堆栈而不是使用调用堆栈会很痛苦通过线路分析器直接分析事物。
【问题讨论】:
您知道 R 中的Rprof()
以及用于后处理的“R CMD Rprof”,以及至少两个用于汇总和可视化分析数据的 CRAN 包吗?如果你这样做了,你能详细说明你所寻找的东西有什么不同吗?
你好@Iterator。我知道堆栈采样的机制在 matlab 中,因为它可以被 Ctrl-C 中断,你可以用dbstack 显示堆栈。如果您需要构建分析器,您可以自动化manual method。无需解析。
@Dirk:是的,知道Rprof()
并且已经广泛使用它和其他包-see this Q。简而言之,Rprof
是调用堆栈分析器,而不是行分析器。堆栈分析器通常很有用且足够,但根据我的经验,行分析器在使用 R、Matlab 和 Python 等语言时通常更有帮助。与Rprof()
合作,我写了很多代码来分析它的输出,然后放弃并手动修饰代码;调用堆栈可能很刺激,但也很痛苦。
@Iterator:感谢您的更新。有了这个我会说这是错误的论坛。在 r-devel 上询问,或直接给蒂尔尼教授发电子邮件。
@Dirk:我手边没有笔记,但我正在考虑使用 Luke Tierney 的软件包之一来解析代码,然后装饰离散的代码块,因为我不确定简单地装饰每一行不会破坏代码。
【参考方案1】:
我会说是的,你需要一个解析树(和源代码)——否则你怎么知道什么是“行”和一个有效的语句?
一个实际的简化可能是“语句分析器”而不是“行分析器”。
在 R 中,解析树很容易获得:body(theFunction)
,因此在每个语句周围插入测量代码应该相当容易。再做一些工作,您可以将它插入到属于同一行的一组语句周围。
在 R 中,从文件加载的函数体通常还有一个属性 srcref
,它列出了每个“行”(实际上是每个语句)的源:
这是一个示例函数(放入“example.R”):
f <- function(x, y=3)
a <- 0; a <- 1 # Two statements on one line
a <- (x + 1) * # One statement on two lines
(y + 2)
a <- "foo
bar" # One string on two lines
然后在 R 中:
source("example.R")
dput(attr(body(theFunction), "srcref"))
打印此行/列信息:
list(structure(c(2L, 1L, 2L, 1L, 1L, 1L, 2L, 2L), srcfile = <environment>, class = "srcref"),
structure(c(3L, 2L, 3L, 7L, 9L, 14L, 3L, 3L), srcfile = <environment>, class = "srcref"),
structure(c(3L, 10L, 3L, 15L, 17L, 22L, 3L, 3L), srcfile = <environment>, class = "srcref"),
structure(c(4L, 2L, 5L, 15L, 9L, 15L, 4L, 5L), srcfile = <environment>, class = "srcref"),
structure(c(7L, 2L, 8L, 6L, 9L, 20L, 7L, 8L), srcfile = <environment>, class = "srcref"))
如您所见(每个结构中的最后两个数字是开始/结束行),表达式 a <- 0
和 a <- 1
映射到同一行...
祝你好运!
【讨论】:
我很同意,你说得对:知道语句的边界与知道行的边界是不一样的。例如if(condition)
和if(condition)\n
通常是相同的,但是在)
和
之间插入日志代码通常会影响行为。知道某些东西会影响流量控制似乎势在必行,因此是问题的“必要”部分。至于足够——我相信是这样——还有什么比解析器更足够的呢? :)【参考方案2】:
听起来你所说的线路分析器是指测量每条线路所花费的时间(即插桩)。 我希望你所说的时间是挂钟时间,因为在真正的大型软件中,如果你只看 CPU 时间,你会错过很多。
另一种方法是在挂钟时间进行堆栈采样,如Zoom 和LTProf 分析器。 由于堆栈示例的每一行都可以仅使用 map 或 pdb 文件本地化为一行代码,因此与调试器一样,无需解析或修改源代码。
一行代码所花费的时间百分比只是包含它的堆栈样本的百分比。 由于您在线路级别工作,因此无需区分独占(自身)时间和包含时间。 这是因为线路的活动时间百分比很重要,无论是调用另一个函数、调用盲系统函数还是调用微码。
查看百分比而不是绝对时间的优势在于,您无需担心应用程序会因采样本身或与其他进程的竞争而变慢,因为这些事情不会影响百分比非常好。
您也不必担心递归。 如果一行代码在递归函数中并且在样本中出现多次,那没关系。 它仍然算作只有一个包含该行的样本。 没关系的原因是,如果可以以某种方式使那行代码不花时间(例如通过删除它),那么样本就不会发生。 因此,包含该行的样本将从样本集中删除,并且程序的总时间将减少与删除的样本分数相同的数量。 这与递归无关。
您也不需要计算一行代码执行了多少次,因为对于定位您应该优化的代码而言,重要的数字是它处于活动状态的时间百分比。
这里是这些问题的more explanation。
【讨论】:
嘿迈克!我很高兴你回答 - 如果你没有找到这个我会很失望 - 我已经阅读了很多你的建议。 ;-) 你对时间安排和获得的信息是绝对正确的。我发现行(相对于堆栈)分析信息的地方是可操作单元是行或代码块。 LTProf 非常令人费解:它是如何在没有仪器的情况下获取这些信息的?我不认为可以使用调用堆栈并保证反向映射回代码(即从代码到调用堆栈的映射是满射的,但我认为它不是双射的)。 Zoom 也很有趣;不幸的是,我认为感兴趣的语言没有 DWARF 调试支持。所以,我认为这一切都始于解析树......你对墙和 CPU 时间也是正确的。我打算测量两者。 @Iterator:我认为 LTProf 的工作方式与 Zoom 一样。我一直都是手工完成的(你可能知道),但我也写过一次堆栈采样器。从根本上说,堆栈样本只是一个地址列表——PC,加上链上的每个返回地址。可以在映射文件中查找每个地址以获取其文件和行号(和功能)。最简单的实现就是保存样本,然后,为了查看,选择其中一个地址,查看它有多少样本,并将其显示为与文件行一起的百分比。然后你可以有一个蝴蝶视图等。为什么要解析? 我正在查看的语言通常不是编译的 - 它是解释的。当前选项只是一个带有调用堆栈的统计采样器(很像您所提倡的)。但是,当代码中的一行调用外部库中的大量活动时,日志会变得非常混乱。 @Iterator:好的,如果语言被解释,你可能不想要 CPU 调用堆栈。您可能需要解释器的调用堆栈,因为这将与用户代码相关。许多解释器允许随机时间 Ctrl-C 或某些信号,然后捕获堆栈并继续。 Here's a simple example in python.以上是关于代码的行分析器是不是需要解析树,这是不是足够?的主要内容,如果未能解决你的问题,请参考以下文章
jvisualvm 或 NetBeans 分析器是不是有可用的调用树视图?