first try on a fancy upload_custom_dap_script
This commit is contained in:
@@ -9,6 +9,7 @@ from .pids import align_pid_left, align_pid_right, aligned_pid_and_n
|
||||
from .tools import get_current_pulseid
|
||||
from .poweron import guided_power_on
|
||||
from .jfstatus import color_bar, header_bar
|
||||
from .customdap import upload_custom_dap_script
|
||||
|
||||
|
||||
class BrokerClient:
|
||||
@@ -152,6 +153,10 @@ class BrokerClient:
|
||||
take_pedestal(self.restapi, self.config, detectors=detectors, rate=rate, pedestalmode=pedestalmode)
|
||||
|
||||
|
||||
def upload_custom_dap_script(self, fname):
|
||||
upload_custom_dap_script(self.restapi, fname)
|
||||
|
||||
|
||||
@forwards_to(guided_power_on, nfilled=1)
|
||||
def guided_power_on(self, *args, **kwargs):
|
||||
guided_power_on(self, *args, **kwargs)
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
import sys
|
||||
import time
|
||||
import linecache
|
||||
import importlib.util as ilu
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
|
||||
import numpy as np
|
||||
import timeit
|
||||
|
||||
|
||||
def upload_custom_dap_script(restapi, fname, *args, name=None, **kwargs):
|
||||
name = name or Path(fname).stem
|
||||
code = read_file(fname)
|
||||
|
||||
func = load_proc_from_file(fname)
|
||||
test_run(func)
|
||||
|
||||
msg = restapi.upload_custom_dap_script(name, code, *args, **kwargs)
|
||||
print(msg)
|
||||
|
||||
|
||||
def read_file(fn):
|
||||
with open(fn) as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def load_proc_from_file(fn):
|
||||
mod = load_module(fn)
|
||||
|
||||
proc_func_name = "proc"
|
||||
mod_name = mod.__name__
|
||||
|
||||
func = getattr(mod, proc_func_name, None) or getattr(mod, mod_name, None)
|
||||
if func is None:
|
||||
raise AttributeError(f'module "{mod_name}" contains neither "{proc_func_name}" nor "{mod_name}" function')
|
||||
|
||||
return func
|
||||
|
||||
|
||||
def load_module(file_path, module_name=None):
|
||||
module_name = module_name or Path(file_path).stem
|
||||
spec = ilu.spec_from_file_location(module_name, file_path)
|
||||
module = ilu.module_from_spec(spec)
|
||||
spec.loader.exec_module(module)
|
||||
return module
|
||||
|
||||
|
||||
def test_run(func, max_time=0.1):
|
||||
shape = (1024, 512) #TODO: does this have to be the correct JF's size?
|
||||
image = np.random.random(shape)
|
||||
mask = image < 0.5
|
||||
meta = {} #TODO: add some/all possible entries
|
||||
|
||||
orig_meta = meta.copy()
|
||||
orig_image = image.copy()
|
||||
orig_mask = mask.copy()
|
||||
|
||||
with LineProfiler() as lp:
|
||||
func(meta, image, mask)
|
||||
|
||||
name = func.__name__
|
||||
|
||||
if meta != orig_meta:
|
||||
raise RuntimeError(f'function "{name}" modifies the metadata -- this is not allowed, return the result(s) instead')
|
||||
|
||||
compare(name, "image", orig_image, image)
|
||||
compare(name, "mask", orig_mask, mask)
|
||||
|
||||
run = lambda: func(meta, image, mask)
|
||||
mean, _std, msg = timeit_verbose(run)
|
||||
print("Timing results:", msg)
|
||||
|
||||
if mean <= max_time:
|
||||
return
|
||||
|
||||
print("Profiling results:")
|
||||
lp.print(func.__code__.co_filename)
|
||||
print()
|
||||
|
||||
raise RuntimeError(f'function "{name}" runs for {mean:.3g}s on average -- this is too slow, check the profiling results')
|
||||
|
||||
|
||||
def compare(name, what, before, after):
|
||||
if not np.array_equal(after, before, equal_nan=True):
|
||||
print(f'function "{name}" modifies the {what} -- this has no effect outside the function itself')
|
||||
|
||||
|
||||
def timeit_verbose(func, min_time=0.2, target_time=2, min_repeat=3):
|
||||
timer = timeit.Timer(func)
|
||||
number, repeat = find_number_and_repeat(timer, min_time, target_time, min_repeat)
|
||||
times = run_timer(timer, number, repeat)
|
||||
mean, std = calc_stats(times, number)
|
||||
msg = f"{fmt_secs(mean)} ± {fmt_secs(std)} per loop (mean ± std. dev. of {repeat:,} runs, {number:,} loops each)"
|
||||
return mean, std, msg
|
||||
|
||||
|
||||
def find_number_and_repeat(timer, min_time, target_time, min_repeat):
|
||||
"""
|
||||
find number so that the total time per repeat >= min_time
|
||||
pick repeat so that the total time overall ~ target_time, but at least min_repeat
|
||||
"""
|
||||
number = 1
|
||||
total_time = timer.timeit(number)
|
||||
while total_time < min_time:
|
||||
number *= 10
|
||||
total_time = timer.timeit(number)
|
||||
|
||||
repeat = int(round(target_time / total_time))
|
||||
repeat = max(min_repeat, repeat)
|
||||
|
||||
return number, repeat
|
||||
|
||||
|
||||
def run_timer(timer, number, repeat):
|
||||
return [timer.timeit(number) for _ in range(repeat)]
|
||||
|
||||
|
||||
def calc_stats(times, number):
|
||||
mean = np.mean(times) / number
|
||||
std = np.std(times) / number
|
||||
return mean, std
|
||||
|
||||
|
||||
def fmt_secs(time):
|
||||
UNITS = {
|
||||
"n": 1e9,
|
||||
"µ": 1e6,
|
||||
"m": 1e3,
|
||||
"": 1
|
||||
}
|
||||
for prefix, factor in UNITS.items():
|
||||
current = time * factor
|
||||
if current < 1000 or factor == 1:
|
||||
return f"{current:.3g} {prefix}s"
|
||||
|
||||
|
||||
|
||||
class LineProfiler:
|
||||
|
||||
def __init__(self):
|
||||
self.timings = defaultdict(lambda: defaultdict(int)) # one per file with timing per lineno/line
|
||||
self.prev_time = None
|
||||
self.prev_frame = None
|
||||
self.prev_lineno = None
|
||||
|
||||
def __enter__(self):
|
||||
sys.settrace(self.tracer)
|
||||
return self
|
||||
|
||||
def __exit__(self, _exc_type, _exc_value, _traceback):
|
||||
sys.settrace(None)
|
||||
|
||||
def tracer(self, frame, event, _arg):
|
||||
now = time.perf_counter()
|
||||
if self.prev_time is not None:
|
||||
filename = self.prev_frame.f_code.co_filename
|
||||
lineno = self.prev_lineno
|
||||
line = linecache.getline(filename, lineno).rstrip("\n")
|
||||
key = (lineno, line)
|
||||
delta = now - self.prev_time
|
||||
self.timings[filename][key] += delta
|
||||
self.prev_time = now
|
||||
self.prev_frame = frame
|
||||
self.prev_lineno = frame.f_lineno
|
||||
return self.tracer
|
||||
|
||||
def print(self, fname):
|
||||
entries = self.timings[fname]
|
||||
print(f"\nFile: {fname}")
|
||||
for (lineno, line), timing in sorted(entries.items()):
|
||||
print(f"{lineno:4} {timing*1e3:8.3f} ms | {line}")
|
||||
|
||||
def print_all(self):
|
||||
for fname in sorted(self.timings):
|
||||
self.print(fname)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user