Python in High Performance Computing
Python in High Performance Computing
Knoxville, Tennessee
February 5, 2018
[Link]
Agenda
2 2
Administrative and Logistical Notes
● SAFETY: Please note emergency exit locations and watch for bags and
cords to avoid tripping hazards
● There is a shared Google Doc for asking questions and sharing code snippets:
[Link]
we will add a copy to the repo with slides and materials after the tutorial
● We will be doing hands-on exercises on NERSC Cori. If needed, training
accounts are available. (Thanks NERSC!) Please see Matt or William at the
first hands on if you didn’t get setup before the tutorial started.
● Slides and materials are available at:
LINK HERE
● The materials are an amalgamation of a decade of community efforts by
volunteers. Formatting might be wonky, but the information isn’t.
● Please ask questions. We’ll be watching the room and the Google Doc.
3 3
Tutorial Objectives
What we assume:
● You know and use Python
● You have some familiarity with the Scientific Python Stack, or
● You know and use HPC resources and are curious about using
Python in your own HPC work.
4 4
Why this tutorial? Why Python?
5 5
Why this tutorial?
• Python is popular
• It’s becoming the de facto language
for data science
• It’s behind a large number of
scientific workflows
• It’s not uncommon for prototyping [Link]
or even implementing production
software
[Link]
6 [Link]
Why Not Python?
• Performance is often a secondary concern for developers and distributions
• Most Python developers aren’t in HPC environments
• Most Python developers aren’t in science environments
• Many tools were designed to work best in generic environments
• Language maintainers favor consistency over compatibility
• Backwards compatibility is seldom guaranteed
• Low learning curve
• It’s easy to develop a code base that works, but won’t scale
• Sometimes Python isn’t the right tool
• Existing investments in commercial high-productivity languages (e.g. Matlab)
7
Why is Python Popular?
Makes a great first impression:
• Clean, clear syntax.
• Multi-paradigm, interpreted.
• Duck typing, garbage collection.
• Built-ins mostly map to C/C++ equivalents.
• Excellent built-in documentation.
Primary Uses:
● Script workflows for both data analysis and simulations
● Perform exploratory, interactive data analytics & viz
9 9
Choosing between Python 2 or 3
Python was originally developed as a system scripting language for the Amoeba distributed operating system
and has been developing ever since, with many backwards-incompatible changes made in the name of progress
without too much delay on adoption. However, the changes from Python 2 to Python 3 were sufficiently radical
that adoption has been slow going. That said:
10
Python at the HPC Center
Observation: High productivity has driven the growth of
Python in the sciences.
11 11
PyFR: Gordon Bell & SC16 Best Paper Finalist
[[Link]
12 [[Link]
12
Basic Guidelines for Python in HPC
● Identify and exploit parallelism at the core, node, and cluster levels.
● Control your environment and check for correctness.
● Understand and apply numpy array syntax and its broadcasting rules (skipped
here):
[Link]
[Link]
● Use mpi4py appropriately.
● Use community solvers.
● Measure your codes’ performance using profiling tools.
● Develop tests.
● Be part of the community.
13 13
Practical Matters:
Using Python at NERSC, ALCF, and OLCF
14 14
Python at NERSC, ALCF, & OLCF
15 15
Python Builds and Distributions
17 17
Customizing and Controlling Your Environment I: Virtualenv (cont’d)
#!/usr/bin/env python2.7
activate_this = '/path/to/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
N.B.: Packages installed in the venv will supercede versions installed at the site
level.
18 18
Customizing and Controlling Your Environment II: Conda
Anaconda provides the conda tool for creating a Conda “environment”:
● [Link]
● Create, update, share environments.
● Incompatible with virtualenv, replaces it.
● Many pre-built packages organized in custom “channels.”
● Leverage your center’s Anaconda install to create custom environments with the conda tool.
19 19
Python at NERSC
NERSC-built:
module load python[/2.7.9]
python_base/2.7.9
numpy/1.9.2
scipy/0.15.1
matplotlib/1.4.3 [default]
ipython/3.1.0
Anaconda:
module load python/2.7-anaconda
module load python/3.5-anaconda
NERSC-built: [default]
None
Anaconda:
module load [python/2.7-anaconda]
module load python/3.5-anaconda
21 21
Python at OLCF
Provided interpreters:
module load python[/2.7.9]
python/3.5.1
Major Provided Packages:
python_numpy/1.9.2
python_scipy/0.15.1
python_matplotlib/1.2.1
python_ipython/3.0.0
python_mpi4py/1.3.1
python_h5py/2.6.0
python_netcdf4/1.1.7
Anaconda:
● Prefer to build your own
● Generally interferes with Tcl Environment Modules
24 24
Structuring a HPC Python code
26
How does CPython work? (Part 2)
Let’s use that to calculate an array of circle areas:
27
Can we improve things in pure Python?
List comprehensions seem to simplify things…
28
Takeaways on CPython
● While you can improve pure Python performance through language features
running in CPython, it won’t deliver the efficiency of compiled code.
29 29
Parallelism & Python: A Word on the GIL
To keep memory coherent, Python only allows a single thread to run in the
interpreter's memory space at once. This is enforced by the Global Interpreter
Lock, or GIL.
For the gory details, see David Beazley's talk on the GIL:
[Link]
30 30
NumPy and SciPy
NumPy should almost always be your first stop for performance
improvement. Compiled from C and FORTRAN 77, it provides:
• Numerical data types that ease working with C/C++/Fortran
• N-dimensional homogeneous arrays (ndarray)
• Universal functions (ufunc)
• Built-in linear algebra, FFT, PRNGs
• Tools for integrating with C/C++/Fortran
• Heavy lifting done by optimized C/Fortran libraries such as Intel’s
MKL, OpenBLAS, or IBM’s ESSL
31
How NumPy is built matters:
Optimized and built with MKL via Spack Installed via pip
32
Checking your NumPy Configuration:
NumPy’s distutils can give insight into compilers and options used:
>>> import numpy
>>> import [Link]
>>> np_config_vars = [Link].get_config_vars()
>>> # np_config_vars is a dict with configuration values
>>> import pprint
>>> # pprint is a pretty printer and not required, just recommended
>>> [Link](np_config_vars)
{'AC_APPLE_UNIVERSAL_BUILD': 0,
'AIX_GENUINE_CPLUSPLUS': 0,
'AR': 'ar',
'ARCH': 'x86_64',
'ARFLAGS': 'rc',
...
33
NumPy Data Types
NumPy covers all the same numeric data types available in C/C++ and Fortran as
variants of int, float, and complex:
34
Creating NumPy Arrays
35
Slicing NumPy Arrays (Part 1)
>>> a = [Link]([[1,2,3,4],[9,8,7,6],[1,6,5,4]])
>>> arow = a[0,:] # get slice referencing row zero
>>> arow
array([1, 2, 3, 4])
36
Slicing NumPy Arrays (Part 2)
# NOTE: arow & cols are NOT copies, they point to the original data
>>> arow
array([1, 2, 3, 4])
>>> arow[:] = 0
>>> arow
array([0, 0, 0, 0])
>>> a
array([[0, 0, 0, 0],
[9, 8, 7, 6],
[1, 6, 5, 4]])
37
Broadcasting with universal functions (ufuncs)
Applies operations to many elements with a single call – with compiled code
>>> a = [Link](([1,2,3,4],[8,7,6,5]))
>>> a
array([[1, 2, 3, 4],
[8, 7, 6, 5]])
Rule 1: Dimensions of one may be prepended to either array to match the array with the greatest number of
dimensions
>>> a + 1 # add 1 to each element in array
array([[2, 3, 4, 5],
[9, 8, 7, 6]])
Rule 2: Arrays may be repeated along dimensions of length 1 to match the size of a larger array
>>> a + [Link](([1],[10])) # add 1 to 1st row, 10 to 2nd row
array([[ 2, 3, 4, 5],
[18, 17, 16, 15]])
● Where bindings for a library aren’t available, it’s often easy to generate them
39 39
Developing Your Own Bindings and Compiled Modules
40 40
Developing Your Own Modules: Cython
41 41
Developing Your Own Modules: Cython
Using cython -a ${sourcefile}.{pyx,py}, we can get guidance on where a
module built with Cython would have to interact with CPython and lose performance:
42
42
Developing Your Own Modules: f2py
f2py comes with NumPy and can be used to rapidly generate wrappers for Fortran code
43 43
Other Tools for Performance
There are a handful of projects that seek to improve performance of pure Python
code. Two noteworthy options are:
44 44
Python Parallelism
45 45
Overview of Parallel and Distributed Programming Options
threading
● useful for certain concurrency issues, not really usable for parallel computing due to the GIL
subprocess
● relatively low level control for spawning and managing processes, think popen
MPI
● mpi4py exposes your full local MPI API within Python
● as scalable as your local MPI
47
Why not MPI?
• Generally unsupported outside HPC contexts
o Packages provided with a distribution may be highly un-tuned
o Commercial cloud services generally don’t have fast interconnects
• Mixing programming paradigms can be messy
o MPI applications are generally synchronous – you only compute as fast as the
slowest process
o Generally projects use MPI+X where X is node-local
o Mixing threading paradigms is generally a recipe for disaster
• There can be a steep learning curve
o Simple to learn, but difficult to master
o APIs aren’t generally taught in CS programs
o Best tools for debugging and profiling are generally commercial
o Performance gains aren’t automatic
48
Python and MPI
o Python was originally developed as a system scripting language for the
Amoeba distributed operating system
o Folks have been writing Python MPI bindings since at least 1996
• David Beazley may have started this…
• Other contenders: Pypar (Ole Nielsen), pyMPI (Patrick Miller, et al), Pydusa
( Timothy H. Kaiser), and Boost MPI Python (Andreas Klöckner and Doug
Gregor)
• The community has mostly settled on mpi4py by Lisandro Dalcin
• You can mix bindings, libraries, and languages – just watch the MPI you link
and your data types
o ALCF has required vendor support as part of machine acquisitions since at
least 2003.
o Python 3 is the future – and the future is here
• All major libraries now work under Python 3.5
• Python 3’s loader and internals are more I/O intensive which presents
49
challenges for scaling
mpi4py: why mpi4py?
● [Link]
50 50
mpi4py: running
51 51
mpi4py: startup
○ calling Init() or Init_thread() more than once violates the MPI standard
○ This will lead to a Python exception or an abort in C/C++
○ use Is_initialized() to test for initialization
52 52
mpi4py: shutdown
53 53
mpi4py and program structure
Any code, even if after MPI_Init(), unless reserved to a given rank will run on all
ranks:
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
mpisize = comm.Get_size()
if rank%2 == 0:
print(“Hello from an even rank: %d” %(rank))
[Link]()
print(“Goodbye from rank %d” %(rank))
54
mpi4py and datatypes
● Python objects, unless they conform to a C data type, are pickled (serialized)
○ pickling and unpickling have significant compute overhead
○ instances of such classes whose __dict__() or the result of calling __getstate__() is picklable
55
mpi4py and datatypes
● When in doubt, ask if what is being processed can be represented as memory buffer or if it can
only be represented in C as PyObject
56
mpi4py: communicators
57 57
mpi4py: collectives and operations
● Collectives operating on Python objects are naïve
○ Iterable types generally get cast to a list – this has some interesting side-effects
○ Operators work about the way one would expect with Python types
58 58
mpi4py: crashing
If you crash or have trouble running simple codes:
● Remember: CPython is a C binary and mpi4py is a binding
● You will likely get core files and mangled stack traces
● Use ld or otool to check which MPI mpi4py is linked against
○ mpi4py.get_config() will show you the contents of [Link] used at build time and is
generally of limited utility
● Ensure Python, mpi4py, and your code are available on all nodes and libraries and paths are
correct
● Try running with a single rank
● Rebuild binary modules with debugging symbols
● The default error handler is MPI.ERRORS_RETURN which allows the use of Python exception
handling, but can allow for silent death in C/C++/Fortan MPI code.
○ Use MPI.{Comm|Win|File}.Set_errhandler() to set MPI.ERRORS_ARE_FATAL on any
communicator, memory window, or file you pass into C/C++/Fortan MPI code.
○ Use MPI.{Comm|Win|File}.Get_errhandler() to check the error handler on any
communicator, memory window, or file passed from C/C++/Fortan MPI code.
59 59
Parallel I/O and h5py
● General Python I/O isn’t MPI-safe
○ As in any other language, reads are safe though there may be locking issues
○ Likewise, if you must use Python I/O, write a file per MPI rank or thread
60 60
Parallel I/O and h5py
● h5py 2.2.0 and later support parallel I/O
● Requires mpi4py and the mpi used to compile hdf5, mpi4py, and h5py must be
the same.
● Beware pre-packaged h5py and hdf5 – it’s frequently serial
● Confirm h5py’s MPI support before using:
>>> import h5py
>>> h5py.get_config().mpi
True
● While setup is sometime trouble, it is as easy to use as:
f = [Link]('myfile.hdf5', 'w',
driver='mpio', comm=MPI.COMM_WORLD)
● All changes to file structure or metadata of a file must be performed on all
ranks with an open file
61 61
Issues Affecting Python at Scale
worse
GPFS
DVS
better
R/O Burst
Caching Buffer GPFS Lustre
● Python’s “import” statement is file metadata intensive (.py, .pyc, .so open/stat calls).
● Becomes more severe as the number of Python processes trying to access files increases.
● Result: Very slow times to just start Python applications at larger concurrency (MPI).
● Storage local to compute nodes, use of containers (Shifter) helps fix:
○ Eliminates metadata calls off the compute nodes.
○ In containers, paths to .so libraries can be cached via ldconfig.
● Other approaches:
○ Ship software stack to compute nodes (e.g., python-mpi-bcast).
○ Install software to read-only/cache-enabled file systems.
○ See also Spindle or collfs (Scalable Shared Library Loading). 62
62
Hands-on Exercise 2: Using mpi4py
Instructions:
1. Grab a node:
salloc -N 1 -q regular -t 240 -C haswell -A ntrain --reservation=ecp_python
2. Activate the environment setup earlier:
source activate myenv
3. Change to the directory basics
cd ~/ecp_python_tutorial/basics
4. Run basic_features.py on 8 ranks:
srun -n 8 -c 1 python basic_features.pybasic_features.py
63 63
Hands-on Exercise 2: Using mpi4py (part 2)
Instructions:
64 64
Hands-on Exercise 2: Using mpi4py
10. Run threads_pi.py with 1, 8, and 16 threads with the same sample count:
./threads_pi.py 12000000 1
./threads_pi.py 12000000 8
./threads_pi.py 12000000 16
11. What does this tell us about native Python threads?
65 65
Basic Profiling: cProfile & SnakeViz
cProfile
Low-overhead profiler, from standard library.
Outputs statistics on what your code is doing:
Number of function calls,
Total time spend in functions,
Time per function call, etc.
[[Link]
[[Link]
[[Link]
SnakeViz
Lets you visualize cProfile output in a browser:
Statistics mentioned above.
Visualize call stack & drill-down.
Thread timelines.
Hotspot analysis.
Memory profiling.
Locks & waits.
Filter/zoom in timeline.
[[Link]
Run GUI (amplxe-gui) over NX! [[Link]
Roofline analysis*:
Performance of code in relation
to hardware limits.
Memory bandwidth or compute
bound?
68 68
[* Roofline: An upcoming IDEAS Webinar topic.]
Getting Started with Python Resources
● [Link]
● [Link]
● [Link]
● [Link]
● [Link]
● [Link]
[Link]
69 69
More Resources
Your NERSC and LCF Python contacts:
● NERSC: Rollin Thomas rcthomas@[Link]
● ALCF: William Scullin wscullin@[Link]
● OLCF: Matt Belhorn belhornmp@[Link]
Documentation:
● NERSC: [Link]
● OLCF: [Link]
practices/
Other presentations:
● ALCF Performance Workshop (May 2017):
Python on HPC Best Practices [Link]
● NERSC Intel Python Training Event (March 2017):
Optimization Example [Link]
by Oleksandr Pavlyk (Intel)
70 70
Conclusion
● NERSC, ALCF, and OLCF recognize, welcome, and want to support new and
experienced Python users in HPC.
● Using Python on our systems can be as easy as a module load, but can be
customized by users.
● We have provided some guidance and best practices to help users improve
Python performance in HPC context.
● Try out some of the profiling and performance analysis tools described here,
and ask for help if you get stuck.
● While there are many challenges for Python in HPC, if users, staff, & vendors
work together, there are many rewards.
71 71
Thank you!
[Link]
Cross-Compiling on Cray XC30s with pip
Instruct Cray compiler wrappers to target the login node architecture so code will run everywhere
module unload craype-interlagos
module load craype-istanbul
# If pip is badly out of date, the TLS certificates may not be trusted.
pip install --trusted-host [Link] --upgrade pip
Set envvars needed to guide pip for cross-compiling and instruct it to build from source
CC=cc MPICC=cc pip install -v --no-binary :all: mpi4py
Set envvars needed for pip to use external dependencies. See package documentation.
HDF5_DIR="${CRAY_HDF5_DIR}/${PE_ENV}/${GNU_VERSION%.*}"
CC=cc HDF5_MPI="ON" HDF5_DIR="${HDF5_DIR}" pip install -v --no-binary :all: h5py
deactivate "${VENV_NAME}"
module unload craype-istanbul
module load craype-interlagos
73 73