java,时间单独的if语句和循环而不是带有探查器的方法

Posted

技术标签:

【中文标题】java,时间单独的if语句和循环而不是带有探查器的方法【英文标题】:java, time individual if statements and loops rather than methods with profiler 【发布时间】:2015-08-03 01:38:45 【问题描述】:

我使用 VisualVM 之类的工具来分析我的方法,以找到慢速方法来返工以使它们更快,但我似乎找不到任何分析器可以对方法内的单个循环或代码块进行计时。

现在我不得不求助于手动计时>

long sTime = System.currentTimeMillis();
//my Code here
System.out.println("time=" + ((System.currentTimeMillis() - sTime)));

但这真的很乏味,因为我必须一直为每个循环/if/codeblock 手动添加和删除计时器,以获得细粒度的计时数据。我想知道是否有任何工具可以分析代码块或某种 Eclipse 插件可以自动添加删除计时器的过程。我使用过代码模板,但它远非完美。

我敢肯定,我不是唯一一个想要更优雅的细粒度分析解决方案的人。

【问题讨论】:

您正在构建什么样的项目?这是控制台还是 Web 应用程序? 把有问题的循环/块放在他们自己的方法中? @ScottHunter 我认为他不想那么有侵略性。 @Tim Biegeleisen 它各不相同,有时是独立库,有时是服务器端代码或重复分配的批处理作业 【参考方案1】:

您说(实际上)您想了解如何使您的代码运行得更快。 这是大多数人想要做的。 有一个根本不需要分析器的简单方法。

如果您的代码在做一些不需要做的事情并且您摆脱了那件事,您的代码只能在相同的硬件上运行得更快。没有其他办法。 删除它(我们称之为 K)将节省一些时间。假设是 30%。

这意味着,如果您有办法获取随机时间堆栈样本,例如使用 jstack 或只是在调试器中暂停它并显示堆栈(可能还有一些数据),那么该样本至少在 K 期间有 30% 的机会发生。“至少”因为它实际上可能会花费 50% 的时间,而修复只节省 30%。 如果您这样做 10 次,您可能会看到 K 大约 3 次(或更多)。

这样做 10 次。 每个堆栈示例都会向您展示堆栈上的每个函数和代码行,如果您阅读它,您将完全了解它在做什么以及为什么在那个时刻。 在那里寻找任何可以做得更好的东西,在堆栈的任何级别。 如果您发现某件事可以做得更好,并且您在多个样本上看到它,那么您就找到了 K。 事实上,它的百分比越大,您需要多次查看它的样本就越少。 修复它,然后使用您的计时器查看结果。

然后你可以重新做一遍,以获得下一个加速。 当你找不到更多的时候,你的代码基本上是最优的。

这可能令人惊讶,但这可以找到任何分析器可以找到的任何加速器,有些是他们不会找到的。 它叫random pausing,很多人都依赖它。

有些人说这正是分析器所做的,只是更好,但他们需要考虑一下。 即使是在挂钟时间以行级精度对堆栈进行采样的优秀分析器的问题是它们有一个总结 - 热路径、调用图、火焰图、“总时间”、“自身时间”的后端... 任何。 我们鼓励您查看这些内容,而不是实际样本本身。This post 展示了加速器隐藏这些摘要是多么容易,以及为什么这是一件坏事。 样本是洞察力的所在,它可以告诉您正在发生的事情可以做得更好,如果需要足够的时间来修复,则不需要大量的样本来查看它。 如果你想知道为什么,the math is here.


回复评论:

你在正确的轨道上。你知道它适用于无限循环或接近无限循环,因为这样的问题需要 99.99% 的时间,所以你的停顿肯定会落入其中。如果问题花费的时间比例更小,例如 30% 甚至 10%,它也可以工作。

如果问题需要 F 的时间,那么您将平均每 1/F 个样本看到它。 例如,如果 F 为 0.3 (30%),那么您将平均每 1/0.3 = 3.33 个样本看到它。 但是,如果您只看到一些可疑代码一次,这并不意味着这是一个问题。 但是,如果您看到它两次(或更多),那么您肯定知道这是一个获得加速的机会。 两次看到问题所需的平均样本数为 2/F,或者如果 F 为 30%,则为 6.67 个样本。 因此,请继续采样,直到您看到两次可疑代码为止。

如果您的代码运行速度过快,没问题。只需在它周围放置一个长时间运行的循环。正如你所说,假设它需要 3 微秒。你所要做的就是循环它 10^7 次,它需要 30 秒。 如果那里有一个加速机会,需要 30% 的时间,那么每次暂停都有 30% 的机会显示它,无论它有多快。 您无需担心代码速度很快。

您想节省大约 70% 的时间。可能需要多次修复才能做到这一点。假设有 30%、20%、10% 和 10% 的潜在加速。去掉 30% 的那个,其他的扩展 1.43 到 29%、14% 和 14%。去掉 29% 一个,现在是 20% 和 20%。去掉下一个,最后一个是25%。这就是您获得大幅加速的方式 - 通过剥离。

下面是最重要的一点: 从这里你可以看出一个不漏一个是多么重要。 仅获得四分之三,缺少一个,将不会削减芥末。 Speedup opportunities can easily hide from profilers' summaries,但他们无法隐藏你查看堆栈样本。 忘记所有花哨的分析器结果,您需要速度,这是底线。

【讨论】:

有趣,我实际上有时在 Eclipse 中这样做,但只有当事情运行如此缓慢时,我认为它卡在一个循环中并暂停。我遇到的问题是在很短的时间内以编程方式获取堆栈跟踪,因为我的黄金方法中的代码平均只需要 3100 纳秒来运行(但它被称为 212K 次),所以我需要将它降低到 1000 纳秒所以所有呼叫在我的目标 200-300 毫秒内完成。在这个级别上,可能是某些操作会减慢速度,但我不知道是哪一个,例如 bitshifts/ifs/arrayIdx//bitmasks,我将看看我如何从 java 中的 API 获取堆栈【参考方案2】:

要编写微基准测试,您应该使用适当的工具,例如 JMH。使用正确的基准测试工具还可以避免the common pitfalls。

【讨论】:

我重复我所有的计时测试以获得平均值并进行数千次迭代并通过迭代次数对结果进行平均,JMH 还可以,但它太费力且耗时太长,无法一直更改而且我不认为它只能对代码块进行基准测试。

以上是关于java,时间单独的if语句和循环而不是带有探查器的方法的主要内容,如果未能解决你的问题,请参考以下文章

避免 if 语句的内联函数指针

如何知道 Python 编程是不是在探查器下运行?

在 vuejs 中使用 if 语句循环

如何在 Java VisualVM 中显示探查器选项卡? [复制]

Java Eclipse RCP 探查器

为啥使用预处理器#if 语句而不是 if() else?