首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带有JMH和Mode.AverageTime的Mode.AverageTime

带有JMH和Mode.AverageTime的Mode.AverageTime
EN

Stack Overflow用户
提问于 2015-08-19 13:49:14
回答 1查看 1K关注 0票数 3

我正在编写一个微基准测试来比较使用+操作符与StringBuilder的字符串连接。为此,我创建了一个基于 parameter的JMH基准测试类。

代码语言:javascript
复制
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@Measurement(batchSize = 10000, iterations = 10)
@Warmup(batchSize = 10000, iterations = 10)
@Fork(1)
public class StringConcatenationBenchmark {

    private String string;

    private StringBuilder stringBuilder;

    @Setup(Level.Iteration)
    public void setup() {
        string = "";
        stringBuilder = new StringBuilder();
    }

    @Benchmark
    public void stringConcatenation() {
        string += "some more data";
    }

    @Benchmark
    public void stringBuilderConcatenation() {
        stringBuilder.append("some more data");
    }

}

当我运行基准测试时,我得到了stringBuilderConcatenation方法的以下错误:

代码语言:javascript
复制
java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3332)
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:137)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:121)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:421)
    at java.lang.StringBuilder.append(StringBuilder.java:136)
    at link.pellegrino.string_concatenation.StringConcatenationBenchmark.stringBuilderConcatenation(StringConcatenationBenchmark.java:29)
    at link.pellegrino.string_concatenation.generated.StringConcatenationBenchmark_stringBuilderConcatenation.stringBuilderConcatenation_avgt_jmhStub(StringConcatenationBenchmark_stringBuilderConcatenation.java:165)
    at link.pellegrino.string_concatenation.generated.StringConcatenationBenchmark_stringBuilderConcatenation.stringBuilderConcatenation_AverageTime(StringConcatenationBenchmark_stringBuilderConcatenation.java:130)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:497)
    at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:430)
    at org.openjdk.jmh.runner.BenchmarkHandler$BenchmarkTask.call(BenchmarkHandler.java:412)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

我当时认为必须增加默认的JVM堆大小,所以我尝试使用JMH提供的-Xmx10G值和-jvmArgs选项来允许最多10 by。不幸的是,我仍然得到了错误。

因此,我试图将batchSize参数的值降为1,但仍然得到一个OutOfMemoryError。

我找到的唯一解决办法是将基准测试模式设置为Mode.SingleShotTime。由于此模式似乎将批处理视为一次操作(即使在Unit列中显示了s/op ),所以我似乎得到了我想要的度量:执行批处理操作集的平均时间。然而,我仍然不明白为什么它不与Mode.AverageTime一起工作。

还请注意,方法stringConcatenation的基准测试与预期一样工作,无论使用的是基准模式。此问题仅在使用stringBuilderConcatenation的StringBuilder方法中发生。

任何帮助理解为什么前面的示例没有使用设置为Mode.AverageTime的基准模式是值得欢迎的。

我使用的JMH版本是1.10.4。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-08-23 12:32:57

您是正确的,Mode.SingleShotTime是您所需要的:它度量单个批处理的时间。当使用Mode.AverageTime时,您的迭代仍然工作到迭代时间结束(默认情况下是1秒)。它测量单个批处理的每次执行时间(只计算在执行期间完全完成的批),因此最终结果不同,但执行时间相同。

另一个问题是,@Setup(Level.Iteration)强制设置在每次迭代之前执行,而不是在每个批处理之前执行。因此,您的字符串实际上并不受批处理大小的限制。字符串版本不会仅仅因为OutOfMemoryErrorStringBuilder慢得多而导致它,所以在1秒内它能够构建更短的字符串。

修复基准测试(同时仍然使用平均时间模式和batchSize参数)的方法不是很好,而是手动重置字符串/字符串生成器:

代码语言:javascript
复制
@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@Measurement(batchSize = 10000, iterations = 10)
@Warmup(batchSize = 10000, iterations = 10)
@Fork(1)
public class StringConcatenationBenchmark {
    private static final String S = "some more data";
    private static final int maxLen = S.length()*10000;

    private String string;

    private StringBuilder stringBuilder;

    @Setup(Level.Iteration)
    public void setup() {
        string = "";
        stringBuilder = new StringBuilder();
    }

    @Benchmark
    public void stringConcatenation() {
        if(string.length() >= maxLen) string = "";
        string += S;
    }

    @Benchmark
    public void stringBuilderConcatenation() {
        if(stringBuilder.length() >= maxLen) stringBuilder = new StringBuilder();
        stringBuilder.append(S);
    }
}

下面是我的盒上的结果(i5 3340,4GBRAM,64位Win7,JDK 1.8.0_45):

代码语言:javascript
复制
Benchmark                   Mode  Cnt       Score       Error  Units
stringBuilderConcatenation  avgt   10     145.997 ±     2.301  us/op
stringConcatenation         avgt   10  324878.341 ± 39824.738  us/op

因此,您可以看到,只有大约3批适合stringConcatenation (1e6/324878)的第二批,而对于stringBuilderConcatenation,可以执行数千批,从而产生导致OutOfMemoryError的巨大字符串。

我不知道为什么添加更多的内存对您不起作用,对于我来说,-Xmx4G已经足够运行原始基准的stringBuilder测试了。可能你的盒子更快,所以得到的字符串更长。请注意,对于非常大的字符串,即使您有足够的内存,也可以达到数组大小限制(20亿个元素)。添加内存后检查异常堆栈跟踪:是否相同?如果达到数组大小限制,则仍然是OutOfMemoryError,但是堆栈跟踪会有一点不同。无论如何,即使有足够的内存,基准测试的结果也是不正确的( StringStringBuilder都是如此)。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32097429

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档