org.optaplanner.benchmark.config.PlannerBenchmarkConfig Maven / Gradle / Ivy
/*
* Copyright 2011 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.optaplanner.benchmark.config;
import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.annotations.XStreamImplicit;
import org.optaplanner.benchmark.api.PlannerBenchmark;
import org.optaplanner.benchmark.config.blueprint.SolverBenchmarkBluePrintConfig;
import org.optaplanner.benchmark.config.report.BenchmarkReportConfig;
import org.optaplanner.benchmark.impl.PlannerBenchmarkRunner;
import org.optaplanner.benchmark.impl.result.PlannerBenchmarkResult;
import org.optaplanner.benchmark.impl.result.ProblemBenchmarkResult;
import org.optaplanner.benchmark.impl.result.SolverBenchmarkResult;
import org.optaplanner.core.config.AbstractConfig;
import org.optaplanner.core.config.SolverConfigContext;
import org.optaplanner.core.config.util.ConfigUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@XStreamAlias("plannerBenchmark")
public class PlannerBenchmarkConfig {
public static final String PARALLEL_BENCHMARK_COUNT_AUTO = "AUTO";
/**
* @see Runtime#availableProcessors()
*/
public static final String AVAILABLE_PROCESSOR_COUNT = "availableProcessorCount";
public static final Pattern VALID_NAME_PATTERN;
// TODO Remove workaround for Java 6 (once we no longer support it) and unignore tests related to PLANNER-348.
static {
Pattern validNamePattern;
try {
validNamePattern = Pattern.compile("(?U)^[\\w\\d _\\-\\.\\(\\)]+$");
} catch (PatternSyntaxException e) {
// Java 6 does not support (?U)
validNamePattern = Pattern.compile("^[\\w\\d _\\-\\.\\(\\)]+$");
}
VALID_NAME_PATTERN = validNamePattern;
}
private static final Logger logger = LoggerFactory.getLogger(PlannerBenchmarkConfig.class);
private String name = null;
private File benchmarkDirectory = null;
private String parallelBenchmarkCount = null;
private Long warmUpMillisecondsSpentLimit = null;
private Long warmUpSecondsSpentLimit = null;
private Long warmUpMinutesSpentLimit = null;
private Long warmUpHoursSpentLimit = null;
@XStreamAlias("benchmarkReport")
private BenchmarkReportConfig benchmarkReportConfig = null;
@XStreamAlias("inheritedSolverBenchmark")
private SolverBenchmarkConfig inheritedSolverBenchmarkConfig = null;
@XStreamImplicit(itemFieldName = "solverBenchmarkBluePrint")
private List solverBenchmarkBluePrintConfigList = null;
@XStreamImplicit(itemFieldName = "solverBenchmark")
private List solverBenchmarkConfigList = null;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public File getBenchmarkDirectory() {
return benchmarkDirectory;
}
public void setBenchmarkDirectory(File benchmarkDirectory) {
this.benchmarkDirectory = benchmarkDirectory;
}
/**
* Using multiple parallel benchmarks can decrease the reliability of the results.
*
* If there aren't enough processors available, it will be decreased.
* @return null, {@value #PARALLEL_BENCHMARK_COUNT_AUTO}
* or a JavaScript calculation using {@value #AVAILABLE_PROCESSOR_COUNT}.
*/
public String getParallelBenchmarkCount() {
return parallelBenchmarkCount;
}
public void setParallelBenchmarkCount(String parallelBenchmarkCount) {
this.parallelBenchmarkCount = parallelBenchmarkCount;
}
public Long getWarmUpMillisecondsSpentLimit() {
return warmUpMillisecondsSpentLimit;
}
public void setWarmUpMillisecondsSpentLimit(Long warmUpMillisecondsSpentLimit) {
this.warmUpMillisecondsSpentLimit = warmUpMillisecondsSpentLimit;
}
public Long getWarmUpSecondsSpentLimit() {
return warmUpSecondsSpentLimit;
}
public void setWarmUpSecondsSpentLimit(Long warmUpSecondsSpentLimit) {
this.warmUpSecondsSpentLimit = warmUpSecondsSpentLimit;
}
public Long getWarmUpMinutesSpentLimit() {
return warmUpMinutesSpentLimit;
}
public void setWarmUpMinutesSpentLimit(Long warmUpMinutesSpentLimit) {
this.warmUpMinutesSpentLimit = warmUpMinutesSpentLimit;
}
public Long getWarmUpHoursSpentLimit() {
return warmUpHoursSpentLimit;
}
public void setWarmUpHoursSpentLimit(Long warmUpHoursSpentLimit) {
this.warmUpHoursSpentLimit = warmUpHoursSpentLimit;
}
public BenchmarkReportConfig getBenchmarkReportConfig() {
return benchmarkReportConfig;
}
public void setBenchmarkReportConfig(BenchmarkReportConfig benchmarkReportConfig) {
this.benchmarkReportConfig = benchmarkReportConfig;
}
public SolverBenchmarkConfig getInheritedSolverBenchmarkConfig() {
return inheritedSolverBenchmarkConfig;
}
public void setInheritedSolverBenchmarkConfig(SolverBenchmarkConfig inheritedSolverBenchmarkConfig) {
this.inheritedSolverBenchmarkConfig = inheritedSolverBenchmarkConfig;
}
public List getSolverBenchmarkBluePrintConfigList() {
return solverBenchmarkBluePrintConfigList;
}
public void setSolverBenchmarkBluePrintConfigList(List solverBenchmarkBluePrintConfigList) {
this.solverBenchmarkBluePrintConfigList = solverBenchmarkBluePrintConfigList;
}
public List getSolverBenchmarkConfigList() {
return solverBenchmarkConfigList;
}
public void setSolverBenchmarkConfigList(List solverBenchmarkConfigList) {
this.solverBenchmarkConfigList = solverBenchmarkConfigList;
}
// ************************************************************************
// Builder methods
// ************************************************************************
public PlannerBenchmark buildPlannerBenchmark() {
return buildPlannerBenchmark(new SolverConfigContext());
}
public PlannerBenchmark buildPlannerBenchmark(SolverConfigContext solverConfigContext) {
validate();
generateSolverBenchmarkConfigNames();
List effectiveSolverBenchmarkConfigList = buildEffectiveSolverBenchmarkConfigList();
PlannerBenchmarkResult plannerBenchmarkResult = new PlannerBenchmarkResult();
plannerBenchmarkResult.setName(name);
plannerBenchmarkResult.setAggregation(false);
PlannerBenchmarkRunner plannerBenchmarkRunner = new PlannerBenchmarkRunner(plannerBenchmarkResult, solverConfigContext);
plannerBenchmarkRunner.setBenchmarkDirectory(benchmarkDirectory);
plannerBenchmarkResult.setParallelBenchmarkCount(resolveParallelBenchmarkCount());
plannerBenchmarkResult.setWarmUpTimeMillisSpentLimit(calculateWarmUpTimeMillisSpentLimit());
BenchmarkReportConfig benchmarkReportConfig_ = benchmarkReportConfig == null ? new BenchmarkReportConfig()
: benchmarkReportConfig;
plannerBenchmarkRunner.setBenchmarkReport(benchmarkReportConfig_.buildBenchmarkReport(plannerBenchmarkResult));
plannerBenchmarkResult.setUnifiedProblemBenchmarkResultList(new ArrayList());
plannerBenchmarkResult.setSolverBenchmarkResultList(new ArrayList(
effectiveSolverBenchmarkConfigList.size()));
for (SolverBenchmarkConfig solverBenchmarkConfig : effectiveSolverBenchmarkConfigList) {
solverBenchmarkConfig.buildSolverBenchmark(plannerBenchmarkResult);
}
return plannerBenchmarkRunner;
}
protected void validate() {
if (name != null) {
if (!PlannerBenchmarkConfig.VALID_NAME_PATTERN.matcher(name).matches()) {
throw new IllegalStateException("The plannerBenchmark name (" + name
+ ") is invalid because it does not follow the nameRegex ("
+ PlannerBenchmarkConfig.VALID_NAME_PATTERN.pattern() + ")" +
" which might cause an illegal filename.");
}
if (!name.trim().equals(name)) {
throw new IllegalStateException("The plannerBenchmark name (" + name
+ ") is invalid because it starts or ends with whitespace.");
}
}
if (ConfigUtils.isEmptyCollection(solverBenchmarkBluePrintConfigList)
&& ConfigUtils.isEmptyCollection(solverBenchmarkConfigList)) {
throw new IllegalArgumentException(
"Configure at least 1 (or 1 )"
+ " in the configuration.");
}
}
protected void generateSolverBenchmarkConfigNames() {
if (solverBenchmarkConfigList != null) {
Set nameSet = new HashSet(solverBenchmarkConfigList.size());
Set noNameBenchmarkConfigSet = new LinkedHashSet(solverBenchmarkConfigList.size());
for (SolverBenchmarkConfig solverBenchmarkConfig : solverBenchmarkConfigList) {
if (solverBenchmarkConfig.getName() != null) {
boolean unique = nameSet.add(solverBenchmarkConfig.getName());
if (!unique) {
throw new IllegalStateException("The benchmark name (" + solverBenchmarkConfig.getName()
+ ") is used in more than 1 benchmark.");
}
} else {
noNameBenchmarkConfigSet.add(solverBenchmarkConfig);
}
}
int generatedNameIndex = 0;
for (SolverBenchmarkConfig solverBenchmarkConfig : noNameBenchmarkConfigSet) {
String generatedName = "Config_" + generatedNameIndex;
while (nameSet.contains(generatedName)) {
generatedNameIndex++;
generatedName = "Config_" + generatedNameIndex;
}
solverBenchmarkConfig.setName(generatedName);
generatedNameIndex++;
}
}
}
protected List buildEffectiveSolverBenchmarkConfigList() {
List effectiveSolverBenchmarkConfigList = new ArrayList(0);
if (solverBenchmarkConfigList != null) {
effectiveSolverBenchmarkConfigList.addAll(solverBenchmarkConfigList);
}
if (solverBenchmarkBluePrintConfigList != null) {
for (SolverBenchmarkBluePrintConfig solverBenchmarkBluePrintConfig : solverBenchmarkBluePrintConfigList) {
effectiveSolverBenchmarkConfigList.addAll(
solverBenchmarkBluePrintConfig.buildSolverBenchmarkConfigList());
}
}
if (inheritedSolverBenchmarkConfig != null) {
for (SolverBenchmarkConfig solverBenchmarkConfig : effectiveSolverBenchmarkConfigList) {
// Side effect: changes the unmarshalled solverBenchmarkConfig
solverBenchmarkConfig.inherit(inheritedSolverBenchmarkConfig);
}
}
return effectiveSolverBenchmarkConfigList;
}
protected int resolveParallelBenchmarkCount() {
int availableProcessorCount = Runtime.getRuntime().availableProcessors();
int resolvedParallelBenchmarkCount;
if (parallelBenchmarkCount == null) {
resolvedParallelBenchmarkCount = 1;
} else if (parallelBenchmarkCount.equals(PARALLEL_BENCHMARK_COUNT_AUTO)) {
// TODO Tweak it based on experience
if (availableProcessorCount <= 2) {
resolvedParallelBenchmarkCount = 1;
} else if (availableProcessorCount <= 4) {
resolvedParallelBenchmarkCount = 2;
} else {
resolvedParallelBenchmarkCount = (availableProcessorCount / 2) + 1;
}
} else {
String scriptLanguage = "JavaScript";
ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName(scriptLanguage);
scriptEngine.put(AVAILABLE_PROCESSOR_COUNT, availableProcessorCount);
Object scriptResult;
try {
scriptResult = scriptEngine.eval(parallelBenchmarkCount);
} catch (ScriptException e) {
throw new IllegalArgumentException("The parallelBenchmarkCount (" + parallelBenchmarkCount
+ ") is not " + PARALLEL_BENCHMARK_COUNT_AUTO + " and cannot be parsed in " + scriptLanguage
+ " with the variables ([" + AVAILABLE_PROCESSOR_COUNT + "]).", e);
}
if (!(scriptResult instanceof Number)) {
throw new IllegalArgumentException("The parallelBenchmarkCount (" + parallelBenchmarkCount
+ ") is resolved to scriptResult (" + scriptResult + ") in " + scriptLanguage
+ " and is not a Number.");
}
resolvedParallelBenchmarkCount = ((Number) scriptResult).intValue();
}
if (resolvedParallelBenchmarkCount < 1) {
throw new IllegalArgumentException("The parallelBenchmarkCount (" + parallelBenchmarkCount
+ ") resulted in a resolvedParallelBenchmarkCount (" + resolvedParallelBenchmarkCount
+ ") that is lower than 1.");
}
if (resolvedParallelBenchmarkCount > availableProcessorCount) {
logger.warn("Because the resolvedParallelBenchmarkCount (" + resolvedParallelBenchmarkCount
+ ") is higher than the availableProcessorCount (" + availableProcessorCount
+ "), it is reduced to availableProcessorCount.");
resolvedParallelBenchmarkCount = availableProcessorCount;
}
return resolvedParallelBenchmarkCount;
}
protected long calculateWarmUpTimeMillisSpentLimit() {
long warmUpTimeMillisSpentLimit = 0L;
if (warmUpMillisecondsSpentLimit != null) {
warmUpTimeMillisSpentLimit += warmUpMillisecondsSpentLimit;
}
if (warmUpSecondsSpentLimit != null) {
warmUpTimeMillisSpentLimit += warmUpSecondsSpentLimit * 1000L;
}
if (warmUpMinutesSpentLimit != null) {
warmUpTimeMillisSpentLimit += warmUpMinutesSpentLimit * 60000L;
}
if (warmUpHoursSpentLimit != null) {
warmUpTimeMillisSpentLimit += warmUpHoursSpentLimit * 3600000L;
}
return warmUpTimeMillisSpentLimit;
}
}