Add benchmark runner script with example config
This commit is contained in:
parent
e2cb68c8fb
commit
5d06d0127c
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
## Structure
|
## Structure
|
||||||
|
|
||||||
|
- master branch: contains the [benchmark runner script](benchmark.py)
|
||||||
- [config branch](https://git.luehne.de/patrick/tplp-planning-benchmark/src/config): contains the tested [configurations](https://git.luehne.de/patrick/tplp-planning-benchmark/src/config/configurations.yaml) and [instance sets](https://git.luehne.de/patrick/tplp-planning-benchmark/src/config/instances.yaml)
|
- [config branch](https://git.luehne.de/patrick/tplp-planning-benchmark/src/config): contains the tested [configurations](https://git.luehne.de/patrick/tplp-planning-benchmark/src/config/configurations.yaml) and [instance sets](https://git.luehne.de/patrick/tplp-planning-benchmark/src/config/instances.yaml)
|
||||||
- [status branch](https://git.luehne.de/patrick/tplp-planning-benchmark/src/status): contains the [benchmark status log](https://git.luehne.de/patrick/tplp-planning-benchmark/src/status/status.log) (updated after each instance)
|
- [status branch](https://git.luehne.de/patrick/tplp-planning-benchmark/src/status): contains the [benchmark status log](https://git.luehne.de/patrick/tplp-planning-benchmark/src/status/status.log) (updated after each instance)
|
||||||
- [results branch](https://git.luehne.de/patrick/tplp-planning-benchmark/src/results): contains the raw result files
|
- [results branch](https://git.luehne.de/patrick/tplp-planning-benchmark/src/results): contains the raw result files
|
||||||
|
256
benchmark.py
Executable file
256
benchmark.py
Executable file
@ -0,0 +1,256 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import atexit
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
import pprint
|
||||||
|
|
||||||
|
def executeCommand(command, stdin = None, cwd = None):
|
||||||
|
with subprocess.Popen(command, stdout = subprocess.PIPE, stderr = subprocess.PIPE, stdin = (subprocess.PIPE if stdin != None else None), cwd = cwd) as process:
|
||||||
|
stdout, stderr = process.communicate(input = (stdin.encode("utf-8") if stdin != None else None))
|
||||||
|
exitCode = process.returncode
|
||||||
|
|
||||||
|
return stdout.decode("utf-8"), stderr.decode("utf-8"), exitCode
|
||||||
|
|
||||||
|
def plaspVersion(config):
|
||||||
|
version, _, _ = executeCommand([config["executables"]["plasp"]["binary"], "-v"])
|
||||||
|
version = version.strip()
|
||||||
|
|
||||||
|
match = re.match(r'plasp version (.*?)$', version, re.M | re.I)
|
||||||
|
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
def clingoVersion(config):
|
||||||
|
version, _, _ = executeCommand([config["executables"]["clingo"]["binary"], "-v"])
|
||||||
|
version = version.strip()
|
||||||
|
|
||||||
|
match = re.match(r'clingo version (.*?)$', version, re.M | re.I)
|
||||||
|
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
def plannerVersion(config):
|
||||||
|
version, _, _ = executeCommand(["git", "rev-parse", "HEAD"], cwd = config["executables"]["planner"]["directory"])
|
||||||
|
date, _, _ = executeCommand(["git", "show", "-s", "--format=%ci"], cwd = config["executables"]["planner"]["directory"])
|
||||||
|
|
||||||
|
return version.strip() + " (" + date.strip() + ")"
|
||||||
|
|
||||||
|
def fastDownwardVersion(config):
|
||||||
|
version, _, _ = executeCommand(["hg", "log", "-r.", "-T {rev}:{node} ({date|isodate})"], cwd = config["executables"]["fastDownward"]["directory"])
|
||||||
|
|
||||||
|
return version.strip()
|
||||||
|
|
||||||
|
def git(command, cwd):
|
||||||
|
stdout, stderr, exitCode = executeCommand(["git"] + command, cwd = cwd)
|
||||||
|
|
||||||
|
if exitCode != 0:
|
||||||
|
print(stderr, file = sys.stderr)
|
||||||
|
raise RuntimeError("git error")
|
||||||
|
|
||||||
|
def initRepo(config):
|
||||||
|
dataDir = config["storage"]["local"]
|
||||||
|
|
||||||
|
# clone repo if not existing
|
||||||
|
if not os.path.isdir(config["storage"]["local"]):
|
||||||
|
git(["clone", config["storage"]["remote"], dataDir], None)
|
||||||
|
|
||||||
|
# default settings
|
||||||
|
git(["config", "--local", "user.name", config["storage"]["user"]], dataDir)
|
||||||
|
git(["config", "--local", "user.email", config["storage"]["userEMail"]], dataDir)
|
||||||
|
git(["config", "--local", "commit.gpgsign", "false"], dataDir)
|
||||||
|
|
||||||
|
# fetch origin
|
||||||
|
git(["fetch"], cwd = dataDir)
|
||||||
|
|
||||||
|
# pull all branches
|
||||||
|
for key, branch in config["storage"]["branches"].items():
|
||||||
|
git(["checkout", branch], cwd = dataDir)
|
||||||
|
git(["pull"], cwd = dataDir)
|
||||||
|
|
||||||
|
def readBenchmarkConfig(config):
|
||||||
|
initRepo(config)
|
||||||
|
|
||||||
|
dataDir = config["storage"]["local"]
|
||||||
|
|
||||||
|
# checkout config branch
|
||||||
|
git(["checkout", config["storage"]["branches"]["config"]], cwd = dataDir)
|
||||||
|
|
||||||
|
# read instance list
|
||||||
|
instancesFile = os.path.join(config["storage"]["local"], "instances.yaml")
|
||||||
|
|
||||||
|
with open(instancesFile, "r") as stream:
|
||||||
|
instances = yaml.load(stream, Loader=yaml.CLoader)
|
||||||
|
|
||||||
|
# read configurations to test
|
||||||
|
configurationsFile = os.path.join(config["storage"]["local"], "configurations.yaml")
|
||||||
|
|
||||||
|
with open(configurationsFile, "r") as stream:
|
||||||
|
configurations = yaml.load(stream, Loader=yaml.CLoader)
|
||||||
|
|
||||||
|
# flatten lists of options
|
||||||
|
for configuration in configurations["configurations"]:
|
||||||
|
configuration["options"] = [item for sublist in configuration["options"] for item in sublist]
|
||||||
|
|
||||||
|
return {"configurations": configurations, "instances": instances}
|
||||||
|
|
||||||
|
def inputFilenames(instance, config):
|
||||||
|
pddlInstancesDir = config["input"]["pddlInstances"]
|
||||||
|
|
||||||
|
domainFile = os.path.join(pddlInstancesDir, instance["ipc"], "domains", instance["domain"], "domain.pddl")
|
||||||
|
instanceFile = os.path.join(pddlInstancesDir, instance["ipc"], "domains", instance["domain"], "instances", "instance-" + str(instance["instance"]) + ".pddl")
|
||||||
|
|
||||||
|
return {"domainFile": domainFile, "instanceFile": instanceFile}
|
||||||
|
|
||||||
|
def outputFilenames(configuration, instance, config):
|
||||||
|
instanceID = instance["ipc"] + "_" + instance["domain"] + "_" + str(instance["instance"])
|
||||||
|
outputFile = os.path.join(configuration["id"], instanceID + ".out")
|
||||||
|
errorFile = os.path.join(configuration["id"], instanceID + ".err")
|
||||||
|
environmentFile = os.path.join(configuration["id"], instanceID + ".env")
|
||||||
|
|
||||||
|
return {"outputFile": outputFile, "errorFile": errorFile, "environmentFile": environmentFile}
|
||||||
|
|
||||||
|
def nextJob(config):
|
||||||
|
benchmarkConfig = readBenchmarkConfig(config)
|
||||||
|
|
||||||
|
dataDir = config["storage"]["local"]
|
||||||
|
|
||||||
|
# checkout results branch
|
||||||
|
git(["checkout", config["storage"]["branches"]["results"]], cwd = dataDir)
|
||||||
|
|
||||||
|
configurations = benchmarkConfig["configurations"]["configurations"]
|
||||||
|
instances = benchmarkConfig["instances"]
|
||||||
|
|
||||||
|
for instanceSetName, instanceSet in instances.items():
|
||||||
|
for instance in instanceSet:
|
||||||
|
for configuration in configurations:
|
||||||
|
filenames = outputFilenames(configuration, instance, config)
|
||||||
|
outputFile = os.path.join(config["storage"]["local"], filenames["outputFile"])
|
||||||
|
environmentFile = os.path.join(config["storage"]["local"], filenames["environmentFile"])
|
||||||
|
|
||||||
|
if not os.path.exists(outputFile) or not os.path.exists(environmentFile):
|
||||||
|
return {"configuration": configuration, "instance": instance}
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def writeStatus(message, config):
|
||||||
|
dataDir = config["storage"]["local"]
|
||||||
|
|
||||||
|
# checkout status branch
|
||||||
|
git(["checkout", config["storage"]["branches"]["status"]], cwd = dataDir)
|
||||||
|
|
||||||
|
statusFilename = os.path.join(dataDir, "status.log")
|
||||||
|
|
||||||
|
if os.path.exists(statusFilename):
|
||||||
|
with open(statusFilename, "r") as statusFile:
|
||||||
|
content = statusFile.read()
|
||||||
|
else:
|
||||||
|
content = ""
|
||||||
|
|
||||||
|
with open(statusFilename, "w") as statusFile:
|
||||||
|
print(time.strftime("%Y-%m-%d %H:%M:%S %z") + "\t" + message + "\n" + content, file = statusFile, end = "")
|
||||||
|
|
||||||
|
git(["add", "status.log"], dataDir)
|
||||||
|
git(["commit", "-m Update status: " + message], dataDir)
|
||||||
|
git(["push"], dataDir)
|
||||||
|
|
||||||
|
def runJob(configuration, instance, config):
|
||||||
|
jobName = "[" + str(configuration["id"]) + " | " + instance["ipc"] + " | " + instance["domain"] + " | " + str(instance["instance"]) + "]"
|
||||||
|
|
||||||
|
writeStatus("started benchmark job " + jobName, config)
|
||||||
|
|
||||||
|
dataDir = config["storage"]["local"]
|
||||||
|
|
||||||
|
inputFiles = inputFilenames(instance, config)
|
||||||
|
|
||||||
|
# checkout results branch
|
||||||
|
git(["checkout", config["storage"]["branches"]["results"]], cwd = dataDir)
|
||||||
|
|
||||||
|
command = \
|
||||||
|
[
|
||||||
|
config["executables"]["timeout"]["binary"],
|
||||||
|
"-m=" + str(config["limits"]["memory"]),
|
||||||
|
"-t=" + str(config["limits"]["time"]),
|
||||||
|
config["executables"]["planner"]["binary"],
|
||||||
|
"--domain=" + inputFiles["domainFile"],
|
||||||
|
inputFiles["instanceFile"],
|
||||||
|
]
|
||||||
|
|
||||||
|
command += configuration["options"]
|
||||||
|
|
||||||
|
# TODO: verify planner Git hash
|
||||||
|
plannerDir = config["executables"]["planner"]["directory"]
|
||||||
|
stdout, stderr, exitCode = executeCommand(command, cwd = plannerDir)
|
||||||
|
|
||||||
|
outputFiles = outputFilenames(configuration, instance, config)
|
||||||
|
outputDir = os.path.dirname(os.path.join(config["storage"]["local"], outputFiles["outputFile"]))
|
||||||
|
|
||||||
|
if not os.path.isdir(outputDir):
|
||||||
|
os.makedirs(outputDir)
|
||||||
|
|
||||||
|
with open(os.path.join(config["storage"]["local"], outputFiles["outputFile"]), "w") as outputFile, \
|
||||||
|
open(os.path.join(config["storage"]["local"], outputFiles["errorFile"]), "w") as errorFile, \
|
||||||
|
open(os.path.join(config["storage"]["local"], outputFiles["environmentFile"]), "w") as environmentFile:
|
||||||
|
print(stdout, file = outputFile)
|
||||||
|
print("# configuration: " + str(configuration), file = errorFile)
|
||||||
|
print("# instance: " + str(instance), file = errorFile)
|
||||||
|
print("# command: " + str(command), file = errorFile)
|
||||||
|
print("# working directory: " + plannerDir, file = errorFile)
|
||||||
|
print(stderr, file = errorFile)
|
||||||
|
|
||||||
|
if exitCode != 0:
|
||||||
|
print(stderr)
|
||||||
|
|
||||||
|
environment = \
|
||||||
|
{
|
||||||
|
"configuration": configuration,
|
||||||
|
"instance": instance,
|
||||||
|
"command": command,
|
||||||
|
"workingDirectory": plannerDir,
|
||||||
|
"versions": \
|
||||||
|
{
|
||||||
|
"clingo": clingoVersion(config),
|
||||||
|
"plasp": plaspVersion(config),
|
||||||
|
"planner": plannerVersion(config),
|
||||||
|
"fastDownward": fastDownwardVersion(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print(yaml.dump(environment, default_flow_style = False), file = environmentFile)
|
||||||
|
|
||||||
|
git(["add", outputFiles["outputFile"], outputFiles["errorFile"], outputFiles["environmentFile"]], dataDir)
|
||||||
|
git(["commit", "-m Add benchmark result " + jobName], dataDir)
|
||||||
|
git(["push"], dataDir)
|
||||||
|
|
||||||
|
if exitCode != 0:
|
||||||
|
writeStatus("errors reported for benchmark job " + jobName, config)
|
||||||
|
else:
|
||||||
|
writeStatus("finished benchmark job " + jobName, config)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
with open("config.yaml", "r") as stream:
|
||||||
|
config = yaml.load(stream, Loader=yaml.CLoader)
|
||||||
|
|
||||||
|
atexit.register(writeStatus, "benchmark runner exited", config)
|
||||||
|
|
||||||
|
performedJobs = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
job = nextJob(config)
|
||||||
|
|
||||||
|
if not job:
|
||||||
|
break
|
||||||
|
|
||||||
|
configuration = job["configuration"]
|
||||||
|
instance = job["instance"]
|
||||||
|
|
||||||
|
runJob(configuration, instance, config)
|
||||||
|
performedJobs += 1
|
||||||
|
|
||||||
|
if performedJobs == 0:
|
||||||
|
writeStats("finished benchmark series", config)
|
||||||
|
|
||||||
|
main()
|
43
config.example.yaml
Normal file
43
config.example.yaml
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
storage:
|
||||||
|
# local directory where remote is cloned to
|
||||||
|
local: storage
|
||||||
|
# repote Git repository URL
|
||||||
|
remote: git@git.luehne.de:patrick/tplp-planning-benchmark.git
|
||||||
|
# user name for commits
|
||||||
|
user: potassco-bot
|
||||||
|
# user e-mail for commits
|
||||||
|
userEMail: bot@potassco.org
|
||||||
|
# data branches
|
||||||
|
branches:
|
||||||
|
results: results
|
||||||
|
config: config
|
||||||
|
status: status
|
||||||
|
|
||||||
|
executables:
|
||||||
|
timeout:
|
||||||
|
# path to timeout script (https://github.com/pshved/timeout)
|
||||||
|
binary: /home/user/repos/timeout/timeout
|
||||||
|
plasp:
|
||||||
|
# path to plasp binary (https://github.com/potassco/plasp)
|
||||||
|
binary: plasp
|
||||||
|
clingo:
|
||||||
|
# path to clingo binary (https://github.com/potassco/clingo)
|
||||||
|
binary: clingo
|
||||||
|
planner:
|
||||||
|
# path to planner executable (https://github.com/javier-romero/plasp/tree/master/encodings/planner)
|
||||||
|
directory: /home/user/repos/plasp-javier/encodings/planner
|
||||||
|
binary: /home/user/repos/plasp-javier/encodings/planner/runplanner.py
|
||||||
|
fastDownward:
|
||||||
|
# path to Fast Downward executable (http://hg.fast-downward.org/)
|
||||||
|
directory: /home/user/repos/fast-downward
|
||||||
|
binary: /home/user/repos/fast-downward/fast-downward.py
|
||||||
|
|
||||||
|
input:
|
||||||
|
# path to PDDL instances (https://github.com/potassco/pddl-instances)
|
||||||
|
pddlInstances: /home/user/repos/pddl-instances
|
||||||
|
|
||||||
|
limits:
|
||||||
|
# time limit per instance in seconds
|
||||||
|
time: 900
|
||||||
|
# memory limit per instance in kilobytes
|
||||||
|
memory: 8000000
|
Loading…
Reference in New Issue
Block a user