Source code for snudda.utils.benchmark_logging
import json
import os
import timeit
from collections import OrderedDict
[docs]
class BenchmarkLogging:
""" Saves benchmark logging when running snudda from command line.
Trivial example below:
import time
bl = BenchmarkLogging("/home/hjorth/HBP/Snudda/snudda")
bl.start_timer("test")
time.sleep(1)
bl.start_timer("test2")
time.sleep(2)
bl.stop_timer("test2")
bl.stop_timer("test")
bl.write_log()
"""
def __init__(self, network_path, parallel_flag=False, log_file=None, running_neuron=False,
ipython_profile=None):
"""
Constructor.
Args:
network_path (str): Path to network
parallel_flag (bool): Running in parallel, should we determine number of workers?
log_file (str) : Log file to save text to
running_neuron (bool) : Are we running NEURON? (Sets method for determining number of workers)
"""
if log_file:
self.log_file = log_file
else:
self.log_file = os.path.join(network_path, "benchmark_log.json")
self.network_name = self.get_network_name(network_path)
self.start_time = dict()
self.end_time = dict()
self.pc = None # Used if running neuron
if parallel_flag or running_neuron:
self.num_workers = self.get_number_of_workers(running_neuron=running_neuron,
ipython_profile=ipython_profile)
else:
self.num_workers = 1
[docs]
def get_number_of_workers(self, running_neuron, ipython_profile=None):
"""
Returns number of workers.
Args:
running_neuron (bool) : Are we running NEURON? Used when determining number of workers).
"""
if running_neuron:
# We are running neuron, different way to detect number of workers
from mpi4py import MPI # Import mpi before NEURON for parallel
from neuron import h
self.pc = h.ParallelContext()
return self.pc.nhost()
# Is there a simpler way to get the number of workers?
if ipython_profile is None:
ipython_profile = os.getenv('IPYTHON_PROFILE')
if not ipython_profile:
ipython_profile = "default"
ipython_dir = os.getenv('IPYTHONDIR')
if not ipython_dir:
ipython_dir = os.path.join(os.path.abspath(os.getcwd()), ".ipython")
import ipyparallel
u_file = os.path.join(ipython_dir, f"profile_{ipython_profile}", "security", "ipcontroller-client.json")
if os.path.isfile(u_file):
print(f"Benchmark reading ipyparallel config file: {u_file}")
try:
rc = ipyparallel.Client(url_file=u_file, profile=ipython_profile, timeout=120, debug=False)
d_view = rc.direct_view(targets='all') # rc[:] # Direct view into clients
return len(d_view) + 1 # We also include the master node
except:
return -1
else:
return 1
[docs]
@staticmethod
def get_network_name(network_path):
""" Returns network name based on network_path. """
if os.path.basename(network_path):
network_name = os.path.basename(network_path)
else:
network_name = os.path.basename(os.path.dirname(network_path))
return network_name
[docs]
def start_timer(self, item_name):
""" Start benchmark timer for item_name. """
self.start_time[item_name] = timeit.default_timer()
[docs]
def stop_timer(self, item_name):
""" Stops benchmark timer for item_name. """
self.end_time[item_name] = timeit.default_timer()
[docs]
def write_log(self):
""" Writes to log. """
if self.pc and self.pc.id() != 0:
# If this is true we are running NEURON, and with id != 0 we are not master node, just return
return
if os.path.exists(self.log_file):
try:
with open(self.log_file, "r") as fr:
data = json.load(fr, object_pairs_hook=OrderedDict)
except:
print(f"Error loading {self.log_file}, creating new benchmark log.")
# Start with fresh data, this will overwrite old data
data = OrderedDict()
else:
data = OrderedDict()
if self.network_name not in data:
data[self.network_name] = OrderedDict()
del_keys = []
for item_name in self.end_time.keys():
assert item_name in self.start_time, f"Benchmark logging was not started for {item_name}"
duration = self.end_time[item_name] - self.start_time[item_name]
if item_name in data[self.network_name]:
data[self.network_name][item_name].append([duration, self.num_workers])
else:
data[self.network_name][item_name] = [[duration, self.num_workers]]
del_keys.append(item_name)
for key in del_keys:
# Remove the old start and end times
del self.start_time[key]
del self.end_time[key]
with open(self.log_file, "w") as fw:
json.dump(data, fw, indent=4)