You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

275 lines
11 KiB

import sys
import os
import json
import pathlib
import argparse
import subprocess
import re
import platform
import numpy
import pandas
from datetime import datetime
def get_time_stamp():
return datetime.now().strftime("%Y-%b-%d-%H-%M-%S")
def get_machine_info():
if platform.system() == "Windows":
return "Windows machine info not implemented"
if platform.system() == "Darwin":
core_count = int(subprocess.run(
["sysctl", "-n", "machdep.cpu.core_count"],
capture_output=True, text=True).stdout.strip())
brand_string = subprocess.run(
["sysctl", "-n", "machdep.cpu.brand_string"],
capture_output=True, text=True).stdout.strip()
memsize = int(subprocess.run(
["sysctl", "-n", "hw.memsize"],
capture_output=True, text=True).stdout.strip()) / 1024**3
return f"{core_count:d}-core {brand_string}, {memsize} GB memory"
if platform.system() == "Linux":
lscpu = subprocess.run(
["lscpu"], capture_output=True, text=True).stdout
cores_per_socket = int(
re.search(r"Core\(s\) per socket:\s*(\d*)", lscpu).group(1))
sockets = int(
re.search(r"Socket\(s\):\s*(\d*)", lscpu).group(1))
cpu_freq = (
float(re.search(r"CPU max MHz:\s*(.+)", lscpu).group(1)) / 1000)
model_name = re.search(r"Model name:\s*(.+)", lscpu).group(1)
meminfo = subprocess.run(
["cat", "/proc/meminfo"], capture_output=True, text=True).stdout
memsize = (
int(re.search(r"MemTotal:\s*(\d*) kB", meminfo).group(1)) / 1024**2)
return ((f"{sockets}x" if sockets > 1 else "")
+ f"{cores_per_socket}-core {cpu_freq} GHz {model_name}, {memsize:.1f} GB memory")
return ""
# A re equvalent to %g used in scanf
percent_g = "[-+]?(\d+(\.\d*)?|\.\d+)([eE][-+]?\d+)?"
def fixture_dir():
return pathlib.Path(__file__).parents[1] / "fixtures"
def sim_exe_name():
return "rigid_ipc_sim"
def find_sim_exe():
for build_dir in (pathlib.Path("."), pathlib.Path(__file__).parents[1] / "build"):
for sub_dir in "", "release", "debug":
sim_exe = build_dir / sub_dir / sim_exe_name()
if sim_exe.is_file():
return sim_exe.resolve()
return None
def get_git_hash():
return subprocess.run(
"git rev-parse HEAD".split(), capture_output=True,
cwd=pathlib.Path(__file__).parent, text=True).stdout.strip()
def get_remote_storage():
for remote_name in "google-drive", "nyu-gdrive", None:
if remote_name is None:
print("Unable to find remote storage using rclone! "
"Videos will not be uploaded")
return None
r = subprocess.run(["rclone", "about", f"{remote_name}:"],
capture_output=True, text=True)
print(r.stdout)
print(r.stderr)
if (r.stderr.strip() == ""):
break
return f"{remote_name}:rigid-ipc/benchmark/"
def create_parser():
parser = argparse.ArgumentParser(
description="Run all scenes and save a CSV of the results.")
parser.add_argument(
"--sim-exe", metavar=f"path/to/{sim_exe_name()}", type=pathlib.Path,
default=None, help="path to simulation executable")
parser.add_argument(
"-i", "--input", metavar="path/to/input", type=pathlib.Path,
dest="input", default=None, help="path to input json(s)", nargs="+")
parser.add_argument(
"-o", "--output", metavar="path/to/output.csv", type=pathlib.Path,
dest="output", default=None, help="path to output CSV")
parser.add_argument(
"--no-video", action="store_true", default=False,
help="do not render a video of the sim")
parser.add_argument(
"--loglevel", default=3, type=int, choices=range(7),
help="set log level 0=trace, 1=debug, 2=info, 3=warn, 4=error, 5=critical, 6=off")
parser.add_argument(
"--sim-args", default="", help=f"arguments to {sim_exe_name()}")
return parser
def parse_arguments():
parser = create_parser()
args = parser.parse_args()
if args.sim_exe is None:
args.sim_exe = find_sim_exe()
if args.sim_exe is None:
parser.exit(1, f"Unable to find {sim_exe_name()}\n")
else:
print(f"Using {args.sim_exe}")
if args.input is None:
args.input = [fixture_dir() / "3D" / "simple"]
input_jsons = []
for input_file in args.input:
if input_file.is_file() and input_file.suffix == ".json":
input_jsons.append(input_file.resolve())
elif input_file.is_dir():
input_jsons.extend(list(input_file.glob('**/*.json')))
args.input = input_jsons
if args.output is None:
args.output = pathlib.Path("benchmark.csv")
return args
def main():
args = parse_arguments()
machine_info = get_machine_info()
base_dir = fixture_dir().resolve()
remote_storage = get_remote_storage()
df = pandas.DataFrame(columns=[
"scene", "dim", "num_bodies", "num_vertices", "num_edges", "num_faces",
"timestep", "num_timesteps", "dhat", "mu", "eps_v",
"friction_iterations", "cor", "eps_d", "avg_num_contacts",
"max_num_contacts", "avg_step_time", "max_step_time", "ccd_broad_phase",
"ccd_narrow_phase", "distance_broad_phase", "avg_solver_iterations",
"max_solver_iterations", "video", "machine", "max_threads", "memory", "git_hash",
"notes"])
max_threads = 16
for scene in args.input:
print(f"Running {scene}")
git_hash = get_git_hash()
try:
scene_name = scene.resolve().relative_to(base_dir)
scene_name = str(scene_name.parent / scene_name.stem)
except ValueError:
scene_name = scene.stem
sim_output_dir = pathlib.Path("output") / scene_name
sim_output_dir.mkdir(parents=True, exist_ok=True)
# with open(sim_output_dir / "log.txt", 'w') as log_file:
# sim = subprocess.Popen(
# [str(args.sim_exe), scene.resolve(),
# sim_output_dir, "--loglevel", str(args.loglevel)],
# stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True)
# for line in sim.stdout:
# sys.stdout.write(line)
# log_file.write(line)
# sim.wait()
subprocess.run([str(args.sim_exe), "--ngui", str(scene.resolve()),
str(sim_output_dir), "--loglevel", str(args.loglevel),
"--nthreads", str(max_threads)]
+ args.sim_args.split())
if(args.no_video):
video_url = "N/a"
else:
print("Rendering simulation")
video_name = f"{scene.stem}-{get_time_stamp()}.mp4"
subprocess.run([str(args.sim_exe.parent / "tools" / "render_simulation"),
sim_output_dir / "sim.json",
"-o", sim_output_dir / video_name,
"--loglevel", str(args.loglevel)])
if remote_storage is not None:
remote_path = (
f"{remote_storage}{pathlib.Path(scene_name).parent}")
subprocess.run(
["rclone", "copy", sim_output_dir / video_name,
remote_path])
video_url = subprocess.run(
["rclone", "link", f"{remote_path}/{video_name}"],
capture_output=True, text=True).stdout.strip()
print(f"Uploaded video to {video_url}")
# subprocess.run([str(args.sim_exe.parent / "tools" / "obj_sequence"),
# "-i", sim_output_dir / "sim.json",
# "-o", sim_output_dir / "objs",
# "--loglevel", str(args.loglevel)])
with open(sim_output_dir / "sim.json") as sim_output:
sim_json = json.load(sim_output)
sim_args = sim_json["args"]
sim_stats = sim_json["stats"]
log_dirs = list(filter(lambda p: p.is_dir(),
sim_output_dir.glob("log*")))
if log_dirs:
profiler_dir = max(log_dirs, key=os.path.getmtime)
profiler_df = pandas.read_csv(
profiler_dir / "summary.csv", header=1, index_col=0,
skipinitialspace=True, converters={
"percentage_time": lambda x: float(x.strip('%'))})
broad_ccd = profiler_df.percentage_time.get(
"detect_continuous_collision_candidates_rigid", -1)
narrow_ccd = profiler_df.percentage_time.get(
"DistanceBarrierConstraint::compute_earliest_toi_narrow_phase",
-1)
broad_distance = profiler_df.percentage_time.get(
"DistanceBarrierConstraint::construct_constraint_set", -1)
else:
print("Profiling not enabled")
broad_ccd = -1
narrow_ccd = -1
broad_distance = -1
df_row = {
"scene": scene_name,
"dim": sim_stats["dim"],
"num_bodies": sim_stats["num_bodies"],
"num_vertices": sim_stats["num_vertices"],
"num_edges": sim_stats["num_edges"],
"num_faces": sim_stats["num_faces"],
"timestep": sim_args["timestep"],
"num_timesteps": sim_stats["num_timesteps"],
"dhat": sim_args["distance_barrier_constraint"]["initial_barrier_activation_distance"],
"mu": sim_args["rigid_body_problem"]["coefficient_friction"],
"eps_v": sim_args["friction_constraints"]["static_friction_speed_bound"],
"friction_iterations": sim_args["friction_constraints"]["iterations"],
"cor": sim_args["rigid_body_problem"]["coefficient_restitution"],
"eps_d": sim_args["ipc_solver"]["velocity_conv_tol"],
"avg_num_contacts": numpy.average(sim_stats["num_contacts"]),
"max_num_contacts": max(sim_stats["num_contacts"]),
"avg_step_time": numpy.average(sim_stats["step_timings"]),
"max_step_time": max(sim_stats["step_timings"]),
"ccd_broad_phase": f"{broad_ccd:g}%",
"ccd_narrow_phase": f"{narrow_ccd:g}%",
"distance_broad_phase": f"{broad_distance:g}%",
"avg_solver_iterations": numpy.average(sim_stats["solver_iterations"]),
"max_solver_iterations": max(sim_stats["solver_iterations"]),
"video": video_url,
"machine": machine_info,
"max_threads": max_threads,
"memory": sim_stats["memory"] / 1024**2,
"git_hash": git_hash,
"notes": "" # results.stdout.strip()
}
df.loc[scene] = df_row
df.to_csv(args.output, index=False)
print(f"Results written to {args.output}")
if __name__ == "__main__":
main()