Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to enable cProfile #223

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions charm4py/charm.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from . import reduction
from . import wait
import array
import cProfile
try:
import numpy
except ImportError:
Expand Down Expand Up @@ -96,6 +97,7 @@ def __init__(self):

self.options = Options()
self.options.profiling = False
self.options.cprofiling = False
self.options.pickle_protocol = -1 # -1 selects the highest protocol number
self.options.local_msg_optim = True
self.options.local_msg_buf_size = 50
Expand Down Expand Up @@ -292,7 +294,8 @@ def recvArrayMsg(self, aid, index, ep, msg, dcopy_start):
if isinstance(args, Chare): # obj migrating in
em = self.entryMethods[ep + 1] # get 'migrated' EntryMethod object instead of __init__
obj = args
obj._contributeInfo = self.lib.initContributeInfo(aid, index, CONTRIBUTOR_TYPE_ARRAY)
obj._contributeInfo = self.lib.initContributeInfo(
aid, index, CONTRIBUTOR_TYPE_ARRAY)
self.arrays[aid][index] = obj
em.run(obj, {}, ())
else:
Expand Down Expand Up @@ -992,7 +995,10 @@ def getPeHost(self, pe):
def getPeHostRank(self, pe):
return self.lib.CkPhysicalRank(pe)

def exit(self, exit_code=0):
def exit(self, exit_code=0, profile_filename="profile"):
if self.options.cprofiling:
self.thisProxy.stop_cprofile(filename=profile_filename,
awaitable=True).get()
self.lib.CkExit(exit_code)

def abort(self, message):
Expand All @@ -1012,6 +1018,8 @@ class CharmRemote(Chare):

def __init__(self):
charm.thisProxy = self.thisProxy
if charm.options.cprofiling:
self.start_cprofile()

def exit(self, exit_code=0):
charm.exit(exit_code)
Expand All @@ -1028,6 +1036,16 @@ def LBTurnInstrumentOff(self):
def addReducer(self, func):
charm.addReducer(func)

def start_cprofile(self):
self.pr = cProfile.Profile()
self.pr.enable()

def stop_cprofile(self, dump=True, filename="profile"):
if hasattr(self, "pr"):
self.pr.disable()
if dump:
self.pr.dump_stats(filename + str(self.myPe()) + ".prof")

# user signature is: `def updateGlobals(self, global_dict, module_name='__main__')`
def updateGlobals(self, *args):
global_dict = {}
Expand Down
8 changes: 6 additions & 2 deletions docs/profiling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ lot of time waiting for communication to complete (which depends on the size
and number of messages per second, network throughput and latency, efficiency
of the communication layer, etc.). If you are seeing significant idle time, one
thing that can increase performance is using an efficient communication layer
such as MPI (see :doc:`perf-tips`).
such as MPI (see :doc:`perf-tips`). Charm4Py also supports profiling using cProfile.

In the future, we plan to support Projections_ which is a full-fledged
parallel performance analysis tool for Charm++.
Expand Down Expand Up @@ -120,6 +120,10 @@ method of each Cell. Most of the send overhead is from the "run" method
is called when one of its channels receives a message). There is also some
receive overhead due to chares migrating into this PE (method ``migrated``).


To use cProfile, the extension modules need to be rebuilt with profiling enabled.
This can be done by setting the ``CHARM4PY_BUILD_CPROFILE`` environment variable.
To enable cProfile, set ``charm.options.cprofiling`` to ``True`` before the runtime
starts. This will dump a profiling output file for each PE.
Note that the entry method that calls ``charm.exit()`` needs to be made a coroutine.

.. _Projections: https://charm.readthedocs.io/en/latest/projections/manual.html
8 changes: 7 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,14 +279,20 @@ def run(self):
else:
extra_link_args=["-Wl,-rpath,$ORIGIN/../.libs"]

compiler_directives = {}
if os.environ.get('CHARM4PY_BUILD_CPROFILE') == '1':
compiler_directives = {'profile': True}

extensions.extend(cythonize(setuptools.Extension('charm4py.charmlib.charmlib_cython',
sources=['charm4py/charmlib/charmlib_cython.pyx'],
include_dirs=['charm_src/charm/include'] + my_include_dirs,
library_dirs=[os.path.join(os.getcwd(), 'charm4py', '.libs')],
libraries=["charm"],
extra_compile_args=['-g0', '-O3'],
extra_link_args=extra_link_args,
), compile_time_env={'HAVE_NUMPY': haveNumpy}))
),
compiler_directives=compiler_directives,
compile_time_env={'HAVE_NUMPY': haveNumpy}))
else:
try:
check_cffi()
Expand Down