Public Access
added missign file for DurationEstimator
This commit is contained in:
@@ -0,0 +1,109 @@
|
|||||||
|
import io
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
from damaris.experiments import Experiment
|
||||||
|
|
||||||
|
|
||||||
|
class DurationEstimator:
|
||||||
|
"""
|
||||||
|
Consumes an experiment script's generator and estimates total duration
|
||||||
|
without writing any files or invoking the backend.
|
||||||
|
|
||||||
|
Sleeps and synchronizations in the script are tracked instead of
|
||||||
|
blocking, so their wall-clock time is included in the estimate.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def estimate(self, script):
|
||||||
|
"""
|
||||||
|
Run the experiment script in a sandbox and return a duration estimate.
|
||||||
|
|
||||||
|
:param str script: experiment script source code
|
||||||
|
:returns: dict with 'experiment_time', 'sleep_time', 'total_time', and 'job_count'
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
sleep_time = [0.0] # list so it's mutable in nested function
|
||||||
|
|
||||||
|
def tracking_sleep(seconds):
|
||||||
|
sleep_time[0] += seconds
|
||||||
|
|
||||||
|
def noop_sync(before=0, waitsteps=0.1):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def noop_data():
|
||||||
|
return {}
|
||||||
|
|
||||||
|
dataspace = {}
|
||||||
|
exp_classes = __import__('damaris.experiments', dataspace, dataspace, ['Experiment'])
|
||||||
|
for name in dir(exp_classes):
|
||||||
|
if name[:2] == "__" and name[-2:] == "__":
|
||||||
|
continue
|
||||||
|
dataspace[name] = exp_classes.__dict__[name]
|
||||||
|
del exp_classes
|
||||||
|
|
||||||
|
# ranges module for lin_range, log_range, etc.
|
||||||
|
ranges = __import__('damaris.tools.ranges', dataspace, dataspace, [])
|
||||||
|
for name in dir(ranges):
|
||||||
|
if name[:2] == "__" and name[-2:] == "__":
|
||||||
|
continue
|
||||||
|
dataspace[name] = getattr(ranges, name)
|
||||||
|
del ranges
|
||||||
|
|
||||||
|
# inject sandbox helpers (non-blocking versions)
|
||||||
|
dataspace["data"] = noop_data
|
||||||
|
dataspace["synchronize"] = noop_sync
|
||||||
|
dataspace["sleep"] = tracking_sleep
|
||||||
|
|
||||||
|
# execute the script
|
||||||
|
try:
|
||||||
|
exec(script, dataspace)
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
raise RuntimeError("Script execution failed") from e
|
||||||
|
|
||||||
|
if "experiment" not in dataspace:
|
||||||
|
raise ValueError("Script does not define an experiment() function")
|
||||||
|
|
||||||
|
# consume the generator
|
||||||
|
try:
|
||||||
|
exp_iterator = dataspace["experiment"]()
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
raise RuntimeError("Calling experiment() failed") from e
|
||||||
|
|
||||||
|
experiment_time = 0.0
|
||||||
|
job_count = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
job = next(exp_iterator)
|
||||||
|
except StopIteration:
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
raise RuntimeError("Generator failed") from e
|
||||||
|
|
||||||
|
experiment_time += job.get_length()
|
||||||
|
job_count += 1
|
||||||
|
|
||||||
|
total_time = experiment_time + sleep_time[0]
|
||||||
|
|
||||||
|
return {
|
||||||
|
"experiment_time": experiment_time,
|
||||||
|
"sleep_time": sleep_time[0],
|
||||||
|
"total_time": total_time,
|
||||||
|
"job_count": job_count,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def estimate_duration(script):
|
||||||
|
"""
|
||||||
|
Convenience function to estimate experiment duration.
|
||||||
|
|
||||||
|
:param str script: experiment script source code
|
||||||
|
:returns: dict with 'experiment_time', 'sleep_time', 'total_time', and 'job_count'
|
||||||
|
:rtype: dict
|
||||||
|
"""
|
||||||
|
return DurationEstimator().estimate(script)
|
||||||
Reference in New Issue
Block a user