Commit 835b4ffa authored by Pierre Augier's avatar Pierre Augier
Browse files

Profiling fluidsim.

parent a1b6f8f648ca
......@@ -29,14 +29,14 @@ vimtex:
doit: vimtex $(name).pdf
zathura $(name).pdf &
figdata:
syncdata:
python Python/sync.py
figures: figdata tmp/fig_compare_with_ns3d.pdf
figures: syncdata tmp/fig_compare_with_ns3d.pdf
Python/make_fig_profile.py
$(name).pdf: tmp/fig_compare_with_ns3d.pdf $(name).log
$(name).pdf: figures $(name).log
@# $(LATEX) $(name).tex
@if [ `grep "Package rerunfilecheck Warning: File" $(name).log | wc -l` != 0 ]; then $(LATEX) $(name).tex; fi
@if [ `grep "Rerun to get cross-references right." $(name).log | wc -l` != 0 ]; then $(LATEX) $(name).tex; fi
......
......@@ -2,8 +2,12 @@
import os
import getpass
from pathlib import Path
import matplotlib.pyplot as plt
from fluidsim.util.console import profile as pf
from fluiddyn.util import modification_date
from base import matplotlib_rc, titles, curdir
......@@ -21,15 +25,17 @@ if not os.path.exists(root):
patterns2d = [
# TODO: replace np=1 case
(root / 'beskow_1024x1024/').glob('*np=2*fftwmpi2d*'),
(root / 'beskow_1024x1024/').glob('*np=16*fftwmpi2d*'),
(root / 'beskow_1024x1024/').glob('*np=8*fftwmpi2d*'),
]
patterns3d = [
# TODO: replace np=1 case
(root / 'beskow_128x128x128/').glob('*np=2*fftwmpi3d*'),
(root / 'beskow_128x128x128/').glob('*np=8*fftwmpi3d*'),
(root / 'beskow_512x512x512/').glob('*np=64*fftwmpi3d*'),
(root / 'beskow_512x512x512/').glob('*np=128*fftwmpi3d*'),
(root / 'beskow_1024x1024x1024/').glob('*np=1024*fftwmpi3d*'),
# (root / 'beskow_1024x1024x1024/').glob('*np=1024*p3dfft*'),
# (root / 'beskow_1024x1024x1024/').glob('*np=1024*fftwmpi3d*'),
]
def paths_from_patterns(patterns):
......@@ -40,6 +46,10 @@ def paths_from_patterns(patterns):
return path_stats
paths2d = paths_from_patterns(patterns2d)
paths3d = paths_from_patterns(patterns3d)
def plot(path, ax, title='', **kwargs):
path_dir = os.path.dirname(path)
nb_dim = path_dir.split('_')[1].count('x') + 1
......@@ -63,32 +73,55 @@ def modif_title(title, path, with_resolution=True):
resol = resol.replace('x', r'$\times$')
if with_resolution:
title += ' ' + resol + ','
title += ' ' + nb_proc + ' processes'
title += ' ' + nb_proc + ' process'
if int(nb_proc) > 1:
title += 'es'
return title
pies2d = []
fig2d, axes2d = plt.subplots(1, 2)
for path, ax, title in zip(
paths_from_patterns(patterns2d), axes2d.ravel(), titles):
title = modif_title(title, path, with_resolution=False)
pies2d.append(plot(path, ax, title)) # labeldistance=0.))
pies3d = []
fig3d, axes3d = plt.subplots(2, 2)
for path, ax, title in zip(
paths_from_patterns(patterns3d), axes3d.ravel(), titles):
title = modif_title(title, path)
pies3d.append(plot(path, ax, title))
fig2d.set_size_inches(figx, figy)
fig3d.set_size_inches(figx, 2 * figy)
for fig, pies in zip((fig2d, fig3d), (pies2d, pies3d)):
fig.tight_layout()
if save:
path = curdir + '/../tmp/fig_profile{}.pdf'
fig2d.savefig(path.format('2d'))
fig3d.savefig(path.format('3d'))
modif_date_input_2d = min(modification_date(path) for path in paths2d)
modif_date_input_3d = min(modification_date(path) for path in paths3d)
path_fig = curdir + '/../tmp/fig_profile{}.pdf'
path_fig2d = path_fig.format('2d')
path_fig3d = path_fig.format('3d')
if Path(path_fig2d).exists():
modif_date_fig_2d = modification_date(path_fig2d)
has_to_make2d = modif_date_fig_2d < modif_date_input_2d
else:
has_to_make2d = True
if Path(path_fig3d).exists():
modif_date_fig_3d = modification_date(path_fig3d)
has_to_make3d = modif_date_fig_3d < modif_date_input_3d
else:
has_to_make3d = True
if has_to_make2d:
pies2d = []
fig2d, axes2d = plt.subplots(1, 2)
for path, ax, title in zip(paths2d, axes2d.ravel(), titles):
title = modif_title(title, path, with_resolution=False)
pies2d.append(plot(path, ax, title)) # labeldistance=0.))
fig2d.set_size_inches(figx, figy)
fig2d.tight_layout()
if save:
fig2d.savefig(path_fig2d)
if has_to_make3d:
pies3d = []
fig3d, axes3d = plt.subplots(2, 2)
for path, ax, title in zip(paths3d, axes3d.ravel(), titles):
title = modif_title(title, path)
pies3d.append(plot(path, ax, title))
fig3d.set_size_inches(figx, 2 * figy)
fig3d.tight_layout()
if save:
fig3d.savefig(path_fig3d)
if not save and (has_to_make2d or has_to_make3d):
plt.show()
......@@ -3,7 +3,7 @@ import os
import getpass
from subprocess import call
import matplotlib.pyplot as plt
# import matplotlib.pyplot as plt
from fluiddyn.util import modification_date
......@@ -36,21 +36,21 @@ else:
os.chdir(here)
def save_fig_scaling(dir_name, dim, n0, n1, n2=None):
path_dir = os.path.join(path_fluidsim_bench_results, dir_name)
path_fig = os.path.join(here_tmp, 'fig_' + dir_name + '.pdf')
if not os.path.exists(path_fig) or \
modification_date(path_dir) > modification_date(path_fig):
print('make fig', path_fig)
fig = plot_scaling(path_dir, None, dim, n0, n1, n2, show=False,
for_latex=True)
fig.set_size_inches(10, 5)
fig.suptitle('')
fig.tight_layout(
pad=1.02, h_pad=None, w_pad=None, rect=(-0.01, -0.02, 1, 1)
)
fig.savefig(path_fig, dpi=800)
# def save_fig_scaling(dir_name, dim, n0, n1, n2=None):
# path_dir = os.path.join(path_fluidsim_bench_results, dir_name)
# path_fig = os.path.join(here_tmp, 'fig_' + dir_name + '.pdf')
# if not os.path.exists(path_fig) or \
# modification_date(path_dir) > modification_date(path_fig):
# print('make fig', path_fig)
# fig = plot_scaling(path_dir, None, dim, n0, n1, n2, show=False,
# for_latex=True)
# fig.set_size_inches(10, 5)
# fig.suptitle('')
# fig.tight_layout(
# pad=1.02, h_pad=None, w_pad=None, rect=(-0.01, -0.02, 1, 1)
# )
# fig.savefig(path_fig, dpi=800)
#
# save_fig_scaling('legi_cluster8_320x640x640', '3d', 320, 640, 640)
......
......@@ -601,12 +601,12 @@ following sections.
\subsubsection*{Profiling}
It is straightforward to perform profiling with the help of the
\href{https://docs.python.org/3/library/profile.html}{cProfile} module,
available in the Python standard library. Here, we have analysed both 2D and
3D Navier-Stokes solvers in Beskow, and plotted them in
\href{https://docs.python.org/3/library/profile.html}{cProfile} module, available
in the Python standard library. Here, we have analyzed both 2D and 3D
Navier-Stokes solvers in Beskow, and plotted the results in
Fig.~\ref{fig:profile2d} and Fig.~\ref{fig:profile3d} respectively. Functions
which cumulatively consume less than $2\%$ of the total time are displayed in
the \emph{other} category.
which cumulatively consume less than $2\%$ of the total time are displayed in the
\emph{other} category.
% todo: redo these figures (API changes + better speed). Use with_pyfftw for
% sequential runs.
......@@ -616,7 +616,7 @@ the \emph{other} category.
\includegraphics[width=\linewidth]{tmp/fig_profile2d}
\caption{Profiling analysis of the 2D Navier-Stokes
(\codeinline{fluidsim.solvers.ns2d}) solver using a grid sized $1024\times1024$
(a) in sequential with \codeinline{fft2d.with\_fftw1d} operator and (b) with 16
(a) in sequential with \codeinline{fft2d.with\_fftw1d} operator and (b) with 8
processes with \codeinline{fft2d.mpi\_with\_fftwmpi2d}
operator.}\label{fig:profile2d}
\end{figure}
......@@ -624,38 +624,51 @@ operator.}\label{fig:profile2d}
In Fig.~\ref{fig:profile2d} both sequential and parallel profiles of the 2D
Navier-Stokes solver shows that majority of time is spent in inverse and forward
FFT calls (\codeinline{ifft\_as\_arg} and \codeinline{fft\_as\_arg}). For the
sequential solver, over $97\%$ of the time is spent in compiled C++ extensions,
built using \pack{Cython} and \pack{Pythran} and all pure Python functions limited
in the \emph{other} category. \pack{Cython} extensions are responsible for
interfacing with FFT operators and also for the time-step algorithm.
\pack{Pythran} extensions are used to translate most of the linear algebra
operations into optimized, statically compiled extensions. With 16 processes
deployed in parallel, time spent in compiled extensions falls to around $93\%$,
with increased contribution from \codeinline{tendencies\_nonlin}, a Python
function which is responsible for computing non-linear tendencies, or in other
words the advection term of the 2D Navier-Stokes equation.
sequential case, approximately 0.3\% of the time is spent in pure Python
functions, i.e. functions not built using \pack{Cython} and \pack{Pythran}.
%
\pack{Cython} extensions are responsible for interfacing with FFT operators and
also for the time-step algorithm. \pack{Pythran} extensions are used to translate
most of the linear algebra operations into optimized, statically compiled
extensions.
%
We also see that only 1.3\% of the time is not spent in the main six functions
(category \emph{other} in the figure).
%
With 8 processes deployed in parallel, time spent in pure Python function
increases to 1.1\% of the total time.
%
These results show that during the optimization process, we have to focus on a
very small number of functions.
\begin{figure}[htp]
\centering
\includegraphics[width=\linewidth]{tmp/fig_profile3d}
\caption{Profiling analysis of the 3D Navier-Stokes
(\codeinline{fluidsim.solvers.ns3d}) solver using a grid sized
$128\times128\times128$ solved (a) sequentially using
\codeinline{fft3d.with\_fftw3d} operator and (b) with 2 processes using
\codeinline{fft3d.mpi\_with\_fftwmpi3d} operator in the top row. In the bottom
row are similar results for the same solver using
\codeinline{fft3d.mpi\_with\_fftwmpi3d} operator (c) with 128 processes on a grid
sized $512\times512\times512$, and (d) with 1024 processes on a grid sized
$1024\times1024\times1024$.}\label{fig:profile3d}
\centering
\includegraphics[width=\linewidth]{tmp/fig_profile3d}
\caption{Profiling analysis of the 3D Navier-Stokes
(\codeinline{fluidsim.solvers.ns3d}) solver.
%
Top row: grid sized $128\times128\times128$ solved (a) sequentially using
\codeinline{fft3d.with\_fftw3d} operator and (b) with 8 processes using
\codeinline{fft3d.mpi\_with\_fftwmpi3d} operator.
%
Bottom row: grid sized $512\times512\times512$ using
\codeinline{fft3d.mpi\_with\_fftwmpi3d} operator (c) with 64 processes and (d)
with 128 processes.}
\label{fig:profile3d}
\end{figure}
From Fig.~\ref{fig:profile3d} it can be shown that, for the 3D Navier-Stokes
solver for all cases majority of time is attributed to FFT calls
(\codeinline{ifft} and \codeinline{fft}). The overall time spent in compiled
extensions range from around $70\%$ in sequential mode with $128^3$ grid points
to $90\%$ in parallel mode with $1024^3$ grid points. There is also substantial
contribution from the Python function \codeinline{vgradv\_from\_v} which is
responsible for computing the non-linear term in the solver.
solver for all cases majority of time is attributed to FFT calls. The overall time
spent in pure Python function range from 0.07\% for $512^3$ grid points and 64
processes to 0.46\% for $128^3$ grid points and 8 processes.
%
This percentage tends to increase with the number of processes used since the real
calculation done in compiled extensions take less time.
%
This percentage is also higher for the coarser resolution for the same reason.
%
However, the time in pure Python remains for all cases largely negligible compared
to the time spent in compiled extensions.
\subsubsection*{Scalability}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment