Skip to content

Commit

Permalink
Merge pull request #1343 from denisalevi/set_device_profile_arg
Browse files Browse the repository at this point in the history
Allow to turn on profiling in `set_device`
  • Loading branch information
mstimberg committed Oct 9, 2021
2 parents 1bd2c45 + 240eeb0 commit 3b08129
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 7 deletions.
7 changes: 4 additions & 3 deletions brian2/core/magic.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ def after_run(self):
gc.collect() # Make sure that all unused objects are cleared

def run(self, duration, report=None, report_period=10*second,
namespace=None, profile=False, level=0):
namespace=None, profile=None, level=0):
self._update_magic_objects(level=level+1)
Network.run(self, duration, report=report, report_period=report_period,
namespace=namespace, profile=profile, level=level+1)
Expand Down Expand Up @@ -311,7 +311,7 @@ def collect(level=0):

@check_units(duration=second, report_period=second)
def run(duration, report=None, report_period=10*second, namespace=None,
profile=False, level=0):
profile=None, level=0):
'''
run(duration, report=None, report_period=10*second, namespace=None, level=0)
Expand Down Expand Up @@ -347,7 +347,8 @@ def run(duration, report=None, report_period=10*second, namespace=None,
How frequently (in real time) to report progress.
profile : bool, optional
Whether to record profiling information (see `Network.profiling_info`).
Defaults to ``False``.
Defaults to ``None`` (which will use the value set by ``set_device``,
if any).
namespace : dict-like, optional
A namespace in which objects which do not define their own
namespace will be run. If not namespace is given, the locals and
Expand Down
11 changes: 8 additions & 3 deletions brian2/core/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,7 @@ def _nextclocks(self):
@device_override('network_run')
@check_units(duration=second, report_period=second)
def run(self, duration, report=None, report_period=10*second,
namespace=None, profile=False, level=0):
namespace=None, profile=None, level=0):
'''
run(duration, report=None, report_period=60*second, namespace=None, level=0)
Expand Down Expand Up @@ -972,7 +972,8 @@ def run(self, duration, report=None, report_period=10*second,
and globals around the run function will be used.
profile : bool, optional
Whether to record profiling information (see
`Network.profiling_info`). Defaults to ``False``.
`Network.profiling_info`). Defaults to ``None`` (which will use the
value set by ``set_device``, if any).
level : int, optional
How deep to go up the stack frame to look for the locals/global
(see `namespace` argument). Only used by run functions that call
Expand All @@ -984,8 +985,12 @@ def run(self, duration, report=None, report_period=10*second,
The simulation can be stopped by calling `Network.stop` or the
global `stop` function.
'''
all_objects = self.sorted_objects
device = get_device() # Do not use the ProxyDevice -- slightly faster

if profile is None:
profile = device.build_options.get('profile', False)

all_objects = self.sorted_objects
self._clocks = {obj.clock for obj in all_objects}
single_clock = len(self._clocks) == 1

Expand Down
8 changes: 7 additions & 1 deletion brian2/devices/cpp_standalone/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -1363,14 +1363,20 @@ def delete(self, code=True, data=True, directory=True, force=False):
name_suffix='delete_skips_directory')

def network_run(self, net, duration, report=None, report_period=10*second,
namespace=None, profile=False, level=0, **kwds):
namespace=None, profile=None, level=0, **kwds):
self.networks.add(net)
if kwds:
logger.warn(('Unsupported keyword argument(s) provided for run: '
'%s') % ', '.join(kwds.keys()))
# We store this as an instance variable for later access by the
# `code_object` method
self.enable_profiling = profile

# Allow setting `profile` in the `set_device` call (used e.g. in brian2cuda
# SpeedTest configurations)
if profile is None:
self.enable_profiling = self.build_options.get('profile', False)

all_objects = net.sorted_objects
net._clocks = {obj.clock for obj in all_objects}
t_end = net.t+duration
Expand Down
54 changes: 54 additions & 0 deletions brian2/tests/test_cpp_standalone.py
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,59 @@ def test_changing_profile_arg():
profiling_dict['op3_codeobject_1'] > 0*second)


@pytest.mark.cpp_standalone
@pytest.mark.standalone_only
def test_profile_via_set_device_arg():
set_device('cpp_standalone', build_on_run=False, profile=True)
G = NeuronGroup(10000, 'v : 1')
op1 = G.run_regularly('v = exp(-v)', name='op1')
op2 = G.run_regularly('v = exp(-v)', name='op2')
op3 = G.run_regularly('v = exp(-v)', name='op3')
op4 = G.run_regularly('v = exp(-v)', name='op4')
# Op 1 is active only during the first profiled run
# Op 2 is active during both profiled runs
# Op 3 is active only during the second profiled run
# Op 4 is never active (only during the unprofiled run)
op1.active = True
op2.active = True
op3.active = False
op4.active = False
run(1000*defaultclock.dt) # Should use profile=True via set_device
op1.active = True
op2.active = True
op3.active = True
op4.active = True
run(1000*defaultclock.dt, profile=False)
op1.active = False
op2.active = True
op3.active = True
op4.active = False
run(1000*defaultclock.dt, profile=True)
device.build(directory=None, with_output=False)
profiling_dict = dict(magic_network.profiling_info)
# Note that for now, C++ standalone creates a new CodeObject for every run,
# which is most of the time unnecessary (this is partly due to the way we
# handle constants: they are included as literals in the code but they can
# change between runs). Therefore, the profiling info is potentially
# difficult to interpret
assert len(profiling_dict) == 4 # 2 during first run, 2 during last run
# The two code objects that were executed during the first run
assert ('op1_codeobject' in profiling_dict and
profiling_dict['op1_codeobject'] > 0*second)
assert ('op2_codeobject' in profiling_dict and
profiling_dict['op2_codeobject'] > 0*second)
# Four code objects were executed during the second run, but no profiling
# information was saved
for name in ['op1_codeobject_1', 'op2_codeobject_1', 'op3_codeobject',
'op4_codeobject']:
assert name not in profiling_dict
# Two code objects were exectued during the third run
assert ('op2_codeobject_2' in profiling_dict and
profiling_dict['op2_codeobject_2'] > 0*second)
assert ('op3_codeobject_1' in profiling_dict and
profiling_dict['op3_codeobject_1'] > 0*second)


@pytest.mark.cpp_standalone
@pytest.mark.standalone_only
def test_delete_code_data():
Expand Down Expand Up @@ -518,6 +571,7 @@ def test_constant_replacement():
test_array_cache,
test_run_with_debug,
test_changing_profile_arg,
test_profile_via_set_device_arg,
test_delete_code_data,
test_delete_directory,
test_multiple_standalone_runs
Expand Down

0 comments on commit 3b08129

Please sign in to comment.