jmetal中没有自带统计算法运行时间的程序,因此,为了记录算法的运行时间,需要将其设置为单线程的方式运行,在jemtal中,可以通过ExperimentBuilder对象的setNumberOfCores方法设置。
如上图所示,通过将内核数设置为0, 实现了程序的单线程方式运行。
package org.uma.jmetal.util.experiment.util;
import java.io.File;
import java.util.List;
import org.uma.jmetal.algorithm.Algorithm;
import org.uma.jmetal.solution.Solution;
import org.uma.jmetal.util.JMetalLogger;
import org.uma.jmetal.util.experiment.Experiment;
import org.uma.jmetal.util.fileoutput.SolutionListOutput;
import org.uma.jmetal.util.fileoutput.impl.DefaultFileOutputContext;
import com.sim.common.MathUtil;
/**
* Class defining tasks for the execution of algorithms in parallel.
*
* @author Antonio J. Nebro <[email protected]>
*/
public class ExperimentAlgorithm<S extends Solution<?>, Result extends List<S>> {
private Algorithm<Result> algorithm;
private String algorithmTag;
private String problemTag;
private String referenceParetoFront;
private int runId;
//添加
private double runTime;
/**
* Constructor
*/
public ExperimentAlgorithm(Algorithm<Result> algorithm, String algorithmTag, ExperimentProblem<S> problem,
int runId) {
this.algorithm = algorithm;
this.algorithmTag = algorithmTag;
this.problemTag = problem.getTag();
this.referenceParetoFront = problem.getReferenceFront();
this.runId = runId;
}
public ExperimentAlgorithm(Algorithm<Result> algorithm, ExperimentProblem<S> problem, int runId) {
this(algorithm, algorithm.getName(), problem, runId);
}
public void runAlgorithm(Experiment<?, ?> experimentData) {
String outputDirectoryName = experimentData.getExperimentBaseDirectory() + "/data/" + algorithmTag + "/"
+ problemTag;
File outputDirectory = new File(outputDirectoryName);
if (!outputDirectory.exists()) {
boolean result = new File(outputDirectoryName).mkdirs();
if (result) {
JMetalLogger.logger.info("Creating " + outputDirectoryName);
} else {
JMetalLogger.logger.severe("Creating " + outputDirectoryName + " failed");
}
}
String funFile = outputDirectoryName + "/FUN" + runId + ".tsv";
String varFile = outputDirectoryName + "/VAR" + runId + ".tsv";
JMetalLogger.logger.info(" Running algorithm: " + algorithmTag + ", problem: " + problemTag + ", run: " + runId
+ ", funFile: " + funFile);
// 1.记录开始时间
long startTime = System.currentTimeMillis();
// 2.运行算法
algorithm.run();
// 3.记录结束时间
long endTime = System.currentTimeMillis();
// 4.计算耗时
runTime = MathUtil.round(MathUtil.divide(endTime - startTime, 1000), 2);
JMetalLogger.logger.info("No." + runId + " " + algorithmTag + " on " + problemTag + " speed: " + runTime + "s");
Result population = algorithm.getResult();
// 这里就是为什么var和fun文件始终位/t分割的原因
new SolutionListOutput(population).setSeparator("\t")
.setVarFileOutputContext(new DefaultFileOutputContext(varFile))
.setFunFileOutputContext(new DefaultFileOutputContext(funFile)).print();
}
public Algorithm<Result> getAlgorithm() {
return algorithm;
}
public String getAlgorithmTag() {
return algorithmTag;
}
public String getProblemTag() {
return problemTag;
}
public String getReferenceParetoFront() {
return referenceParetoFront;
}
public int getRunId() {
return this.runId;
}
public double getRunTime() {
return runTime;
}
}
那么,为什么jmetal中内核数设为0就可以使得其单线程运行呢?
答案在于:jmetal的多线程实现方式是使用JDK8的并行流,而并行流中通过设置如下的全局属性限制线程池的大小:
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","0");
官方的JDK给出的解释如下:
上图也解释了为什么我们将并行等级设置为0时,jmetal仍然可以执行算法的原因,因为ForkJoinPool的源码实现中定义了至少有一个活动线程。经笔者实验,将内核数【并行等级】设为1时,运行的线程数为2;当内核数【并行等级】设为2时,运行的线程数为3,这里运行的线程数是指实际执行算法的线程个数。下图清晰地展示了并行等级设置为2时的情况下,线程的个数: