前言
在日常开发功能时,同一种功能可能会有多种实现方式。我们需要做一个取舍。
最常见的条件就是性能、可读性、可维护性。
本篇文章,我们主要讨论“性能”。
场景
假设我们现在需要计算一段代码的运行时间。
最常见的写法是,在执行这段代码前,获得一下当前的时间戳,在执行这段代码后,获取一下当前的时间戳,然后俩时间相减,就是花费时间了。
但有时,我们需要将这段代码执行多次。
这种场景是,执行的时间戳很小,没有可比性。比如执行一段代码,运行时间是 0 或者 2毫秒。
这种时候你可能会说,那就用一个for循环,执行多次,计算平均时间就好了。
问题来了,如果这种相似的操作,写的多了呢,用的地方很多呢?
我们现在就需要将它给写成一套模板,只需要简单的填充参数,调用,就能实现上述的功能。
代码实现
MethodBody 接口定义
这个接口主要是用于填充代码段,因此设计为函数式接口,方便调用。
package org.feng.calc;
@FunctionalInterface
public interface MethodBody {
void run();
}
CalcExecuteTimeResult 运行结果实体
package org.feng.calc;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
public class CalcExecuteTimeResult {
private List<Long> costTime;
public CalcExecuteTimeResult(int size) {
costTime = new ArrayList<>(size);
}
}
ExecuteTemplate 执行模板定义
package org.feng.calc;
import lombok.Getter;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
public class ExecuteTemplate {
private final List<MethodBody> methodBodyList;
@Getter
private CalcExecuteTimeResult calcExecuteTimeResult;
public ExecuteTemplate() {
this.methodBodyList = new ArrayList<>();
}
public void addMethod(MethodBody methodBody) {
methodBodyList.add(methodBody);
}
void process(TimeUnit timeUnit, long frequency) {
this.calcExecuteTimeResult = new CalcExecuteTimeResult(methodBodyList.size());
List<Long> costTime = calcExecuteTimeResult.getCostTime();
for (MethodBody methodBody : methodBodyList) {
long startTime = getTime(timeUnit);
for (int i = 0; i < frequency; i++) {
methodBody.run();
}
long endTime = getTime(timeUnit);
costTime.add(endTime - startTime);
}
}
private long getTime(TimeUnit timeUnit) {
if (!SUPPORTED_TIME_UNIT.contains(timeUnit)) {
throw new UnsupportedOperationException("不支持的时间单位:" + timeUnit);
}
if (TimeUnit.NANOSECONDS.equals(timeUnit)) {
return System.nanoTime();
}
return System.currentTimeMillis();
}
private static final Set<TimeUnit> SUPPORTED_TIME_UNIT = Set.of(TimeUnit.MILLISECONDS, TimeUnit.NANOSECONDS);
}
CalcExecuteTimeContext 计算执行时间上下文
package org.feng.calc;
import lombok.Setter;
import lombok.experimental.Accessors;
import java.util.concurrent.TimeUnit;
@Setter
@Accessors(chain = true)
public class CalcExecuteTimeContext {
private TimeUnit timeUnit;
private Long frequency;
private ExecuteTemplate executeTemplate;
public CalcExecuteTimeResult run() {
executeTemplate.process(timeUnit, frequency);
return executeTemplate.getCalcExecuteTimeResult();
}
public void addMethod(MethodBody methodBody){
executeTemplate.addMethod(methodBody);
}
}
测试运行
package org.feng.calc;
import java.util.concurrent.TimeUnit;
public class Client {
public static void main(String[] args) {
// 初始化上下文:设置每个方法执行的次数,以及计算时间时使用的时间单位,执行模板
CalcExecuteTimeContext context = new CalcExecuteTimeContext()
.setFrequency(2L)
.setTimeUnit(TimeUnit.MILLISECONDS)
.setExecuteTemplate(new ExecuteTemplate());
context.addMethod(() -> {
System.out.println(111);
System.out.println(111);
System.out.println(111);
System.out.println(111);
System.out.println(111);
});
context.addMethod(() -> {
System.out.println(222);
System.out.println(222);
System.out.println(222);
System.out.println(222);
System.out.println(222);
});
// 计算得到方法运行的结果
CalcExecuteTimeResult executeTimeResult = context.run();
System.out.println(executeTimeResult);
}
}
运行结果:
111
111
111
111
111
111
111
111
111
111
222
222
222
222
222
222
222
222
222
222
CalcExecuteTimeResult(costTime=[0, 1])
到此这篇关于Java计算代码段执行时间的详细过程的文章就介绍到这了,更多相关Java代码段执行时间内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!