【问题标题】:Confusing benchmarking results for StringBuilder vs StringBuffer with JUnit [duplicate]使用 JUnit 混淆 StringBuilder 与 StringBuffer 的基准测试结果 [重复]
【发布时间】:2017-05-22 08:15:09
【问题描述】:

我运行了以下 JUnit 测试用例,并且能够持续获得 StringbufferStringBuilder 更好的性能结果。我确定我在这里遗漏了一些东西,但我找不到为什么StringBuffer 的速度比StringBuilder 更快的原因。

我的测试用例是,

    @Test
    public void stringTest(){

        String s1 = "s1";
        String s2 = "s2";

        for(int i=0;i<=100000;i++){
            s1 = s1+s2;
        }
        System.out.println(s1);
    }

    @Test
    public void stringBuilderTest(){

        StringBuilder s1 = new StringBuilder("s1");
        String s2 = "s2";

        for(int i=0;i<=100000;i++){
            s1.append(s2);
        }
        System.out.println(s1);
    }


    @Test
    public void stringBufferTest(){

        StringBuffer s1 = new StringBuffer("s1");
        String s2 = "s2";

        for(int i=0;i<=100000;i++){
            s1.append(s2);
        }
        System.out.println(s1);
    }

请查找JUnit测试结果,

正如您在上面的结果中看到的那样,stringBufferTest 案例比stringBuilderTest 案例执行得更快。我的问题是为什么会这样?我知道这在理论上是不可能的,但我是如何得到这个结果的?


更新

根据@Henry 的评论,我删除了 SysOuts,结果发生了巨大变化。

然后我将循环计数增加 100000 -> 1000000,并且能够得到一些我一直期望的实际结果,

我的新问题是,

  1. 为什么当我删除我的 系统输出?
  2. 当负载从低到高增加到 1000000 时,StringBuffer 给出 比 StringBuilder 最好的结果,这是为什么呢?

【问题讨论】:

  • 您是否尝试将它们按不同的顺序放置,即在构建器之前缓冲?这可能是由于您的缓冲区使用了相同的字符串 iun 内存。
  • 我会从测量中取出 println(可能花费的时间最多)。也看看stackoverflow.com/questions/504103/…
  • @Nathan 是的,我先尝试了 StringBuffer,但仍然得到相同的结果。
  • @Henry 感谢您的输入,我将再次测试。
  • @GhostCat 您链接的帖子可能包含答案,但这两个问题根本不重复?如果我有同样的问题,我根本不会看到你标记为重复的帖子。问题的答案可能是“基准无效”,并链接到您链接的帖子。目前,这显然是一个不同的问题,显示了经过研究的努力(他进行了测试),不值得重复标记。

标签: java junit benchmarking stringbuilder stringbuffer


【解决方案1】:

恐怕你的基准基本上是无效的。

众所周知,微基准测试(代码少,完成相对较快)在 Java 中(在许多其他语言中也是如此)很难正确完成。使这些基准测试变得困难的一些问题:

  • 根据测试的编写方式,编译器可能会优化一些代码,导致基准测试无法测量您想要测量的内容。
  • 在运行时(而不是编译时)发生的优化通常以增量方式发生。最初,代码被解释。只有当它的某些部分被足够频繁地执行时,Just in Time 编译器才会生成优化的机器代码。微基准测试通常不会触发这种优化。 http://www.oracle.com/technetwork/java/whitepaper-135217.html 有更多关于这个话题的信息。
  • 即使代码符合优化条件,取决于它的编写方式,即时编译器可能无法完全修补它,因此它必须退回到次优优化。搜索“堆栈替换”了解更多详情。

Oracle 的这篇文章更详细地介绍了这些问题:http://www.oracle.com/technetwork/articles/java/architect-benchmarking-2266277.html

最后,归结为:除非您在这方面非常有经验,否则不要从头开始编写微基准测试。您获得的结果与该代码在实际应用程序中的执行方式无关。

改用 JMH(Java Microbenchmark Harness)之类的框架。我上面链接的文章包含对 JMH 的介绍,但还有其他关于它的教程(例如 http://java-performance.info/jmh/)。

花点时间学习使用 JMH。这很值得。当您使用 JMH 运行基准测试时,您将在其输出中看到由于 JVM 优化(JMH 多次调用您的测试)而导致基准测试时间随时间变化的剧烈程度。

如果您在 JMH 中运行 StringBuilder 与 StringBuffer 测试,您应该会发现这两个类在现代 CPU 上的性能几乎相同。 StringBuilder 稍微快一些,但没那么快。不过,我还是会使用 StringBuilder,因为它会稍微快一些。

【讨论】:

  • 这篇 StackOverflow 文章可能还包含有关微基准的有用信息:stackoverflow.com/questions/504103/…
  • 感谢您的详细回答。你的最后一行与前面的行有点矛盾。为什么要使用 Stringbuffer?
  • @thusharaK 他可能的意思是差异不足以放弃线程安全属性。即使你不需要它,他也会用它来保持一致性?
  • @Nathan 我想是的。
  • @thusharaK 这是最后一行的错误,混淆了两个类。我已经更正了。
猜你喜欢
  • 1970-01-01
  • 2015-12-13
  • 2017-05-27
  • 2019-02-19
  • 1970-01-01
  • 2016-07-13
  • 2011-06-06
  • 1970-01-01
  • 2019-05-21
相关资源
最近更新 更多