当遇到循环体时,try-catch该放在循环体的里面还是外面呢?哪种方式的性能更高呢?因此,本文便通过JMH来进行基准测试,到底哪种方式的性能更高些。
首先明白两个定义:
- JMH:是Java Microbenchmark Harness 的缩写。翻译成中文是“JAVA 微基准测试套件”的意思。
- 基准测试:百度百科的定义是指通过设计科学的测试方法、测试工具和测试系统,实现对一类测试对象的某项性能指标进行定量的和可对比的测试。
明白这两个定义之后,我们就进行测试了:
-
在maven中导入:
<dependencies> <!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-core --> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-core</artifactId> <version>1.25</version> </dependency> <!-- https://mvnrepository.com/artifact/org.openjdk.jmh/jmh-generator-annprocess --> <dependency> <groupId>org.openjdk.jmh</groupId> <artifactId>jmh-generator-annprocess</artifactId> <version>1.25</version> <scope>provided</scope> </dependency> </dependencies>
进行性能测试
在进行性能测试之前,介绍几个概念
- @BenchmarkMode:测试出平均耗时等信息。
- @OutputTimeUnit:基准测试结果时间的单位。
- @Warmup:用于配置预热参数。
- @Fork:指定fork出多少个子进程来执行同一基准测试方法。
- @State:用于声明某个类是一个状态。
- @Threads:指定使用多少个线程来执行基准测试方法。
以下是代码:
package com.hfut;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.TimeUnit;
/**
* @author Chenzh
*/
@BenchmarkMode(Mode.AverageTime) /*每次调用平均耗时*/
@OutputTimeUnit(TimeUnit.SECONDS) /*基准测试结果时间的单位*/
@Warmup(iterations = 1, time = 1, timeUnit = TimeUnit.SECONDS) /*预热1轮,每次预热结束等待时间为1,单位为s*/
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS) /*迭代5次,执行完一次等待5s钟*/
@Fork(1) /*指定fork出多少个子进程来执行同一基准测试方法*/
@State(Scope.Benchmark) /*用于声明某个类是一个状态,Benchmark指该状态在所有线程间共享*/
@Threads(100) /*指定使用多少个线程来执行基准测试方法*/
public class Test {
/**
* 循环次数
*/
private static final int LOOP_COUNT = 200;
public static void main(String[] args) {
Options ops = new OptionsBuilder().include(Test.class.getSimpleName()).build();
// 执行测试
try {
new Runner(ops).run();
} catch (RunnerException e) {
e.printStackTrace();
}
}
/**
* try-catch在循环体里面
* @return sum
*/
@Benchmark
public int innerLoop() {
int sum = 0;
for (int i = 0; i <= LOOP_COUNT; i++) {
try {
if (i == LOOP_COUNT) {
throw new Exception("出现异常");
}
sum += i;
} catch (Exception e) {
e.printStackTrace();
}
}
return sum;
}
/**
* try-catch在循环体外面
* @return sum
*/
@Benchmark
public int outerLoop() {
int sum = 0;
try {
for (int i = 0; i <= LOOP_COUNT; i++) {
if (i == LOOP_COUNT) {
throw new Exception("出现异常");
}
sum += i;
}
} catch (Exception e) {
e.printStackTrace();
}
return sum;
}
}
代码解读:
预热一轮,每次预热结束后等待1s,共迭代5次,执行完一次迭代等待5s,共使用100个线程来执行该基准测试。
从结果中可以看出,当try-catch放在循环体里面耗时69.656-22.557ms ~ 69.656+22.557ms,try-catch放在循环体外面耗时69.034-20.748ms ~ 69.034+20.748ms,因此,这两种方式在性能上的表现相差无几。