-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathchainreader.tex
More file actions
86 lines (75 loc) · 7.93 KB
/
Copy pathchainreader.tex
File metadata and controls
86 lines (75 loc) · 7.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
% -*- mode: latex; mode: visual-line; fill-column: 9999; coding: utf-8 -*-
\section{Performance of the ChainReader for Split Trajectories}
\label{sec:chainreader}
In section \ref{sec:splitting-traj} we showed how subfiling (splitting the trajectories) could help overcome I/O limitations and improve scaling.
However, the number of trajectories may not necessarily be equal to the number of processes.
For example, trajectories from MD simulations on supercomputers are often kept in small segments in individual files that need to be concatenated later to form a trajectory that can be analyzed with common tools.
Such segments might be useful for subfiling but making sure that the number of processes is equal to the number of trajectory files will not always be feasible.
\package{MDAnalysis} can transparently represent multiple trajectories as one virtual trajectory using the \emph{ChainReader}.
This feature is convenient for serial analysis when trajectories are maintained as segments.
In the current implementation of ChainReader, each process opens all the trajectory segment files but I/O will only happen from a specific block of the trajectory specific to that process only.
We wanted to test if the ChainReader would benefit from the gains measured for the subfiling approach.
Specifically, we measured if the MPI-parallelized RMSD task (with $N_{\text{p}}$ ranks) would benefit if the trajectory was split into $N_{\text{seg}} = N_{\text{p}}$ trajectory segments, corresponding to an ideal scenario.
\begin{figure}[!htb]
\centering
\begin{subfigure}{.4\textwidth}
\includegraphics[width=\linewidth]{figures/Comparison_tot_time_traj_splitting_chain-reader.pdf}
\caption{Scaling total}
\label{fig:MPItottime-chain-reader}
\end{subfigure}
\hfill
\begin{subfigure}{.4\textwidth}
\includegraphics[width=\linewidth]{figures/Comparison_Speed_UP_traj_splitting_chain-reader.pdf}
\caption{Speed-up}
\label{fig:MPIspeedup-chain-reader}
\end{subfigure}
\bigskip
\begin{subfigure}{.45\textwidth}
\includegraphics[width=\linewidth]{figures/IO_compute_scaling_traj_splitting-chain-reader.pdf}
\captionsetup{format=hang}
\caption{Scaling for different components}
\label{fig:MPIscaling-chain-reader}
\end{subfigure}
\hfill
\begin{subfigure}{.5\textwidth}
\includegraphics[width=\linewidth]{figures/chain-reader-no-ga-BarPlot-rank-comparison_192_5.pdf}
\captionsetup{format=hang}
\caption{Time comparison of different parts of the calculations per MPI rank}
\label{fig:MPIranks-split-chain-reader}
\end{subfigure}
\caption{Subfiling with the MDAnalysis \emph{ChainReader} for the RMSD task on \emph{SDSC Comet}.
Five repeats were performed to collect statistics.
The data for subfiling with one file per process (from Figure~\protect{\ref{fig:MPIwithIO-split}}) are shown for comparison as dotted lines in (a) and (b).
(a-c) The error bars show standard deviation with respect to the mean.
(d) Compute \tcomp, read I/O \tIO, communication \tcomm, opening the trajectory $t_{\text{opening\_trajectory}}$, ending the for loop $t_{\text{end\_loop}}$ (includes closing the trajectory file), and overheads $t_{\text{overhead1}}$, $t_{\text{overhead2}}$ per MPI rank. (See Table \ref{tab:notation} for the definitions.)}
\label{fig:MPIwithIO-split-chain-reader}
\end{figure}
In order to perform our experiments we had to work around an issue with the XTC format reader in \package{MDAnalysis} that was related to the XTC random-access functionality that the \texttt{MDAnalysis.coordinates.XTC.XTCReader} class provides:
The Gromacs XTC format \cite{Lindahl01, Spangberg:2011zr} is a lossy-compression, XDR-based file format that was never designed for random access and the compressed format itself does not support fast random seeking.
The \texttt{XTCReader} stores persistent offsets for trajectory frames to disk \cite{Gowers:2016aa} in order to enable efficient access to random frames.
These offsets will be generated automatically the first time a trajectory is opened and the offsets are stored in hidden \texttt{*.xtc\_offsets.npz} files.
The advantage of these persistent offset files is that after opening the trajectory for the first time, opening the same file will be very fast, and random access is immediately available.
However, stored offsets can get out of sync with the trajectory they refer to.
To prevent the use of stale offset data, trajectory file data (number of atoms, size of the file and last modification time) are also stored for validation.
If any of these parameters change the offsets are recalculated.
If the XTC changes but the offset file is not updated then the offset file can be detected as invalid.
With ChainReader in parallel, each process opens all the trajectories because each process builds its own \texttt{MDAnalysis.Universe} data structure.
If an invalid offset file is detected (perhaps because of wrong file modification timestamps across nodes), several processes might want to recalculate these parameters and rebuild the offset file, which can lead to a race condition.
In order to avoid the race condition, we removed this check from MDAnalysis for the purpose of the measurements presented here, but this comes at the price of not checking the validity of the offset files at all; future versions of MDAnalysis may lift this limitation.
We obtained the results for the ChainReader with this modified version of \package{MDAnalysis} that eliminates the race condition by assuming that XTC index files are always valid.
Figure \ref{fig:MPIwithIO-split-chain-reader} shows the results for performance of the ChainReader for the RMSD task.
Strong scaling plateaued for more than 92 cores and performance was worse than what was achieved when each MPI process was assigned its own trajectory segment as described in Section \ref{sec:splitting-traj} and shown for comparison as dotted lines in Figures~\ref{fig:MPItottime-chain-reader} and \ref{fig:MPIspeedup-chain-reader}.
The strong scaling performance did not suffer because of the computation and the read I/O because both \tcomp and \tIO showed excellent strong scaling up to 196 cores (Figure \ref{fig:MPIscaling-chain-reader}).
Instead the time for ending the \texttt{for} loop $t_{\text{end\_loop}}$, which includes the time for closing the trajectory file, and opening the trajectory $t_{\text{opening\_trajectory}}$ appeared to be the scaling bottleneck.
These results differed from the subfiling results (section \ref{sec:splitting-traj}) where neither $t_{\text{end\_loop}}$ nor $t_{\text{opening\_trajectory}}$ limited scaling (Figure \ref{fig:MPIranks-split}).
Although we did not further investigate the deeper cause for the reduced scaling performance of the ChainReader, we speculate that the primary problem is related to each MPI rank having to open all trajectory files in their ChainReader instance even though they will only read from a small subset.
For $N_{\text{p}}$ ranks and $N_{\text{seg}}$ file segments, in total, $N_{p } N_{\text{seg}}$ file opening/closing operations have to be performed.
Each server that is part of a Lustre file system can only handle a limited number of I/O requests (read, write, stat, open, close, etc.) per second.
A large number of such requests, from one or more users and one or more jobs, can lead to contention for storage resources.
For $N_{\text{p}} = N_{\text{seg}} = 100$, the Lustre file system has to perform 10,000 of these operations almost simultaneously, which might degrade performance.
These considerations indicate that the ChainReader in its current implementation limits scaling performance to less than 100 processes due to the large number of file opening operations.
For better performance, the ChainReader would need to be rewritten for MPI such that the layout of the trajectory files is initially obtained by MPI rank 0 (which has to sequentially open all trajectory segments once) and then communicated to all other ranks; each rank then only opens the trajectories it needs to access, thus reducing file access to a minimum.
%%% Local Variables:
%%% mode: latex
%%% TeX-master: t
%%% End: