From 44b8f845847505b81dc0f1199c49e67a495ed7a0 Mon Sep 17 00:00:00 2001 From: couturie Date: Fri, 7 Dec 2012 20:15:43 +0100 Subject: [PATCH] ajout chap6 --- BookGPU/BookGPU.tex | 4 + BookGPU/Chapters/chapter6/Conclu.tex | 25 + BookGPU/Chapters/chapter6/Intro.tex | 34 + BookGPU/Chapters/chapter6/Makefile | 21 + BookGPU/Chapters/chapter6/PartieAsync.tex | 1168 +++++++++++++++++ BookGPU/Chapters/chapter6/PartieORWL.tex | 359 +++++ BookGPU/Chapters/chapter6/PartieSync.tex | 713 ++++++++++ BookGPU/Chapters/chapter6/biblio6.bib | 310 +++++ BookGPU/Chapters/chapter6/ch6.aux | 147 +++ BookGPU/Chapters/chapter6/ch6.tex | 89 ++ .../chapter6/curves/gpuSyncOverlap.pdf | Bin 0 -> 7518 bytes BookGPU/Chapters/chapter6/curves/recouvs.pdf | Bin 0 -> 7483 bytes .../Chapters/chapter6/curves/syncasync.pdf | Bin 0 -> 7072 bytes .../Sync-CompleteInterleaveOverlap.pdf | Bin 0 -> 44466 bytes .../chapter6/figures/Sync-NativeOverlap.pdf | Bin 0 -> 9934 bytes .../figures/Sync-SeqSequenceOverlap.pdf | Bin 0 -> 55734 bytes .../figures/Sync-StreamSequenceOverlap.pdf | Bin 0 -> 56256 bytes BookGPU/Makefile | 3 +- 18 files changed, 2872 insertions(+), 1 deletion(-) create mode 100644 BookGPU/Chapters/chapter6/Conclu.tex create mode 100644 BookGPU/Chapters/chapter6/Intro.tex create mode 100644 BookGPU/Chapters/chapter6/Makefile create mode 100644 BookGPU/Chapters/chapter6/PartieAsync.tex create mode 100644 BookGPU/Chapters/chapter6/PartieORWL.tex create mode 100755 BookGPU/Chapters/chapter6/PartieSync.tex create mode 100644 BookGPU/Chapters/chapter6/biblio6.bib create mode 100644 BookGPU/Chapters/chapter6/ch6.aux create mode 100755 BookGPU/Chapters/chapter6/ch6.tex create mode 100644 BookGPU/Chapters/chapter6/curves/gpuSyncOverlap.pdf create mode 100644 BookGPU/Chapters/chapter6/curves/recouvs.pdf create mode 100644 BookGPU/Chapters/chapter6/curves/syncasync.pdf create mode 100644 BookGPU/Chapters/chapter6/figures/Sync-CompleteInterleaveOverlap.pdf create mode 100755 BookGPU/Chapters/chapter6/figures/Sync-NativeOverlap.pdf create mode 100644 BookGPU/Chapters/chapter6/figures/Sync-SeqSequenceOverlap.pdf create mode 100644 BookGPU/Chapters/chapter6/figures/Sync-StreamSequenceOverlap.pdf diff --git a/BookGPU/BookGPU.tex b/BookGPU/BookGPU.tex index 9ec4258..293141d 100755 --- a/BookGPU/BookGPU.tex +++ b/BookGPU/BookGPU.tex @@ -21,6 +21,9 @@ \usepackage{multirow} \usepackage{layouts} \usepackage{comment} +\usepackage{xcolor} +\usepackage{upquote} +\usepackage{framed} \frenchspacing \tolerance=5000 @@ -114,6 +117,7 @@ \part{This is a Part} \include{Chapters/chapter1/ch1} \include{Chapters/chapter2/ch2} +\include{Chapters/chapter6/ch6} \include{Chapters/chapter8/ch8} \include{Chapters/chapter11/ch11} \include{Chapters/chapter14/ch14} diff --git a/BookGPU/Chapters/chapter6/Conclu.tex b/BookGPU/Chapters/chapter6/Conclu.tex new file mode 100644 index 0000000..8571a7f --- /dev/null +++ b/BookGPU/Chapters/chapter6/Conclu.tex @@ -0,0 +1,25 @@ +\section{Conclusion}\label{ch6:conclu} + +In this chapter, different methodologies that effectively take advantage of +current cluster technologies with GPUs have been presented. Beyond the simple +collaboration of several nodes that include GPUs, we have addressed parallel +schemes to efficiently overlap communications with computations (including GPU +transfers), and also computations on the CPU with computations on the +GPU. Moreover, parallel schemes for synchronous as well as asynchronous +iterative processes have been proposed. The proposed schemes have been validated +experimentally and provide the expected behavior. Finally, as a prospect we have +developed a programming tool that will, in middle or long term, provide all the +required technical elements to implement the proposed schemes in a single tool, +without requiring several external libraries. + +We conclude that GPU computations are very well suited to achieve overlap with +CPU computations and communications and they can be fully integrated in +algorithmic schemes combining several levels of parallelism. + +%%% Local Variables: +%%% mode: latex +%%% fill-column: 80 +%%% ispell-dictionary: "american" +%%% mode: flyspell +%%% TeX-master: "../../BookGPU.tex" +%%% End: diff --git a/BookGPU/Chapters/chapter6/Intro.tex b/BookGPU/Chapters/chapter6/Intro.tex new file mode 100644 index 0000000..5a8d0e7 --- /dev/null +++ b/BookGPU/Chapters/chapter6/Intro.tex @@ -0,0 +1,34 @@ +\section{Introduction}\label{ch6:intro} + +This chapter proposes to draw several development methodologies to obtain +efficient codes in classical scientific applications. Those methodologies are +based on the feedback from several research works involving GPUs, either alone +in a single machine or in a cluster of machines. Indeed, our past +collaborations with industries have allowed us to point out that in their +economical context, they can adopt a parallel technology only if its +implementation and maintenance costs are small according to the potential +benefits (performance, accuracy,...). So, in such contexts, GPU programming is +still regarded with some distance according to its specific field of +applicability (SIMD/SIMT model) and its still higher programming complexity and +maintenance. In the academic domain, things are a bit different but studies for +efficiently integrating GPU computations in multi-core clusters with maximal +overlapping of computations with communications and/or other computations, are +still rare. + +For these reasons, the major aim of that chapter is to propose as simple as +possible general programming patterns that can be followed or adapted in +practical implementations of parallel scientific applications. +% Also, according to our experience in industrial collaborations, we propose a +% small prospect analysis about the perenity of such accelerators in the +% middle/long term. +Also, we propose in a third part, a prospect analysis together with a particular +programming tool that is intended to ease multi-core GPU cluster programming. + + +%%% Local Variables: +%%% mode: latex +%%% fill-column: 80 +%%% ispell-dictionary: "american" +%%% mode: flyspell +%%% TeX-master: "../../BookGPU.tex" +%%% End: diff --git a/BookGPU/Chapters/chapter6/Makefile b/BookGPU/Chapters/chapter6/Makefile new file mode 100644 index 0000000..9c18dac --- /dev/null +++ b/BookGPU/Chapters/chapter6/Makefile @@ -0,0 +1,21 @@ +NOM=ch6 +CIBLE=$(NOM).pdf +SOURCES=$(wildcard *.tex) +DEPOT ?= . +WorkDir = `basename $(PWD)` +ARCHIVE=$(WorkDir)_BookGPU_`date +"%Y-%m-%d"`.tar.gz + +$(CIBLE): $(SOURCES) + @printf "Compilation du chapitre..." + @make -C ../.. + @printf "terminée.\n" + +archive: + @printf "Archivage local vers %s ... " "$(ARCHIVE)" + @cd ..; tar zcf $(DEPOT)/$(ARCHIVE) $(WorkDir)/Makefile $(WorkDir)/*.tex $(WorkDir)/*.bib $(WorkDir)/figures; cd - > /dev/null + @printf "terminé.\n" + +clean: + @printf "Nettoyage du répertoire..." + @rm -f *~ *.aux *.log *.toc *.bbl ../../*~ ../../*.aux ../../*.log ../../*.toc ../../*.ind ../../*.bbl + @printf "terminé.\n" \ No newline at end of file diff --git a/BookGPU/Chapters/chapter6/PartieAsync.tex b/BookGPU/Chapters/chapter6/PartieAsync.tex new file mode 100644 index 0000000..44eadd5 --- /dev/null +++ b/BookGPU/Chapters/chapter6/PartieAsync.tex @@ -0,0 +1,1168 @@ +\section{General scheme of asynchronous parallel code with +computation/communication overlapping} +\label{ch6:part2} + +In the previous part, we have seen how to efficiently implement overlap of +computations (CPU and GPU) with communications (GPU transfers and inter-node +communications). However, we have previously shown that for some parallel +iterative algorithms, it is sometimes even more efficient to use an asynchronous +scheme of iterations\index{iterations!asynchronous} \cite{HPCS2002,ParCo05,Para10}. In that case, the nodes do +not wait for each others but they perform their iterations using the last +external data they have received from the other nodes, even if this +data was produced \emph{before} the previous iteration on the other nodes. + +Formally, if we denote by $f=(f_1,...,f_n)$ the function representing the +iterative process and by $x^t=(x_1^t,...,x_n^t)$ the values of the $n$ elements of +the system at iteration $t$, we pass from a synchronous iterative scheme of the +form: +\begin{algorithm}[H] + \caption{Synchronous iterative scheme}\label{algo:ch6p2sync} + \begin{Algo} + $x^{0}=(x_{1}^{0},...,x_{n}^{0})$\\ + \textbf{for} $t=0,1,...$\\ + \>\textbf{for} $i=1,...,n$\\ + \>\>$x_{i}^{t+1}=f_{i}(x_{1}^t,...,x_i^t,...,x_{n}^t)$\\ + \>\textbf{endfor}\\ + \textbf{endfor} + \end{Algo} +\end{algorithm} +\noindent +to an asynchronous iterative scheme of the form: +\begin{algorithm}[H] + \caption{Asynchronous iterative scheme}\label{algo:ch6p2async} + \begin{Algo} + $x^{0}=(x_{1}^{0},...,x_{n}^{0})$\\ + \textbf{for} $t=0,1,...$\\ + \>\textbf{for} $i=1,...,n$\\ + \>\>$x_{i}^{t+1}=\left\{ + \begin{array}[h]{ll} + x_i^t & \text{if } i \text{ is \emph{not} updated at iteration } i\\ + f_i(x_1^{s_1^i(t)},...,x_n^{s_n^i(t)}) & \text{if } i \text{ is updated at iteration } i + \end{array} + \right.$\\ + \>\textbf{endfor}\\ + \textbf{endfor} + \end{Algo} +\end{algorithm} +where $s_j^i(t)$ is the iteration number of the production of the value $x_j$ of +element $j$ that is used on element $i$ at iteration $t$ (see for example~\cite{BT89, + FS2000} for further details). +Such schemes are called AIAC\index{AIAC} for \emph{Asynchronous Iterations and + Asynchronous Communications}. They combine two aspects that are respectively +different computation speeds of the computing elements and communication delays +between them. + +The key feature of such algorithmic schemes +is that they may be faster than their synchronous counterparts due to the +implied total overlap of computations with communications: in fact, this scheme +suppresses all the idle times induced by nodes synchronizations between each iteration. + +However, the efficiency of such a scheme is directly linked to the frequency at +which new data arrives on each node. Typically, +if a node receives newer data only every four or five local iterations, it is +strongly probable that the evolution of its local iterative process will be +slower than if it receives data at every iteration. The key point here is that +this frequency does not only depend on the hardware configuration of the +parallel system but it also depends on the software that is used to +implement the algorithmic scheme. + +The impact of the programming environments used to implement asynchronous +algorithms has already been investigated in~\cite{SuperCo05}. Although the +features required to efficiently implement asynchronous schemes have not +changed, the available programming environments and computing hardware have +evolved, in particular now GPUs are available. So, there is a need to reconsider the +implementation schemes of AIAC according to the new de facto standards for parallel +programming (communications and threads) as well as the integration of the GPUs. +One of the main objective here is to obtain a maximal overlap between the +activities of the three types of devices that are the CPU, the GPU and the +network. Moreover, another objective is to present what we think +is the best compromise between the simplicity of the implementation and its +maintainability on one side and its +performance on the other side. This is especially important for industries where +implementation and maintenance costs are strong constraints. + +For the sake of clarity, we present the different algorithmic schemes in a progressive +order of complexity, from the basic asynchronous scheme to the complete scheme +with full overlap. Between these two extremes, we propose a synchronization +mechanism on top of our asynchronous scheme that can be used either statically or +dynamically during the application execution. + +Although there exist several programming environments for inter-node +communications, multi-threading and GPU programming, a few of them have +become \emph{de facto standards}, either due to their good stability, their ease +of use and/or their wide adoption by the scientific community. +Therefore, as in the previous section all the schemes presented in the following use MPI~\cite{MPI}, +OpenMP~\cite{openMP} and CUDA~\cite{CUDA}. However, there is no loss of +generality as those schemes may easily be implemented with other libraries. + +Finally, in order to stay as clear as possible, only the parts of code and +variables related to the control of parallelism (communications, threads,...) +are presented in our schemes. The inner organization of data is not detailed as +it depends on the application. We only consider that we have two data arrays +(previous version and current version) and communication buffers. However, in +most of the cases, those buffers can correspond to the data arrays themselves to +avoid data copies. + +\subsection{A basic asynchronous scheme} +\label{ch6:p2BasicAsync} + +The first step toward our complete scheme is to implement a basic asynchronous +scheme that includes an actual overlap of the communications with the +computations\index{overlap!computation and communication}. In order to ensure that the communications are actually performed +in parallel of the computations, it is necessary to use different threads. It +is important to remember that asynchronous communications provided in +communication libraries like MPI are not systematically performed in parallel of +the computations~\cite{ChVCV13,Hoefler08a}. So, the logical and classical way +to implement such an overlap is to use three threads: one for +computing, one for sending and one for receiving. Moreover, since +the communication is performed by threads, blocking synchronous communications\index{MPI!communication!blocking}\index{MPI!communication!synchronous} +can be used without deteriorating the overall performance. + +In this basic version, the termination\index{termination} of the global process is performed +individually on each node according to their own termination. This can be guided by either a +number of iterations or a local convergence detection\index{convergence detection}. The important step at +the end of the process is to perform the receptions of all pending +communications in order to ensure the termination of the two communication +threads. + +So, the global organization of this scheme is set up in \Lst{algo:ch6p2BasicAsync}. + +% \begin{algorithm}[H] +% \caption{Initialization of the basic asynchronous scheme.} +% \label{algo:ch6p2BasicAsync} +\begin{Listing}{algo:ch6p2BasicAsync}{Initialization of the basic asynchronous scheme} +// Variables declaration and initialization +omp_lock_t lockSend; // Controls the sendings from the computing thread +omp_lock_t lockRec; // Ensures the initial reception of external data +char Finished = 0; // Boolean indicating the end of the process +char SendsInProgress = 0; // Boolean indicating if previous data sendings are still in progress +double Threshold; // Threshold of the residual for convergence detection + +// Parameters reading +... + +// MPI initialization +MPI_Init_thread(argc, argv, MPI_THREAD_MULTIPLE, &provided); +MPI_Comm_size(MPI_COMM_WORLD, &nbP); +MPI_Comm_rank(MPI_COMM_WORLD, &numP); + +// Data initialization and distribution among the nodes +... + +// OpenMP initialization (mainly declarations and setting up of locks) +omp_set_num_threads(3); +omp_init_lock(&lockSend); +omp_set_lock(&lockSend); // Initially locked, unlocked to start sendings +omp_init_lock(&lockRec); +omp_set_lock(&lockRec); // Initially locked, unlocked when initial data are received + +#pragma omp parallel +{ + switch(omp_get_thread_num()){ + case COMPUTATION : + computations(... @\emph{relevant parameters}@ ...); + break; + + case SENDINGS : + sendings(); + break; + + case RECEPTIONS : + receptions(); + break; + } +} + +// Cleaning of OpenMP locks +omp_test_lock(&lockSend); +omp_unset_lock(&lockSend); +omp_destroy_lock(&lockSend); +omp_test_lock(&lockRec); +omp_unset_lock(&lockRec); +omp_destroy_lock(&lockRec); + +// MPI termination +MPI_Finalize(); +\end{Listing} +%\end{algorithm} + +% \ANNOT{Est-ce qu'on laisse le 1er échange de données ou pas dans +% l'algo~\ref{algo:ch6p2BasicAsyncComp} ? (lignes 16-19)\\ +% (ça n'est pas nécessaire si les données de +% départ sont distribuées avec les dépendances qui correspondent, sinon il faut +% le faire)} + +In this scheme, the \texttt{lockRec} mutex\index{OpenMP!mutex} is not mandatory. +It is only used to ensure that data dependencies are actually exchanged at the +first iteration of the process. Data initialization and distribution +(lines~16-17) are not detailed here because they are directly related to the +application. The important point is that, in most cases, they should be done +before the iterative process. The computing function is given in +\Lst{algo:ch6p2BasicAsyncComp}. + +%\begin{algorithm}[H] +% \caption{Computing function in the basic asynchronous scheme.} +% \label{algo:ch6p2BasicAsyncComp} +\begin{Listing}{algo:ch6p2BasicAsyncComp}{Computing function in the basic asynchronous scheme} +// Variables declaration and initialization +int iter = 1; // Number of the current iteration +double difference; // Variation of one element between two iterations +double residual; // Residual of the current iteration + +// Computation loop +while(!Finished){ + // Sendings of data dependencies if there is no previous sending in progress + if(!SendsInProgress){ + // Potential copy of data to be sent in additional buffers + ... + // Change of sending state + SendsInProgress = 1; + omp_unset_lock(&lockSend); + } + + // Blocking receptions at the first iteration + if(iter == 1){ + omp_set_lock(&lockRec); + } + + // Initialization of the residual + residual = 0.0; + // Swapping of data arrays (current and previous) + tmp = current; // Pointers swapping to avoid + current = previous; // actual data copies between + previous = tmp; // the two data versions + // Computation of current iteration over local data + for(ind=0; ind residual){ + residual = difference; + } + } + + // Checking of the end of the process (residual under threshold) + // Other conditions can be added to the termination detection + if(residual <= Threshold){ + Finished = 1; + omp_unset_lock(&lockSend); // Activation of end messages sendings + MPI_Ssend(&Finished, 1, MPI_CHAR, numP, tagEnd, MPI_COMM_WORLD); + } + + // Updating of the iteration number + iter++; +} +\end{Listing} +%\end{algorithm} + +As mentioned above, it can be seen in line~18 of \Lst{algo:ch6p2BasicAsyncComp} +that the \texttt{lockRec} mutex is used only at the first iteration to wait for +the initial data dependencies before the computations. The residual\index{residual}, initialized +in line~23 and computed in lines~34-37, is defined by the maximal difference +between the elements from two consecutive iterations. It is classically used to +detect the local convergence of the process on each node. In the more +complete schemes presented in the sequel, a global termination detection that +takes the states of all the nodes into account will be exhibited. + +Finally, the local convergence is tested and updated when necessary. In line~44, +the \texttt{lockSend} mutex is unlocked to allow the sending function to send +final messages to the dependency nodes. Those messages are required to keep the +reception function alive until all the final messages have been received. +Otherwise, a node could stop its reception function whereas other nodes are +still trying to communicate with it. Moreover, a local sending of a final +message to the node itself is required (line~45) to ensure that the reception +function will not stay blocked in a message probing +(see~\Lst{algo:ch6p2BasicAsyncReceptions}, line~11). This may happen if the node +receives the final messages from its dependencies \emph{before} being itself in +local convergence. + +All the messages but this final local one are performed in the sending function +described in \Lst{algo:ch6p2BasicAsyncSendings}. + +The main loop is only conditioned by the end of the computing process (line~4). +At each iteration, the thread waits for the permission from the computing thread +(according to the \texttt{lockSend} mutex). Then, data are sent with +blocking synchronous communications. The \texttt{SendsInProgress} boolean +allows the computing thread to skip data sendings as long as a previous sending +is in progress. This skip is possible due to the nature of asynchronous +algorithms that allows such \emph{message loss}\index{message!loss/miss} or \emph{message miss}. After +the main loop, the final messages are sent to the dependencies of the node. + +%\begin{algorithm}[H] +% \caption{Sending function in the basic asynchronous scheme.} +% \label{algo:ch6p2BasicAsyncSendings} +\begin{Listing}{algo:ch6p2BasicAsyncSendings}{Sending function in the basic asynchronous scheme} +// Variables declaration and initialization +... + +while(!Finished){ + omp_set_lock(&lockSend); // Waiting for signal from the comp. thread + if(!Finished){ + // Blocking synchronous sends to all dependencies + for(i=0; i= asyncDuration){ + // Waiting for the end of previous sends before starting sync mode + omp_set_lock(&lockSendsDone); + curMode = SYNC; // Entering synchronous mode + stampData(dataToSend, SYNC); // Mark data to send with sync flag + nbSyncIter = 0; + } + }else{ + // In main async mode, going back to async mode when the max number of sync iterations are done + if(mainMode == ASYNC){ + nbSyncIter++; // Update of the number of sync iterations done + if(nbSyncIter == 2){ + curMode = ASYNC; // Going back to async mode + stampData(dataToSend, ASYNC); // Mark data to send + asyncStart = MPI_Wtime(); // Get the async starting time + } + } + } + + // Sendings of data dependencies + if(curMode == SYNC || !SendsInProgress){ + ... + } + + // Blocking data receptions in sync mode + if(curMode == SYNC){ + omp_set_lock(&lockRec); + } + + // Local computation + // (init of residual, arrays swapping and iteration computation) + ... + + // Checking of convergences (local & global) only in sync mode + if(curMode == SYNC){ + // Local convergence checking (residual under threshold) + ... + // Blocking global exchange of local states of the nodes + ... + // Determination of global convergence (all nodes in local CV) + // Stop of the iterative process and sending of end messages + // or Re-initialization of state information and iteration barrier + ... + } + } + + // Updating of the iteration number + iter++; +} +\end{Listing} +%\end{algorithm} + +In the sending function, the only modification is the replacement in line~11 of +the assignment of variable \texttt{SendsInProgress} by the unlocking of +\texttt{lockSendsDone}. Finally, in the reception function, the only +modification is the insertion before line~19 +of \Lst{algo:ch6p2BasicAsyncReceptions} of the extraction of the stamp from the +message and its counting among the receipts only if the stamp is \texttt{SYNC}. + +The final step to get our complete scheme using GPU is to insert the GPU +management in the computing thread. The first possibility, detailed +in \Lst{algo:ch6p2syncGPU}, is to simply replace the +CPU kernel (lines~41-43 in \Lst{algo:ch6p2AsyncSyncComp}) by a blocking GPU kernel call. This includes data +transfers from the node RAM to the GPU RAM, the launching of the GPU kernel, the +waiting for kernel completion and the results transfers from GPU RAM to +node RAM. + +%\begin{algorithm}[H] +% \caption{Computing function in the final asynchronous scheme.} +% \label{algo:ch6p2syncGPU} +\begin{Listing}{algo:ch6p2syncGPU}{Computing function in the final asynchronous scheme} +// Variables declarations and initialization +... +dim3 Dg, Db; // CUDA kernel grids + +// Computation loop +while(!Finished){ + // Determination of the dynamic operating mode, sendings of data dependencies and blocking data receptions in sync mode + ... + // Local GPU computation + // Data transfers from node RAM to GPU + CHECK_CUDA_SUCCESS(cudaMemcpyToSymbol(dataOnGPU, dataInRAM, inputsSize, 0, cudaMemcpyHostToDevice), "Data transfer"); + ... // There may be several data transfers: typically A and b in linear problems + // GPU grid definition + Db.x = BLOCK_SIZE_X; // BLOCK_SIZE_# are kernel design dependent + Db.y = BLOCK_SIZE_Y; + Db.z = BLOCK_SIZE_Z; + Dg.x = localSize/BLOCK_SIZE_X + (localSize%BLOCK_SIZE_X ? 1 : 0); + Dg.y = localSize/BLOCK_SIZE_Y + (localSize%BLOCK_SIZE_Y ? 1 : 0); + Dg.z = localSize/BLOCK_SIZE_Z + (localSize%BLOCK_SIZE_Z ? 1 : 0); + // Use of shared memory (when possible) + cudaFuncSetCacheConfig(gpuKernelName, cudaFuncCachePreferShared); + // Kernel call + gpuKernelName<<>>(... @\emph{kernel parameters}@ ...); + // Waiting for kernel completion + cudaDeviceSynchronize(); + // Results transfer from GPU to node RAM + CHECK_CUDA_SUCCESS(cudaMemcpyFromSymbol(resultsInRam, resultsOnGPU, resultsSize, 0, cudaMemcpyDeviceToHost), "Results transfer"); + // Potential post-treatment of results on the CPU + ... + + // Convergences checking + ... +} +\end{Listing} +%\end{algorithm} + +This scheme provides asynchronism through a cluster of GPUs as well as a +complete overlap of communications with GPU computations (similarly +to~\Sec{ch6:part1}). However, the autonomy of GPU devices according to their +host can be further exploited in order to perform some computations on the CPU +while the GPU kernel is running. The nature of computations that can be done by +the CPU may vary depending on the application. For example, when processing data +streams (pipelines), pre-processing of next data item and/or post-processing of +previous result can be done on the CPU while the GPU is processing the current +data item. In other cases, the CPU can perform \emph{auxiliary} +computations\index{computation!auxiliary} +that are not absolutely required to obtain the result but that may accelerate +the entire iterative process. Another possibility would be to distribute the +main computations between the GPU and CPU. However, this +usually leads to poor performance increases. This is mainly due to data +dependencies that often require additional transfers between CPU and GPU. + +So, if we consider that the application enables such overlap of +computations, its implementation is straightforward as it consists in inserting +the additional CPU computations between lines~23 and~24 +in \Lst{algo:ch6p2syncGPU}. Nevertheless, such scheme is fully efficient only +if the computation times on both sides are similar. + +In some cases, especially with auxiliary computations, another interesting +solution is to add a fourth CPU thread to perform them. This suppresses the +duration constraint over those optional computations as they are performed in +parallel of the main iterative process, without blocking it. Moreover, this +scheme stays coherent with current architectures as most nodes include four CPU +cores. The algorithmic scheme of such context of complete overlap of +CPU/GPU computations and communications is described in +Listings~\ref{algo:ch6p2FullOverAsyncMain},~\ref{algo:ch6p2FullOverAsyncComp1} +and~\ref{algo:ch6p2FullOverAsyncComp2}, where we suppose that auxiliary +computations use intermediate results of the main computation process from any previous iteration. This may be +different according to the application. + +%\begin{algorithm}[H] +% \caption{Initialization of the main process of complete overlap with asynchronism.} +% \label{algo:ch6p2FullOverAsyncMain} +\pagebreak +\begin{Listing}{algo:ch6p2FullOverAsyncMain}{Initialization of the main process of complete overlap with asynchronism} +// Variables declarations and initialization +... +omp_lock_t lockAux; // Informs main thread about new aux results +omp_lock_t lockRes; // Informs aux thread about new results +omp_lock_t lockWrite; // Controls exclusion of results access +... auxRes ... ; // Results of auxiliary computations + +// Parameters reading, MPI initialization, data initialization and distribution +... +// OpenMP initialization +... +omp_init_lock(&lockAux); +omp_set_lock(&lockAux); // Unlocked when new aux results are available +omp_init_lock(&lockRes); +omp_set_lock(&lockRes); // Unlocked when new results are available +omp_init_lock(&lockWrite); +omp_unset_lock(&lockWrite); // Controls access to results from threads + +#pragma omp parallel +{ + switch(omp_get_thread_num()){ + case COMPUTATION : + computations(... @\emph{relevant parameters}@ ...); + break; + + case AUX_COMPS : + auxComps(... @\emph{relevant parameters}@ ...); + break; + + case SENDINGS : + sendings(); + break; + + case RECEPTIONS : + receptions(); + break; + } +} + +// Cleaning of OpenMP locks +... +omp_test_lock(&lockAux); +omp_unset_lock(&lockAux); +omp_destroy_lock(&lockAux); +omp_test_lock(&lockRes); +omp_unset_lock(&lockRes); +omp_destroy_lock(&lockRes); +omp_test_lock(&lockWrite); +omp_unset_lock(&lockWrite); +omp_destroy_lock(&lockWrite); + +// MPI termination +MPI_Finalize(); +\end{Listing} +%\end{algorithm} + +%\begin{algorithm}[H] +% \caption{Computing function in the final asynchronous scheme with CPU/GPU overlap.} +% \label{algo:ch6p2FullOverAsyncComp1} +\pagebreak +\begin{Listing}{algo:ch6p2FullOverAsyncComp1}{Computing function in the final asynchronous scheme with CPU/GPU overlap} +// Variables declarations and initialization +... +dim3 Dg, Db; // CUDA kernel grids + +// Computation loop +while(!Finished){ + // Determination of the dynamic operating mode, sendings of data dependencies and blocking data receptions in sync mode + ... + // Local GPU computation + // Data transfers from node RAM to GPU, GPU grid definition and init of shared mem + CHECK_CUDA_SUCCESS(cudaMemcpyToSymbol(dataOnGPU, dataInRAM, inputsSize, 0, cudaMemcpyHostToDevice), "Data transfer"); + ... + // Kernel call + gpuKernelName<<>>(... @\emph{kernel parameters}@ ...); + // Potential pre/post-treatments in pipeline like computations + ... + // Waiting for kernel completion + cudaDeviceSynchronize(); + // Results transfer from GPU to node RAM + omp_set_lock(&lockWrite); // Wait for write access to resultsInRam + CHECK_CUDA_SUCCESS(cudaMemcpyFromSymbol(resultsInRam, resultsOnGPU, resultsSize, 0, cudaMemcpyDeviceToHost), "Results transfer"); + // Potential post-treatments in non-pipeline computations + ... + omp_unset_lock(&lockWrite); // Give back read access to aux thread + omp_test_lock(&lockRes); + omp_unset_lock(&lockRes); // Informs aux thread of new results + + // Auxiliary computations availability checking + if(omp_test_lock(&lockAux)){ + // Use auxRes to update the iterative process + ... // May induce additional GPU transfers + } + + // Convergences checking + if(curMode == SYNC){ + // Local convergence checking and global exchange of local states + ... + // Determination of global convergence (all nodes in local CV) + if(cvLocale == 1 && nbCVLocales == nbP-1){ + // Stop of the iterative process and sending of end messages + ... + // Unlocking of aux thread for termination + omp_test_lock(&lockRes); + omp_unset_lock(&lockRes); + }else{ + // Re-initialization of state information and iteration barrier + ... + } + } +} +\end{Listing} +%\end{algorithm} + +%\begin{algorithm}[H] +% \caption{Auxiliary computing function in the final asynchronous scheme with CPU/GPU overlap.} +% \label{algo:ch6p2FullOverAsyncComp2} +\pagebreak +\begin{Listing}{algo:ch6p2FullOverAsyncComp2}{Auxiliary computing function in the final asynchronous scheme with CPU/GPU overlap} +// Variables declarations and initialization +... auxInput ... // Local array for input data + +// Computation loop +while(!Finished){ + // Data copy from resultsInRam into auxInput + omp_set_lock(&lockRes); // Waiting for new results from main comps + if(!Finished){ + omp_set_lock(&lockWrite); // Waiting for access to results + for(ind=0; ind + +for (size_t i = 0; i < k; ++i) { + MBlock next; + parallel-do { + operation 1: ; + operation 2: { + ; + ; + } + } + operation 3: { + ; + A = next; + } +} + + +\end{Listing} +%\end{algorithm} + + +\Lst{algo:ch6p3ORWLlcopy} shows the local copy operation~3 as it could +be realized with ORWL. +It uses two resource handles +\texttt{nextRead} and \texttt{curWrite} and marks nested \emph{critical + sections} for these handles. Inside the nested sections it obtains pointers to +the resource data; the resource is \emph{mapped} into the address space of the +program, and then a standard call to \texttt{memcpy} achieves the operation +itself. The operation is integrated in its own \textbf{for}-loop, such that it +could run independently in an OS thread by its own. +%\begin{algorithm}[H] +% \caption{An iterative local copy operation.} +% \label{algo:ch6p3ORWLlcopy} +\begin{Listing}{algo:ch6p3ORWLlcopy}{An iterative local copy operation} +for (size_t i = 0; i < k; ++i) { + ORWL_SECTION(nextRead) { + MBlock const* sBlock = orwl_read_map(nextRead); + ORWL_SECTION(curWrite) { + MBlock * tBlock = orwl_write_map(curWrite); + memcpy(tBlock, sBlock, sizeof *tBlock); + } + } +} +\end{Listing} +%\end{algorithm} + +Next, in \Lst{algo:ch6p3ORWLrcopy} we copy data from a remote task to +a local task. Substantially the operation is the same, only that in the example +different handles (\texttt{remRead} and \texttt{nextWrite}) are used to +represent the respective resources. +%\begin{algorithm}[H] +% \caption{An iterative remote copy operation as part of a block cyclic matrix +% multiplication task.} +%\label{algo:ch6p3ORWLrcopy} +\begin{Listing}{algo:ch6p3ORWLrcopy}{An iterative remote copy operation as part of a block cyclic matrix multiplication task} +for (size_t i = 0; i < k; ++i) { + ORWL_SECTION(remRead) { + MBlock const* sBlock = orwl_read_map(remRead); + ORWL_SECTION(nextWrite) { + MBlock * tBlock = orwl_write_map(nextWrite); + memcpy(tBlock, sBlock, sizeof *tBlock); + } + } +} +\end{Listing} +%\end{algorithm} + +Now let us have a look into the operation that probably interests us the most, +the interaction with the GPU in \Lst{algo:ch6p3ORWLtrans}. Again there +is much structural resemblance to the copy operations from above, only that we +transfer the data to the GPU in the innermost block and then run the GPU MM +kernel while we still are inside the critical section for the GPU. +%\begin{algorithm}[H] +% \caption{An iterative GPU transfer and compute operation as part of a block cyclic matrix +% multiplication task.} +% \label{algo:ch6p3ORWLtrans} +\begin{Listing}{algo:ch6p3ORWLtrans}{An iterative GPU transfer and compute operation as part of a block cyclic matrix multiplication task} +for (size_t i = 0; i < k; ++i) { + ORWL_SECTION(GPUhandle) { + ORWL_SECTION(curRead) { + MBlock const* sBlock = orwl_read_map(curRead); + transferToGPU(sBlock, i); + } + runMMonGPU(i); + } +} +\end{Listing} +%\end{algorithm} + +Now that we have seen how the actual procedural access to the resources is +regulated we will show how the association between handles and resources is +specified. E.g in our application of block-cyclic MM the \texttt{curRead} handle +should correspond to current matrix block of the corresponding task, whereas +\texttt{remRead} should point to the current block of the neighboring task. +Both read operations on these matrix blocks can be effected without creating +conflicts, so we would like to express that fact in our resource specification. +From a point of view of the resource ``current block'' of a particular task, +this means that it can have two simultaneous readers, the task itself performing +the transfer to the GPU, and the neighboring task transferring the data to its +``next block''. + +\Lst{algo:ch6p3ORWLdecl} first shows the local dynamic declarations of +our application; it declares a \texttt{block} type for the matrix blocks, a +\texttt{result} data for the collective resource, and the six handles that we +have seen so far. +%\begin{algorithm}[H] +% \caption{Dynamic declaration of handles to represent the resources.} +% \label{algo:ch6p3ORWLdecl} +\begin{Listing}{algo:ch6p3ORWLdecl}{Dynamic declaration of handles to represent the resources} +/* A type for the matrix blocks */ +typedef double MBlock[N][N]; +/* Declaration to handle the collective resource */ +ORWL_GATHER_DECLARE(MBlock, result); + +/* Variables to handle data resources */ +orwl_handle2 remRead = ORWL_HANDLE2_INITIALIZER; +orwl_handle2 nextWrite = ORWL_HANDLE2_INITIALIZER; +orwl_handle2 nextRead = ORWL_HANDLE2_INITIALIZER; +orwl_handle2 curWrite = ORWL_HANDLE2_INITIALIZER; +orwl_handle2 curRead = ORWL_HANDLE2_INITIALIZER; + +/* Variable to handle the device resources */ +orwl_handle2 GPUhandle = ORWL_HANDLE2_INITIALIZER; +\end{Listing} +%\end{algorithm} + +With these declarations, we didn't yet tell ORWL much about the resources to +which these handles refer, nor the type (read or write) or the priority (FIFO +position) of the access. This is done in code +\Lst{algo:ch6p3ORWLinit}. The handles for +\Lst{algo:ch6p3ORWLtrans} are given first, \texttt{GPUhandle} will be +accessed exclusively (therefore the \texttt{write}) and, as said, +\texttt{curRead} is used shared (so a \texttt{read}). Both are inserted in the +FIFO of there respective resources with highest priority, specified by the +\texttt{0}s in the third function parameter. The resources to which they +correspond are specified through calls to the macro \texttt{ORWL\_LOCATION}, +indicating the task (\texttt{orwl\_mytid} is the ID of the current task) and the +specific resource of that task, here \texttt{GPU} and \texttt{curBlock}. + +Likewise, a second block of insertions concerns the handles for +\Lst{algo:ch6p3ORWLrcopy}: \texttt{newWrite} reclaims an exclusive +access and \texttt{remRead} a shared. \texttt{remRead} corresponds to a +resource of another task; \texttt{previous(orwl\_mytid)} is supposed to return +the ID of the previous task in the cycle. Both accesses can be effected +concurrently with the previous operation, so we insert with the same priority +\texttt{0} as before. + +Then, for the specification of the third operation +(\Lst{algo:ch6p3ORWLlcopy}) we need to use a different priority: the +copy operation from \texttt{nextBlock} to \texttt{curBlock} has to be performed +after the other operations have terminated. + +As a final step, we then tell ORWL that the specification of all accesses is +complete and that it may schedule\index{ORWL!schedule} all these accesses in the respective FIFOs of +the resources. +%\begin{algorithm}[H] +% \caption{Dynamic initialization of access mode and priorities.} +% \label{algo:ch6p3ORWLinit} +\begin{Listing}{algo:ch6p3ORWLinit}{Dynamic initialization of access mode and priorities} +/* One operation with priority 0 (highest) consists */ +/* in copying from current to the GPU and run MM, there. */ +orwl_write_insert(&GPUhandle, ORWL_LOCATION(orwl_mytid, GPU), 0); +orwl_read_insert(&curRead, ORWL_LOCATION(orwl_mytid, curBlock), 0); + +/* Another operation with priority 0 consists */ +/* in copying from remote to next */ +orwl_read_insert(&remRead, ORWL_LOCATION(previous(orwl_mytid), curBlock), 0); +orwl_write_insert(&nextWrite, ORWL_LOCATION(orwl_mytid, nextBlock), 0); + +/* One operation with priority 1 consists */ +/* in copying from next to current */ +orwl_read_insert(&nextRead, ORWL_LOCATION(orwl_mytid, nextBlock), 1); +orwl_write_insert(&curWrite, ORWL_LOCATION(orwl_mytid, curBlock), 1); + +orwl_schedule(); +\end{Listing} +%\end{algorithm} + +\subsection{Tasks and operations} +\label{sec:ch6p3tasks} +With that example we have now seen that ORWL distinguishes +\emph{tasks}\index{ORWL!task} and +\emph{operations}\index{ORWL!operation}. An ORWL program is divided into tasks that can be seen as the +algorithmic units that will concurrently access the resources that the program +uses. A task for ORWL is characterized by +\begin{itemize} +\item a fixed set of resources that it manages, ``\emph{owns}'', in our example + the four resources that are declared in \Lst{algo:ch6p3ORWLresources}. +\item a larger set of resources that it accesses, in our example all resources + that are used in \Lst{algo:ch6p3ORWLinit}. +\item a set of operations that act on these resources, in our example the three + operations that are used in \Lst{algo:ch6p3ORWLBCCMM}, and that are + elaborated in Listings~\ref{algo:ch6p3ORWLlcopy},~\ref{algo:ch6p3ORWLrcopy} + and~\ref{algo:ch6p3ORWLtrans}. +\end{itemize} + +\noindent +Each ORWL operation is characterized by +\begin{itemize} +\item one resource, usually one that is owned by the enclosing task, that it + accesses exclusively. In our example, operation~1 has exclusive access to the + \texttt{next} block, operation~2 has exclusive access the \texttt{GPU} + resource, and operation~3 to the \texttt{A} block. +\item several resources that are accessed concurrently with others. +\end{itemize} +In fact each ORWL~operation can be viewed as a compute-and-update procedure of a +particular resource with knowledge of another set of resources. + + +%%% Local Variables: +%%% mode: latex +%%% fill-column: 80 +%%% ispell-dictionary: "american" +%%% mode: flyspell +%%% TeX-master: "../../BookGPU.tex" +%%% End: diff --git a/BookGPU/Chapters/chapter6/PartieSync.tex b/BookGPU/Chapters/chapter6/PartieSync.tex new file mode 100755 index 0000000..ac9a3dd --- /dev/null +++ b/BookGPU/Chapters/chapter6/PartieSync.tex @@ -0,0 +1,713 @@ +\section{General scheme of synchronous code with computation/communication + overlapping in GPU clusters}\label{ch6:part1} + +%Notre chapitre précédent sur l'optimisation des schémas +%parallèles~\cite{ChVCV13}. + +\subsection{Synchronous parallel algorithms on GPU clusters} + +%\subsubsection{Considered parallel algorithms and implementations} + +\noindent {\bf Considered parallel algorithms and implementations} +\medskip + +This section focusses on synchronous parallel algorithms implemented with +overlapping computations and communications\index{overlap!computation and communication}. Parallel synchronous algorithms are +easier to implement, debug and maintain than asynchronous ones, see +Section~\ref{ch6:part2}. Usually, they follow a BSP-like parallel +scheme\index{BSP parallel scheme}, +alternating local computation steps and communication steps, +see~\cite{Valiant:BSP}. Their execution is usually deterministic, excepted +for stochastic algorithms that contain random number generations. Even in +this case, their execution can be controlled during debug steps, allowing to +track and to fix bugs quickly. + +However, depending on the properties of the algorithm, it is sometimes possible to +overlap computations and communications. If processes exchange data +that is not needed for the +computation that is following immediately, it is possible to implement such +an overlap. We have investigated the efficiency of this approach in previous +works~\cite{GUSTEDT:2007:HAL-00280094:1,ChVCV13}, using standard parallel +programming tools to achieve the implementation. + +The normalized and well known Message Passing Interface (MPI\index{MPI}) includes some asynchronous +point-to-point communication routines, that should allow to implement some +communication/computation overlap. However, current MPI implementations do not achieve +that goal efficiently; effective overlapping with MPI requires a group of +dedicated threads (in our case implemented with OpenMP\index{OpenMP}) for the basic +synchronous communications while another group of threads executes computations +in parallel. +% Finally, with explicit OpenMP +% threads executing MPI communications or various computations, we succeeded to +% decrease the execution time. +Nevertheless, communication and computation are not completely independent on +modern multicore architectures: they use shared hardware components such as the +interconnection bus and the RAM. Therefore that approach only saved up to $20\%$ +of the expected time on such a platform. This picture changes on clusters +equipped with GPU. They effectively allow independence of computations on the +GPU and communication on the mainboard (CPU, interconnection bus, RAM, network +card). We saved up to $100\%$ of the expected time on our GPU cluster, as we +will expose in the next section. + + +%\subsubsection{Specific interests for GPU clusters} + +\bigskip +\noindent {\bf Specific interests in GPU clusters} +\medskip + +In a computing node, a GPU is a kind of scientific coprocessor usually located +on an auxiliary board, with its own memory. So, when data have been transferred +from the CPU memory to the GPU memory, then GPU computations can be achieved on +the GPU board, totally in parallel of any CPU activities (like internode cluster +communications). The CPU and the GPU access their respective memories and do not +interfere, so they can achieve a very good overlap\index{overlap!computation +and computation} of their activities (better +than two CPU cores). + +But using a GPU on a computing node requires to transfer data from the CPU to +the GPU memory, and to transfer the computation results back from the GPU to the +CPU. Transfer times are not excessive, but depending on the application +they still can be significant compared to the GPU computation times. So, sometimes it +can be interesting to overlap the internode cluster communications with both the +CPU/GPU data transfers and the GPU computations. We can identify four main +parallel programming schemes on a GPU cluster: + +\begin{enumerate} +\item parallelizing only 'internode CPU communications' with 'GPU computations', + and achieving CPU/GPU data transfers before and after this parallel step, +\item parallelizing 'internode CPU communications' with a '(sequential) sequence + of CPU/GPU data transfers and GPU computations', +\item parallelizing 'internode CPU communications' with a 'streamed sequence of + CPU/GPU data transfers and GPU computations', +\item parallelizing 'internode CPU communications' with 'CPU/GPU data transfers' + and with 'GPU computations', interleaving computation-communication + iterations. +\end{enumerate} + + +\subsection{Native overlap of CPU communications and GPU computations} + +\begin{figure}[t] + \centering + \includegraphics{Chapters/chapter6/figures/Sync-NativeOverlap.pdf} + \caption{Native overlap of internode CPU communications with GPU computations.} + \label{fig:ch6p1overlapnative} +\end{figure} + +Using CUDA\index{CUDA}, GPU kernel executions are non-blocking, and GPU/CPU data +transfers\index{CUDA!data transfer} +are blocking or non-blocking operations. All GPU kernel executions and CPU/GPU +data transfers are associated to "streams"\index{CUDA!stream}, and all operations on a same stream +are serialized. When transferring data from the CPU to the GPU, then running GPU +computations and finally transferring results from the GPU to the CPU, there is +a natural synchronization and serialization if these operations are achieved on +the same stream. GPU developers can choose to use one (default) or several +streams. In this first scheme of overlapping, we consider parallel codes using +only one GPU stream. + +"Non-blocking GPU kernel execution" means a CPU routine runs a parallel +execution of a GPU computing kernel, and the CPU routine continues its execution +(on the CPU) while the GPU kernel is running (on the GPU). Then the CPU routine +can initiate some communications with some others CPU, and so it automatically +overlaps the internode CPU communications with the GPU computations (see +\Fig{fig:ch6p1overlapnative}). This overlapping is natural when programming with +CUDA and MPI: it is easy to deploy, but does not overlap the CPU/GPU data +transfers. + +%\begin{algorithm}[t] +% \caption{Generic scheme implicitly overlapping MPI communications with CUDA GPU +% computations}\label{algo:ch6p1overlapnative} +\pagebreak +\begin{Listing}{algo:ch6p1overlapnative}{Generic scheme implicitly overlapping MPI communications with CUDA GPU computations} +// Input data and result variables and arrays (example with +// float datatype, 1D input arrays, and scalar results) +float *cpuInputTabAdr, *gpuInputTabAdr; +float *cpuResTabAdr, *gpuResAdr; + +// CPU and GPU array allocations +cpuInputTabAdr = malloc(sizeof(float)*N); +cudaMalloc(&gpuInputTabAdr,sizeof(float)*N); +cpuResTabAdr = malloc(sizeof(float)*NbIter); +cudaMalloc(&gpuResAdr,sizeof(float)); + +// Definition of the Grid of blocks of GPU threads +dim3 Dg, Db; +Dg.x = ... +... + +// Indexes of source and destination MPI processes +int dest = ... +int src = ... + +// Computation loop (using the GPU) +for (int i = 0; i < NbIter; i++) { + cudaMemcpy(gpuInputTabAdr, cpuInputTabAdr, // Data transfer: + sizeof(float)*N, // CPU --> GPU (sync. op) + cudaMemcpyHostToDevice); + gpuKernel_k1<<>>(); // GPU comp. (async. op) + MPI_Sendrecv_replace(cpuInputTabAdr, // MPI comms. (sync. op) + N,MPI_FLOAT, + dest, 0, src, 0, ...); + // IF there is (now) a result to transfer from the GPU to the CPU: + cudaMemcpy(cpuResTabAdr + i, gpuResAdr, // Data transfer: + sizeof(float), // GPU --> CPU (sync. op) + cudaMemcpyDeviceToHost); +} +... +\end{Listing} +%\end{algorithm} + +\Lst{algo:ch6p1overlapnative} introduces the generic code of a MPI+CUDA +implementation, natively and implicitly overlapping MPI communications with CUDA +GPU computations. Some input data and output results arrays and variables are +declared and allocated from line 1 up to 10, and a computation loop is +implemented from line 21 up to 34. At each iteration: +\begin{itemize} +\item \texttt{cudaMemcpy} on line 23 transfers data from the CPU memory + to the GPU memory. This is a basic and synchronous data transfer. +\item \texttt{gpuKernel\_k1<<>>} on line 26 starts GPU computation + (running a GPU kernel on the grid of blocks of threads defined at line 12 to + 15). This is a standard GPU kernel run, it is an asynchronous operation. The + CPU can continue to run its code. +\item \texttt{MPI\_Sendrecv\_replace} on line 27 achieves some blocking + internode communications, overlapping GPU computations started just before. +\item If needed, \texttt{cudaMemcpy} on line 31 transfers the iteration result from + one variable in the GPU memory at one array index in the CPU memory (in this example the CPU + collects all iteration results in an array). This operation is started after + the end of the MPI communication (previous instruction) and after the end of + the GPU kernel execution. CUDA insures an implicit synchronization of all + operations involving the same GPU stream, like the default stream in this + example. Result transfer has to wait the GPU kernel execution is finished. + If there is no result transfer implemented, the next operation on the GPU + will wait until the GPU kernel execution will be ended. +\end{itemize} + +This implementation is the easiest one involving the GPU. It achieves an +implicit overlap of internode communications and GPU computations, no explicit +multithreading is required on the CPU. However, CPU/GPU data transfers are +achieved serially and not overlapped. + + +\subsection{Overlapping with sequences of transfers and computations} + +%\subsubsection{Overlapping with a sequential GPU sequence} + +\noindent {\bf Overlapping with a sequential GPU sequence} +\medskip + +\begin{figure}[t] + \centering + \includegraphics[width=\columnwidth]{Chapters/chapter6/figures/Sync-SeqSequenceOverlap.pdf} + \caption{Overlap of internode CPU communications with a sequence of CPU/GPU data transfers and GPU + computations.} + \label{fig:ch6p1overlapseqsequence} +\end{figure} + +When CPU/GPU data transfers are not negligible compared to GPU computations, it +can be interesting to overlap internode CPU computations with a \emph{GPU + sequence}\index{GPU sequence} including CPU/GPU data transfers and GPU computations (see +\Fig{fig:ch6p1overlapseqsequence}). Algorithmic issues of this approach are basic, +but their implementation require explicit CPU multithreading and +synchronization, and CPU data buffer duplication. We need to implement two +threads, one starting and achieving MPI communications, and the other running +the \emph{GPU sequence}. OpenMP allows an easy and portable implementation of +this overlapping strategy. However, it remains more complex to develop and to +maintain than the previous strategy (overlapping only internode CPU +communications and GPU computations), and should be adopted only when CPU/GPU +data transfer times are not negligible. + +%\begin{algorithm} +% \caption{Generic scheme explicitly overlapping MPI communications with +% sequences of CUDA CPU/GPU transfers and CUDA GPU +% computations}\label{algo:ch6p1overlapseqsequence} +\begin{Listing}{algo:ch6p1overlapseqsequence}{Generic scheme explicitly overlapping MPI communications with sequences of CUDA CPU/GPU transfers and CUDA GPU computations} +// Input data and result variables and arrays (example with +// float datatype, 1D input arrays, and scalar results) +float *cpuInputTabAdrCurrent, *cpuInputTabAdrFuture, *gpuInputTabAdr; +float *cpuResTabAdr, *gpuResAdr; + +// CPU and GPU array allocations +cpuInputTabAdrCurrent = malloc(sizeof(float)*N); +cpuInputTabAdrFuture = malloc(sizeof(float)*N); +cudaMalloc(&gpuInputTabAdr,sizeof(float)*N); +cpuResTabAdr = malloc(sizeof(float)*NbIter); +cudaMalloc(&gpuResAdr,sizeof(float)); + +// Definition of the Grid of blocks of GPU threads +dim3 Dg, Db; +Dg.x = ... +... + +// Indexes of source and destination MPI processes +int dest = ... +int src = ... + +// Set the number of OpenMP threads (to create) to 2 +omp_set_num_threads(2); +// Create threads and start the parallel OpenMP region +#pragma omp parallel +{ + // Buffer pointers (thread local variables) + float *current = cpuInputTabAdrCurrent; + float *future = cpuInputTabAdrFuture; + float *tmp; + + // Computation loop (using the GPU) + for (int i = 0; i < NbIter; i++) { + + // - Thread 0: achieves MPI communications + if (omp_get_thread_num() == 0) { + MPI_Sendrecv(current, // MPI comms. (sync. op) + N, MPI_FLOAT, dest, 0, + future, + N, MPI_FLOAT, dest, 0, ...); + + // - Thread 1: achieves the GPU sequence (GPU computations and + // CPU/GPU data transfers) + } else if (omp_get_thread_num() == 1) { + cudaMemcpy(gpuInputTabAdr, current, // Data transfer: + sizeof(float)*N, // CPU --> GPU (sync. op) + cudaMemcpyHostToDevice); + gpuKernel_k1<<>>(); // GPU comp. (async. op) + // IF there is (now) a result to transfer from the GPU to the CPU: + cudaMemcpy(cpuResTabAdr + i, gpuResAdr, // Data transfer: + sizeof(float), // GPU --> CPU (sync. op) + cudaMemcpyDeviceToHost); + } + + // - Wait for both threads have achieved their iteration tasks + #pragma omp barrier + // - Each thread permute its local buffer pointers + tmp = current; + current = future; + future = tmp; + } // End of computation loop +} // End of OpenMP parallel region +... +\end{Listing} +%\end{algorithm} + +\Lst{algo:ch6p1overlapseqsequence} introduces the generic code of a +MPI+OpenMP+CUDA implementation, explicitly overlapping MPI communications with +\emph{GPU sequences}. Lines 25--62 implement the OpenMP parallel region, +around the computation loop (lines 33--61). For performances it is +important to create and destroy threads only one time (not at each iteration): +the parallel region has to surround the computation loop. Lines 1--11 +consist in declaration and allocation of input data arrays and result arrays and +variables, like in previous algorithm (\Lst{algo:ch6p1overlapnative}). However, we implement two input data buffers on the +CPU (current and future version). As we aim to overlap internode MPI +communications and GPU sequence, including CPU to GPU data transfer of current +input data array, we need to store the received new input data array in a +separate buffer. Then, the current input data array will be safely read on the +CPU and copied into the GPU memory. + +The thread creations\index{OpenMP!thread creation} are easily achieved with one OpenMP directive (line +25). Then each thread defines and initializes \emph{its} local buffer pointers, +and enters \emph{its} computing loop (lines 27--33). Inside the computing +loop, a test on the thread number allows to run a different code in each +thread. Lines 37--40 implement the MPI synchronous communication run by +thread number $0$. Lines 45--52 implement the GPU sequence run by thread +$1$: CPU to GPU data transfer, GPU computation and GPU to CPU result +transfer (if needed). Details of the three operations of this sequence have not changed +compared to the previous overlapping strategy. + +At the end of \Lst{algo:ch6p1overlapseqsequence}, an OpenMP synchronization +barrier\index{OpenMP!barrier} on line 56 allows to wait OpenMP threads have achieved MPI +communications and GPU sequence, and do not need to access the current input +data buffer. Then, each thread permute its local buffer pointers (lines 58--60), +and is ready to enter the next iteration, processing the new current input +array. + + +%\subsubsection{Overlapping with a streamed GPU sequence} + +\bigskip +\noindent {\bf Overlapping with a streamed GPU sequence} +\medskip + +\begin{figure}[t] + \centering + \includegraphics[width=\columnwidth]{Chapters/chapter6/figures/Sync-StreamSequenceOverlap.pdf} + \caption{Overlap of internode CPU communications with a streamed sequence of CPU/GPU data + transfers and GPU computations.} + \label{fig:ch6p1overlapstreamsequence} +\end{figure} + +Depending on the algorithm implemented, it is sometimes possible to split the +GPU computation into several parts processing distinct data. Then, we can +speedup the \emph{GPU sequence} using several CUDA \emph{streams}\index{CUDA!stream}. The goal is +to overlap CPU/GPU data transfers with GPU computations\index{overlap!GPU data transfers with GPU computation} inside the \emph{GPU + sequence}. Compared to the previous overlapping strategy, we have to split the +initial data transfer in a set of $n$ asynchronous and smaller data transfers, +and to split the initial GPU kernel call in a set of $n$ calls to the same GPU +kernel. Usually, these smaller calls are deployed with less GPU threads +(i.e. associated to a smaller grid of blocks of threads). Then, the first GPU +computations can start as soon as the first data transfer has been achieved, and +next transfers can be done in parallel of next GPU computations (see +\Fig{fig:ch6p1overlapstreamsequence}). + +NVIDIA advises to start all asynchronous CUDA data transfers, and then to call +all CUDA kernel executions, using up to $16$ streams \cite{cudabestpractices}. +Then, CUDA driver and +runtime optimize the global execution of these operations. So, we cumulate two +overlapping mechanisms. The former is controlled by CPU multithreading, and +overlap MPI communications and the \emph{streamed GPU sequence}. The latter is +controlled by CUDA programming, and overlap CPU/GPU data transfers and GPU +computations. Again, OpenMP allows to easily implement the CPU multithreading, +and to wait for the end of both CPU threads before to execute the next instructions +of the code. + +%\begin{algorithm} +% \caption{Generic scheme explicitly overlapping MPI communications with streamed sequences of CUDA +% CPU/GPU transfers and CUDA GPU computations}\label{algo:ch6p1overlapstreamsequence} +\begin{Listing}{algo:ch6p1overlapstreamsequence}{Generic scheme explicitly overlapping MPI communications with streamed sequences of CUDA CPU/GPU transfers and CUDA GPU computations} +// Input data and result variables and arrays (example with +// float datatype, 1D input arrays, and scalar results) +float *cpuInputTabAdrCurrent, *cpuInputTabAdrFuture, *gpuInputTabAdr; +float *cpuResTabAdr, *gpuResAdr; +// CPU and GPU array allocations (allocates page-locked CPU memory) +cudaHostAlloc(&cpuInputTabAdrCurrent,sizeof(float)*N,cudaHostAllocDefault); +cudaHostAlloc(&cpuInputTabAdrFuture,sizeof(float)*N,cudaHostAllocDefault); +cudaMalloc(&gpuInputTabAdr,sizeof(float)*N); +cpuResTabAdr = malloc(sizeof(float)*NbIter); +cudaMalloc(&gpuResAdr,sizeof(float)); +// Stream declaration and creation +cudaStream_t TabS[NbS]; +for(int s = 0; s < NbS; s++) + cudaStreamCreate(&TabS[s]); +// Definition of the Grid of blocks of GPU threads +... +// Set the number of OpenMP threads (to create) to 2 +omp_set_num_threads(2); +// Create threads and start the parallel OpenMP region +#pragma omp parallel +{ + // Buffer pointers (thread local variables) + float *current = cpuInputTabAdrCurrent; + float *future = cpuInputTabAdrFuture; + float *tmp; + // Stride of data processed per stream + int stride = N/NbS; + // Computation loop (using the GPU) + for (int i = 0; i < NbIter; i++) { + // - Thread 0: achieves MPI communications + if (omp_get_thread_num() == 0) { + MPI_Sendrecv(current, // MPI comms. (sync. op) + N, MPI_FLOAT, dest, 0, + future, + N, MPI_FLOAT, dest, 0, ...); + // - Thread 1: achieves the streamed GPU sequence (GPU computations + // and CPU/GPU data transfers) + } else if (omp_get_thread_num() == 1) { + for (int s = 0; s < NbS; s++) { // Start all data transfers: + cudaMemcpyAsync(gpuInputTabAdr + s*stride, // CPU --> GPU + current + s*stride, // (async. ops) + sizeof(float)*stride, + cudaMemcpyHostToDevice, + TabS[s]); + } + for (int s = 0; s < NbS; s++) { // Start all GPU comps. (async.) + gpuKernel_k1<<>>(gpuInputTabAdr + s*stride); + } + cudaThreadSynchronize(); // Wait all threads are ended + // IF there is (now) a result to transfer from the GPU to the CPU: + cudaMemcpy(cpuResTabAdr, // Data transfers: + gpuResAdr, // GPU --> CPU (sync. op) + sizeof(float), + cudaMemcpyDeviceToHost); + } + // - Wait for both threads have achieved their iteration tasks + #pragma omp barrier + // - Each thread permute its local buffer pointers + tmp = current; current = future; future = tmp; + } // End of computation loop +} // End of OpenMP parallel region +... +// Destroy the streams +for(int s = 0; s < NbS; s++) + cudaStreamDestroy(TabS[s]); +... +\end{Listing} +%\end{algorithm} + +\Lst{algo:ch6p1overlapstreamsequence} introduces the generic MPI+OpenMP+CUDA +code explicitly overlapping MPI communications with +\emph{streamed GPU sequences}\index{GPU sequence!streamed}. Efficient usage of CUDA \emph{streams} requires to execute +asynchronous CPU/GPU data transfers, that needs to read page-locked +data\index{page-locked data} in CPU memory. So, CPU +memory allocations on lines 6 and 7 are implemented with \texttt{cudaHostAlloc} instead of +the basic \texttt{malloc} function. Then, $NbS$ \emph{streams} are created on lines 12--14. +Usually we create $16$ streams: the maximum number supported by CUDA. + +An OpenMP parallel region\index{OpenMP!parallel region} including two threads is implemented on lines 17--61 of +\Lst{algo:ch6p1overlapstreamsequence}, similarly to the previous algorithm (see +\Lst{algo:ch6p1overlapseqsequence}). Code of thread $0$ achieving MPI communication is unchanged, but +code of thread $1$ is now using streams. Following NVIDIA recommandations, we have first implemented +a loop starting $NbS$ asynchronous data transfers (lines 39--45): transferring $N/NbS$ data on +each stream. Then we have implemented a second loop (lines 46--48), starting asynchronous +executions of $NbS$ grids of blocks of GPU threads (one per stream). Data transfers and kernel +executions on the same stream are synchronized by CUDA and the GPU. So, each kernel execution will +start after its data will be transferred into the GPU memory, and the GPU scheduler ensures to start +some kernel executions as soon as the first data transfers are achieved. Then, next data transfers +will be overlapped with GPU computations. After the kernel calls, on the different streams, +we wait for the end of all GPU threads previously run, calling an explicit synchronization +function on line 49. This synchronization is not mandatory, but it will make the implementation more +robust and will facilitate the debugging steps: all GPU computations run by the OpenMP thread number +$1$ will be achieved before this thread will enter a new loop iteration, or before the computation +loop will be ended. + +If a partial result has to be transferred from GPU to CPU memory at the end of each loop iteration +(for example the result of one \emph{reduction} per iteration), this transfer is achieved +synchronously on the default stream (no particular stream is specified) on lines 51--54. +Availability of the result values is ensured by the synchronization implemented on line 49. +However, if a partial result has to be transferred on the CPU on each stream, then $NbS$ asynchronous data +transfers could be started in parallel (one per stream), and should be implemented before the +synchronization operation on line 49. The end of the computation loop includes a synchronization +barrier of the two OpenMP threads, waiting they have finished to access the different data +buffers in the current iteration. Then, each OpenMP thread exchanges its local buffer pointers, like +in the previous algorithm. However, after the computation loop, we have added the +destruction of the CUDA streams (lines 63--65). + +Finally, CUDA streams\index{CUDA!stream} have been used to extend \Lst{algo:ch6p1overlapseqsequence} +with respect to its global scheme. \Lst{algo:ch6p1overlapstreamsequence} still creates an +OpenMP parallel region, with two CPU threads, one in charge of MPI communications, and the other +managing data transfers and GPU computations. Unfortunately, using GPU streams require to be able to +split a GPU computation in independent subparts, working on independent subsets of data. +\Lst{algo:ch6p1overlapstreamsequence} is not so generic than \Lst{algo:ch6p1overlapseqsequence}. + + +\subsection{Interleaved communications-transfers-computations overlapping} + +\begin{figure}[t] + \centering + \includegraphics{Chapters/chapter6/figures/Sync-CompleteInterleaveOverlap.pdf} + \caption{Complete overlap of internode CPU communications, CPU/GPU data transfers and GPU + computations, interleaving computation-communication iterations} + \label{fig:ch6p1overlapinterleaved} +\end{figure} + +Many algorithms do not support to split data transfers and kernel calls, and can +not exploit CUDA streams. For example, when each GPU thread requires to access +some data spread in the global set of transferred data. Then, it is possible to +overlap internode CPU communications and CPU/GPU data transfers and GPU +computations, if the algorithm achieves \emph{computation-communication + iterations} and if we can interleave these iterations. At iteration $k$: CPUs +exchange data $D_k$, each CPU/GPU couple transfers data $D_k$, and each GPU +achieves computations on data $D_{k-1}$ (see +\Fig{fig:ch6p1overlapinterleaved}). Compared to the previous strategies, this +strategy requires twice as many CPU data buffers +and twice as many GPU buffers. + +%\begin{algorithm} +% \caption{Generic scheme explicitly overlapping MPI communications, CUDA CPU/GPU transfers and CUDA +% GPU computations, interleaving computation-communication iterations}\label{algo:ch6p1overlapinterleaved} +\begin{Listing}{algo:ch6p1overlapinterleaved}{Generic scheme explicitly overlapping MPI communications, CUDA CPU/GPU transfers and CUDA GPU computations, interleaving computation-communication iterations} +// Input data and result variables and arrays (example with +// float datatype, 1D input arrays, and scalar results) +float *cpuInputTabAdrCurrent, *cpuInputTabAdrFuture; +float *gpuInputTabAdrCurrent, *gpuInputTabAdrFuture; +float *cpuResTabAdr, *gpuResAdr; + +// CPU and GPU array allocations +cpuInputTabAdrCurrent = malloc(sizeof(float)*N); +cpuInputTabAdrFuture = malloc(sizeof(float)*N); +cudaMalloc(&gpuInputTabAdrCurrent,sizeof(float)*N); +cudaMalloc(&gpuInputTabAdrFuture,sizeof(float)*N); +cpuResTabAdr = malloc(sizeof(float)*NbIter); +cudaMalloc(&gpuResAdr,sizeof(float)); + +// Definition of the Grid of blocks of GPU threads +dim3 Dg, Db; Dg.x = ... +// Indexes of source and destination MPI processes +int dest, src; dest = ... + +// Set the number of OpenMP threads (to create) to 2 +omp_set_num_threads(3); +// Create threads and start the parallel OpenMP region +#pragma omp parallel +{ + // Buffer pointers (thread local variables) + float *cpuCurrent = cpuInputTabAdrCurrent; + float *cpuFuture = cpuInputTabAdrFuture; + float *gpuCurrent = gpuInputTabAdrCurrent; + float *gpuFuture = gpuInputTabAdrFuture; + float *tmp; + + // Computation loop on: NbIter + 1 iteration + for (int i = 0; i < NbIter + 1; i++) { + // - Thread 0: achieves MPI communications + if (omp_get_thread_num() == 0) { + if (i < NbIter) { + MPI_Sendrecv(cpuCurrent, // MPI comms. (sync. op) + N, MPI_FLOAT, dest, 0, + cpuFuture, + N, MPI_FLOAT, dest, 0, ...); + } + // - Thread 1: achieves the CPU/GPU data transfers + } else if (omp_get_thread_num() == 1) { + if (i < NbIter) { + cudaMemcpy(gpuFuture, cpuCurrent, // Data transfer: + sizeof(float)*N, // CPU --> GPU (sync. op) + cudaMemcpyHostToDevice); + } + // - Thread 2: achieves the GPU computations and the result transfer + } else if (omp_get_thread_num() == 2) { + if (i > 0) { + gpuKernel_k1<<>>(gpuCurrent); // GPU comp. (async. op) + // IF there is (now) a result to transfer from GPU to CPU: + cudaMemcpy(cpuResTabAdr + (i-1), // Data transfer: + gpuResAdr, sizeof(float), // GPU --> CPU (sync. op) + cudaMemcpyDeviceToHost); + } + } + // - Wait for both threads have achieved their iteration tasks + #pragma omp barrier + // - Each thread permute its local buffer pointers + tmp = cpuCurrent; cpuCurrent = cpuFuture; cpuFuture = tmp; + tmp = gpuCurrent; gpuCurrent = gpuFuture; gpuFuture = tmp; + } // End of computation loop +} // End of OpenMP parallel region +... +\end{Listing} +%\end{algorithm} + +\Lst{algo:ch6p1overlapinterleaved} introduces the generic code of a +MPI+OpenMP+CUDA implementation, explicitly interleaving +computation-communication iterations and overlapping MPI communications, CUDA CPU/GPU +transfers and CUDA GPU computations. As in the previous algorithms, we declare two CPU input data arrays +(current and future version) on line 3, but we also declare two GPU input data arrays on line 4. On +lines 8--11, these four data arrays are allocated, using \texttt{malloc} and +\texttt{cudaMalloc}. +We do not need to allocate page-locked memory space. On lines 23--65 we +create an OpenMP parallel region, configured to run three threads (see line 21). Lines 26--30 are +declarations of thread local pointers on data arrays and variables (each thread will use its own +pointers). On line 33, the three threads enter a computation loop of \texttt{NbIter + 1} +iterations. We need to run one more iteration than with previous algorithms. + +Lines 34--41 are the MPI communications, achieved by the thread number $0$. They send the +current CPU input data array to another CPU, and receive the future CPU input data array from +another CPU, like in previous algorithms. But this thread achieves communications only during the +\emph{first} \texttt{NbIter} iterations. Lines 43--48 are the CPU to GPU input data +transfers, achieved by thread number $1$. These data transfers are run in parallel of MPI +communications. They are run during the \emph{first} \texttt{NbIter} iterations, and transfer +current CPU input data array into the future GPU data array. Lines 50--57 +correspond to the code run by +thread number $3$. They start GPU computations, to process the current GPU input data array, and if +necessary +transfer a GPU result at an index of the CPU result array. These GPU computations and result +transfers are run during the \emph{last} \texttt{NbIter} iterations: the GPU computations +have to wait the first data transfer is ended before to start to process any data, and can not run +during the first iteration. So, the activity of the third thread is shifted of one iteration +compared to the activities of other threads. Moreover, the address of the current GPU input data +array has to be passed as a parameter of the kernel call on line 52, in order the GPU threads access +the right data array. Like in previous algorithms the GPU result is copied at one index of the CPU +result array, in lines 53--56, but due to the shift of the third thread activity this index is +now \texttt{(i - 1)}. + +Line 60 is a synchronization barrier\index{OpenMP!barrier} of the three OpenMP threads, followed by a pointer permutation +of local pointers on current and future data arrays, on line 62 and 63. Each +thread waits for the completion of other +threads to use the data arrays, and then permutes its data array pointers before to +enter a new loop iteration. + +This complete overlap of MPI communications and CPU/GPU data transfers and GPU computations, is not +too complex to implement, and can be a solution when GPU computations are not adapted to use CUDA +streams: when GPU computations can not be split in subparts working on independent subsets of input +data. However, it requires to run one more iterations (a total of \texttt{NbIter + 1} +iterations). Then, if the number of iterations is very small, it could be more interesting not to +attempt to overlap CPU/GPU data transfers and GPU computations, and to implement \Lst{algo:ch6p1overlapseqsequence}. + + +\subsection{Experimental validation} +\label{ch6:p1expes} +%\subsubsection{Experimentation testbed} + +\noindent {\bf Experimentation testbed} +\medskip + +Two clusters located at SUPELEC in Metz (France) have been used for the entire set of +experiments presented in this chapter: +% +\begin{itemize} + +\item The first consists of 17 nodes with an Intel Nehalem quad-core processor + at 2.67Ghz, 6 Gb RAM and an NVIDIA GeForce GTX480 GPU, each. + +\item The second consists of 16 nodes with an Intel core2 dual-core processor at + 2.67Ghz, 4 Gb RAM and an NVIDIA GeForce GTX580 GPU, each + +\end{itemize} +% +Both clusters have a Gigabit Ethernet interconnection network that is connected +through a DELL Power Object 5324 switch. The two switches are linked twice, +insuring the interconnection of the two clusters. The software environment +consists of a Linux Fedora 64bit OS (kernel v. 2.6.35), GNU C and C++ compilers +(v. 4.5.1) and the CUDA library (v. 4.2). + + +%\subsubsection{Validation of the synchronous approach} + +\bigskip +\noindent {\bf Validation of the synchronous approach} +\medskip + +\begin{figure}[t] + \centering + \includegraphics{Chapters/chapter6/curves/gpuSyncOverlap.pdf} + \caption{Experimental performances of different synchronous algorithms computing a + dense matrix product} + \label{fig:ch6p1syncexpematrixprod} +\end{figure} + +\label{ch6:p1block-cyclic} +We have experimented our approach of synchronous parallel algorithms with a classic +block cyclic algorithm for dense matrix multiplication\index{matrix + multiplication!block cyclic}. This problem requires to split two input matrices ($A$ and $B$) on a ring of +computing nodes, and to establish a circulation of the slices of $A$ matrix on the ring ($B$ matrix +partition does not evolve during all the run). Compared to our generic algorithms, there is no +partial result to transfer from GPU to CPU at the end of each computing iteration. The part of the +result matrix computed on each GPU is transferred on the CPU at the end of the computation loop. + +We have first implemented a synchronous version without any overlap of MPI communications, CPU/GPU +data transfers, and GPU computations. We have added some synchronizations in the native overlapping +version in order to avoid any overlap. We have measured the performance achieved on our cluster with +NVIDIA GTX480 GPUs and matrices sizes of 4096$\times$4096, and we have obtained the curves in \Fig{fig:ch6p1syncexpematrixprod} labeled +\emph{no-ovlp}. We observe that performance increases when the number of processor increases. Of course, +there is a significant increase in cost when comparing a single node (without any MPI communication) with +two nodes (starting to use MPI communications). But beyond two nodes we get a classical performance +curve. + +Then, we implemented and experimented \Lst{algo:ch6p1overlapnative}, see +\emph{ovlp-native} in \Fig{fig:ch6p1syncexpematrixprod}. The native +overlap of MPI communications with asynchronous run of CUDA kernels appears efficient. When the +number of nodes increases the ratio of the MPI communications increases a lot (because the +computation times decrease a lot). So, there is not a lot of GPU computation +time that remains to be +overlapped, and both \emph{no-ovlp} and \emph{ovlp-native} tend to the same +limit. Already, the native overlap performed in \Lst{algo:ch6p1overlapnative} +achieves a high level of performance very quickly, using only four +nodes. Beyond four nodes, a faster interconnection network would be required for +a performance increase. + +Finally, we implemented \Lst{algo:ch6p1overlapseqsequence}, overlapping MPI communications +with a GPU sequence including both CPU/GPU data transfers and GPU computations, +see \emph{ovlp-GPUsequence} in \Fig{fig:ch6p1syncexpematrixprod}. From four +up to sixteen nodes it achieves better performances than \emph{ovlp-native}: we better overlap +MPI communications. However, this parallelization mechanism has more overhead: OpenMP threads +have to be created and synchronized. Only for two nodes it is less efficient than the native +overlapping algorithm. Beyond two nodes, the CPU multithreading overhead seems compensated. +% No, it doesn't need the more implementation of time, but more implementation +% of code :) +\Lst{algo:ch6p1overlapseqsequence} requires more time for the implemention +and can be more complex to maintain, but such extra development cost is +justified if we are looking for better performance. + + +%%% Local Variables: +%%% mode: latex +%%% fill-column: 80 +%%% ispell-dictionary: "american" +%%% mode: flyspell +%%% TeX-master: "../../BookGPU.tex" +%%% End: diff --git a/BookGPU/Chapters/chapter6/biblio6.bib b/BookGPU/Chapters/chapter6/biblio6.bib new file mode 100644 index 0000000..65dfda7 --- /dev/null +++ b/BookGPU/Chapters/chapter6/biblio6.bib @@ -0,0 +1,310 @@ +@manual{cudabestpractices, + TITLE = {{NVIDIA {CUDA C} Best Practices Guide 4.0}}, + organization = {NVIDIA}, + month = {May}, + year = {2011}, + howpublished = "\url{http://docs.nvidia.com/cuda/pdf/CUDA_C_Best_Practices_Guide.pdf}", +} + +@ARTICLE{FS2000, + author = {A. Frommer and D. B. Szyld}, + year = 2000, + title = {On Asynchronous Iterations}, + journal = {J. Comput. and Appl. Math.}, + volume = 123, + pages = {201-216} +} + + +@Book{BT89, + author = {D.P.~Bertsekas and J.N.~Tsitsiklis}, + ALTeditor = {}, + title = {Parallel and Distributed Computation}, + publisher = {Prentice Hall}, + year = {1999}, + address = {Englewood Cliffs, New Jersey}, +} + +@InBook{ChapNRJ2011, + author = {Vialle, S. and Contassot-Vivier, S. and Jost, T.}, + editor = {Sanjay Ranka and Ishfag Ahmad}, + title = {Handbook of Energy-Aware and Green Computing}, + chapter = {Optimizing Computing and Energy Performances in Heterogeneous Clusters of CPUs and GPUs}, + publisher = {Chapman and Hall/CRC}, + year = {2012}, + url = {http://www.crcpress.com/product/isbn/9781466501164#}, + isbn = {9781466501164}, + OPTkey = {}, + OPTvolume = {}, + OPTnumber = {}, + series = {Computer \& Information Science Series}, + OPTtype = {}, + OPTaddress = {}, + OPTedition = {}, + month = {Jan}, + OPTpages = {}, + OPTannote = {} +} + +@InBook{ChVCV13, + author = {St\'{e}phane Vialle and Sylvain Contassot-Vivier}, + editor = {Magoul\'{e}s, Fr\'{e}d\'{e}ric}, + title = {Patterns for parallel programming on {G}{P}{U}s}, + chapter = {Optimization methodology for Parallel Programming of Homogeneous or Hybrid Clusters}, + publisher = {Saxe-Coburg Publications}, + year = {2013}, + OPTkey = {}, + OPTvolume = {}, + OPTnumber = {}, + OPTseries = {}, + OPTtype = {}, + OPTaddress = {}, + OPTedition = {}, + month = feb, + OPTpages = {}, + note = {to appear}, + OPTannote = {} +} + +@Book{BCC07, + author = {Bahi, Jacques M. and Contassot-Vivier, Sylvain and Couturier, Rapha\"{e}l}, + title = {Parallel Iterative Algorithms: from sequential to grid computing}, + publisher = {Chapman \& Hall/CRC}, + year = {2007}, + series = {Numerical Analysis \& Scientific Computing Series}, + OPTdoi = {http://www.crcpress.com/shopping_cart/products/product_detail.asp?sku=C808X&isbn=9781584888086&parent_id=&pc=}, +} + +@InProceedings{HPCS2002, + author = {Bahi, Jacques M. and Contassot-Vivier, Sylvain and Couturier, Rapha\"{e}l}, + title = {Asynchronism for Iterative Algorithms in a Global Computing Environment}, + booktitle = {The 16th Annual International Symposium on High Performance +Computing Systems and Applications (HPCS'2002)}, + pages = "90--97", + year = {2002}, + address = {Moncton, Canada}, + month = jun, +} + +@InProceedings{Vecpar08a, + author = {Bahi, Jacques M. and Contassot-Vivier, Sylvain and Couturier, Rapha\"{e}l}, + title = {An efficient and robust decentralized algorithm for detecting the global +convergence in asynchronous iterative algorithms}, + booktitle = {8th International Meeting on High Performance Computing for Computational Science, VECPAR'08}, + pages = {251--264}, + year = {2008}, + address = {Toulouse}, + month = jun, +} + +@article{ParCo05, + author = {Bahi, Jacques M. and Contassot-Vivier, Sylvain and Couturier, Rapha\"{e}l}, + title = {Evaluation of the Asynchronous Iterative Algorithms in the Context of Distant Heterogeneous Clusters}, + journal = {Parallel Computing}, + volume = {31}, + number = {5}, + year = {2005}, + pages = {439-461} +} + +@InProceedings{ECost10, + author = {Contassot-Vivier, Sylvain and Vialle, St\'{e}phane and Jost, Thomas}, + title = {Optimizing computing and energy performances on {GPU} clusters: experimentation on a {PDE} solver}, + booktitle = {COST Action IC0804 on Large Scale Distributed Systems,1st Year}, + OPTpages = {}, + year = {2010}, + editor = {Jean-Marc Pierson and Helmut Hlavacs}, + organization = {IRIT}, + note = {ISBN: 978-2-917490-10-5}, +} + +@Article{SuperCo05, + author = {Bahi, Jacques M. and Contassot-Vivier, Sylvain and Couturier, Rapha\"{e}l}, + title = {Performance comparison of parallel programming environments for implementing {AIAC} algorithms}, + journal = {Journal of Supercomputing. Special Issue on Performance Modelling and Evaluation of Parallel and Distributed Systems}, + year = {2006}, + volume = {35}, + number = {3}, + pages = {227-244}, +} + +@InProceedings{Para10, + author = {Contassot-Vivier, Sylvain and Jost, Thomas and Vialle, St\'{e}phane}, + title = {Impact of asynchronism on {GPU} accelerated parallel iterative computations}, + booktitle = {PARA 2010 conference: State of the Art in Scientific and Parallel Computing}, + OPTpages = {--}, + year = {2010}, + address = {Reykjavík, Iceland}, + month = jun, +} + +@InProceedings{ECost10, + author = {Contassot-Vivier, Sylvain and Vialle, St\'{e}phane and Jost, Thomas}, + title = {Optimizing computing and energy performances on {GPU} clusters: experimentation on a {PDE} solver}, + booktitle = {COST Action IC0804 on Large Scale Distributed Systems,1st Year}, + OPTpages = {}, + year = {2010}, + editor = {Jean-Marc Pierson and Helmut Hlavacs}, + organization = {IRIT}, + note = {ISBN: 978-2-917490-10-5}, +} + +@InCollection{JCVV10, + author = {Thomas Jost and Sylvain Contassot-Vivier and St\'{e}phane Vialle}, + title = {An efficient multi-algorithm sparse linear solver for {GPU}s}, + booktitle = {Parallel Computing : From Multicores and GPU's to Petascale}, + pages = {546--553}, + publisher = {IOS Press}, + year = {2010}, + OPTeditor = {}, + volume = {19}, + OPTnumber = {}, + series = {Advances in Parallel Computing}, + OPTtype = {}, + OPTchapter = {}, + OPTaddress = {}, + OPTedition = {}, + OPTmonth = {}, + OPTnote = {}, + OPTannote = {Extended version of EuroGPU symposium article, in the International Conference on Parallel Computing (ParCo) 2009} +} + +@InProceedings{ParCo09, + author = {Thomas Jost and Sylvain Contassot-Vivier and St\'{e}phane Vialle}, + title = {An efficient multi-algorithms sparse linear solver for {GPU}s}, + booktitle = {EuroGPU mini-symposium of the International Conference on Parallel Computing, ParCo'2009}, + pages = {546--553}, + year = {2009}, + address = {Lyon}, + month = sep, +} + +@InProceedings{BCVG11, + author = {Bahi, Jacques M. and Contassot-Vivier, Sylvain and Giersch, Arnaud}, + title = {Load Balancing in Dynamic Networks by Bounded Delays Asynchronous Diffusion}, + booktitle = {VECPAR 2010}, + pages = {352--365}, + year = {2011}, + editor = {J.M.L.M. Palma et al.}, + volume = {6449}, + OPTnumber = {}, + series = {LNCS}, + publisher = {Springer, Heidelberg}, + note = "\url{DOI:~10.1007/978-3-642-19328-6\33}" +} + +@manual{CUDA, + title = {{NVIDIA {CUDA} C Programming Guide 4.0}}, + organization = {NVIDIA}, + howpublished = "\url{http://developer.download.nvidia.com/compute/DevZone/docs/html/C/doc/CUDA_C_Programming_Guide.pdf}", + month = {June}, + year = {2011} +} + +@Misc{openMPI, + title = {Open Source High Performance Computing}, + howpublished = {\url{http://www.open-mpi.org}} +} + +@Misc{MPI, + title = {Message Passing Interface}, + howpublished = {\url{http://www.mpi-forum.org/docs}} +} + +@Misc{openMP, + title = {Open{M}{P} multi-threaded programming {API}}, + howpublished = {\url{http://www.openmp.org}} +} + +@article{Hoefler08a, +author = {Torsten Hoefler and Andrew Lumsdaine}, +title = {Overlapping Communication and Computation with High Level Communication Routines}, +journal ={Cluster Computing and the Grid, IEEE International Symposium on}, +OPTvolume = {0}, +isbn = {978-0-7695-3156-4}, +year = {2008}, +pages = {572-577}, +note = "\url{http://doi.ieeecomputersociety.org/10.1109/CCGRID.2008.15}", +publisher = {IEEE Computer Society}, +address = {Los Alamitos, CA, USA}, +} + +@Article{Valiant:BSP, + author = {Valiant, Leslie G.}, + title = {A bridging model for parallel computation}, + journal = {Communications of the ACM}, + year = 1990, + volume = 33, + number = 8, + pages = {103-111} +} + +@inproceedings{gustedt:hal-00639289, + AUTHOR = {Gustedt, Jens and Jeanvoine, Emmanuel}, + TITLE = {{Relaxed Synchronization with Ordered Read-Write Locks}}, + BOOKTITLE = {{Euro-Par 2011: Parallel Processing Workshops}}, + YEAR = {2012}, + MONTH = May, + SERIES = {LNCS}, + EDITOR = {Michael Alexander and others}, + PUBLISHER = {Springer}, + VOLUME = {7155}, + PAGES = {387-397}, + ADDRESS = {Bordeaux, France}, + X-INTERNATIONAL-AUDIENCE = {yes}, + X-PROCEEDINGS = {yes}, + URL = {http://hal.inria.fr/hal-00639289}, + X-ID-HAL = {hal-00639289}, +} + +@article{clauss:2010:inria-00330024:1, + AUTHOR = {Clauss, Pierre-Nicolas and Gustedt, Jens}, + TITLE = {{Iterative Computations with Ordered Read-Write Locks}}, + JOURNAL = {{Journal of Parallel and Distributed Computing}}, + PUBLISHER = {Elsevier}, + VOLUME = {70}, + NUMBER = {5}, + PAGES = {496-504}, + YEAR = {2010}, + DOI = {10.1016/j.jpdc.2009.09.002}, + X-INTERNATIONAL-AUDIENCE = {yes}, + X-EDITORIAL-BOARD = {yes}, + URL = {http://hal.inria.fr/inria-00330024/en}, + X-ID-HAL = {inria-00330024}, +} + +@inproceedings{GUSTEDT:2007:HAL-00280094:1, + TITLE = {The par{X}{X}{L} Environment: Scalable Fine Grained Development for Large Coarse Grained Platforms}, + X-PAYS = {IT}, + X-INTERNATIONAL-AUDIENCE = {yes}, + AUTHOR = {Gustedt, Jens AND Vialle, Stéphane AND De Vivo, Amelia}, + BOOKTITLE = {PARA 06}, + LONG-BOOKTITLE = {PARA 06: Worshop on state-of-the-art in scientific and parallel computing }, + EDITOR = {Bo K{\aa}gstr{\"o}m and others}, + PAGES = {1094-1104 }, + ADDRESS = {Ume{\aa}, Sweden}, + SERIES = LNCS, + PUBLISHER = {Springer}, + VOLUME = {4699}, + YEAR = 2007, + URL = {http://hal-supelec.archives-ouvertes.fr/hal-00280094/en/}, + X-PROCEEDINGS = {yes}, +} + +@InProceedings{suss04:users_exper, + author = {S\"{u}{\ss}, Michael and Leopold, Claudia}, + title = {A User's Experience with Parallel Sorting and {O}pen{M}{P}}, + booktitle = {Proceedings of the 6th European Workshop on OpenMP (EWOMP)}, + pages = {23-28}, + year = 2004, + editor = {Eduard Ayguad\'{e} and others}, + address = {Stockholm, Sweden}} + + +@Book{C11, + editor = {JTC1/SC22/WG14}, + title = {Programming languages - C}, + publisher = {ISO}, + year = 2011, + number = {ISO/IEC 9899}, + edition = {Cor. 1:2012}} diff --git a/BookGPU/Chapters/chapter6/ch6.aux b/BookGPU/Chapters/chapter6/ch6.aux new file mode 100644 index 0000000..973672c --- /dev/null +++ b/BookGPU/Chapters/chapter6/ch6.aux @@ -0,0 +1,147 @@ +\relax +\@writefile{toc}{\author{Sylvain Contassot-Vivier}{}} +\@writefile{toc}{\author{Stephane Vialle}{}} +\@writefile{toc}{\author{Jens Gustedt}{}} +\@writefile{loa}{\addvspace {10\p@ }} +\@writefile{toc}{\contentsline {chapter}{\numberline {3}Development methodologies for GPU and cluster of GPUs}{23}} +\@writefile{lof}{\addvspace {10\p@ }} +\@writefile{lot}{\addvspace {10\p@ }} +\@writefile{toc}{\contentsline {section}{\numberline {3.1}Introduction}{24}} +\newlabel{ch6:intro}{{3.1}{24}} +\@writefile{toc}{\contentsline {section}{\numberline {3.2}General scheme of synchronous code with computation/communication overlapping in GPU clusters}{24}} +\newlabel{ch6:part1}{{3.2}{24}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.2.1}Synchronous parallel algorithms on GPU clusters}{24}} +\@writefile{lof}{\contentsline {figure}{\numberline {3.1}{\ignorespaces Native overlap of internode CPU communications with GPU computations.\relax }}{26}} +\newlabel{fig:ch6p1overlapnative}{{3.1}{26}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.2.2}Native overlap of CPU communications and GPU computations}{26}} +\newlabel{algo:ch6p1overlapnative}{{3.1}{27}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.1}Generic scheme implicitly overlapping MPI communications with CUDA GPU computations}{27}} +\@writefile{lof}{\contentsline {figure}{\numberline {3.2}{\ignorespaces Overlap of internode CPU communications with a sequence of CPU/GPU data transfers and GPU computations.\relax }}{28}} +\newlabel{fig:ch6p1overlapseqsequence}{{3.2}{28}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.2.3}Overlapping with sequences of transfers and computations}{28}} +\newlabel{algo:ch6p1overlapseqsequence}{{3.2}{29}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.2}Generic scheme explicitly overlapping MPI communications with sequences of CUDA CPU/GPU transfers and CUDA GPU computations}{29}} +\@writefile{lof}{\contentsline {figure}{\numberline {3.3}{\ignorespaces Overlap of internode CPU communications with a streamed sequence of CPU/GPU data transfers and GPU computations.\relax }}{30}} +\newlabel{fig:ch6p1overlapstreamsequence}{{3.3}{30}} +\newlabel{algo:ch6p1overlapstreamsequence}{{3.3}{31}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.3}Generic scheme explicitly overlapping MPI communications with streamed sequences of CUDA CPU/GPU transfers and CUDA GPU computations}{31}} +\@writefile{lof}{\contentsline {figure}{\numberline {3.4}{\ignorespaces Complete overlap of internode CPU communications, CPU/GPU data transfers and GPU computations, interleaving computation-communication iterations\relax }}{33}} +\newlabel{fig:ch6p1overlapinterleaved}{{3.4}{33}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.2.4}Interleaved communications-transfers-computations overlapping}{33}} +\newlabel{algo:ch6p1overlapinterleaved}{{3.4}{34}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.4}Generic scheme explicitly overlapping MPI communications, CUDA CPU/GPU transfers and CUDA GPU computations, interleaving computation-communication iterations}{34}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.2.5}Experimental validation}{36}} +\newlabel{ch6:p1expes}{{3.2.5}{36}} +\newlabel{ch6:p1block-cyclic}{{3.2.5}{36}} +\@writefile{lof}{\contentsline {figure}{\numberline {3.5}{\ignorespaces Experimental performances of different synchronous algorithms computing a dense matrix product\relax }}{37}} +\newlabel{fig:ch6p1syncexpematrixprod}{{3.5}{37}} +\@writefile{toc}{\contentsline {section}{\numberline {3.3}General scheme of asynchronous parallel code with computation/communication overlapping}{38}} +\newlabel{ch6:part2}{{3.3}{38}} +\@writefile{loa}{\contentsline {algorithm}{\numberline {1}{\ignorespaces Synchronous iterative scheme\relax }}{38}} +\newlabel{algo:ch6p2sync}{{1}{38}} +\@writefile{loa}{\contentsline {algorithm}{\numberline {2}{\ignorespaces Asynchronous iterative scheme\relax }}{38}} +\newlabel{algo:ch6p2async}{{2}{38}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.1}A basic asynchronous scheme}{40}} +\newlabel{ch6:p2BasicAsync}{{3.3.1}{40}} +\newlabel{algo:ch6p2BasicAsync}{{3.5}{40}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.5}Initialization of the basic asynchronous scheme}{40}} +\newlabel{algo:ch6p2BasicAsyncComp}{{3.6}{41}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.6}Computing function in the basic asynchronous scheme}{41}} +\newlabel{algo:ch6p2BasicAsyncSendings}{{3.7}{42}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.7}Sending function in the basic asynchronous scheme}{42}} +\newlabel{algo:ch6p2BasicAsyncReceptions}{{3.8}{43}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.8}Reception function in the basic asynchronous scheme}{43}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.2}Synchronization of the asynchronous scheme}{44}} +\newlabel{ch6:p2SsyncOverAsync}{{3.3.2}{44}} +\newlabel{algo:ch6p2Sync}{{3.9}{45}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.9}Initialization of the synchronized scheme}{45}} +\newlabel{algo:ch6p2SyncComp}{{3.10}{46}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.10}Computing function in the synchronized scheme}{46}} +\newlabel{algo:ch6p2SyncReceptions}{{3.11}{47}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.11}Reception function in the synchronized scheme}{47}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.3}Asynchronous scheme using MPI, OpenMP and CUDA}{48}} +\newlabel{ch6:p2GPUAsync}{{3.3.3}{48}} +\newlabel{algo:ch6p2AsyncSyncComp}{{3.12}{50}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.12}Computing function in the final asynchronous scheme}{50}} +\newlabel{algo:ch6p2syncGPU}{{3.13}{51}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.13}Computing function in the final asynchronous scheme}{51}} +\newlabel{algo:ch6p2FullOverAsyncMain}{{3.14}{53}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.14}Initialization of the main process of complete overlap with asynchronism}{53}} +\newlabel{algo:ch6p2FullOverAsyncComp1}{{3.15}{54}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.15}Computing function in the final asynchronous scheme with CPU/GPU overlap}{54}} +\newlabel{algo:ch6p2FullOverAsyncComp2}{{3.16}{55}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.16}Auxiliary computing function in the final asynchronous scheme with CPU/GPU overlap}{55}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.3.4}Experimental validation}{56}} +\newlabel{sec:ch6p2expes}{{3.3.4}{56}} +\@writefile{lof}{\contentsline {figure}{\numberline {3.6}{\ignorespaces Computation times of the test application in synchronous and asynchronous modes.\relax }}{57}} +\newlabel{fig:ch6p2syncasync}{{3.6}{57}} +\@writefile{lof}{\contentsline {figure}{\numberline {3.7}{\ignorespaces Computation times with or without overlap of Jacobian updatings in asynchronous mode.\relax }}{58}} +\newlabel{fig:ch6p2aux}{{3.7}{58}} +\@writefile{toc}{\contentsline {section}{\numberline {3.4}Perspective: A unifying programming model}{59}} +\newlabel{sec:ch6p3unify}{{3.4}{59}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.4.1}Resources}{59}} +\newlabel{sec:ch6p3resources}{{3.4.1}{59}} +\newlabel{algo:ch6p3ORWLresources}{{3.17}{60}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.17}Declaration of ORWL resources for a block-cyclic matrix multiplication}{60}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.4.2}Control}{60}} +\newlabel{sec:ch6p3ORWLcontrol}{{3.4.2}{60}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.4.3}Example: block-cyclic matrix multiplication (MM)}{61}} +\newlabel{sec:ch6p3ORWLMM}{{3.4.3}{61}} +\newlabel{algo:ch6p3ORWLBCCMM}{{3.18}{61}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.18}Block-cyclic matrix multiplication, high level per task view}{61}} +\newlabel{algo:ch6p3ORWLlcopy}{{3.19}{62}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.19}An iterative local copy operation}{62}} +\newlabel{algo:ch6p3ORWLrcopy}{{3.20}{62}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.20}An iterative remote copy operation as part of a block cyclic matrix multiplication task}{62}} +\newlabel{algo:ch6p3ORWLtrans}{{3.21}{62}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.21}An iterative GPU transfer and compute operation as part of a block cyclic matrix multiplication task}{62}} +\newlabel{algo:ch6p3ORWLdecl}{{3.22}{63}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.22}Dynamic declaration of handles to represent the resources}{63}} +\newlabel{algo:ch6p3ORWLinit}{{3.23}{64}} +\@writefile{lol}{\contentsline {lstlisting}{\numberline {3.23}Dynamic initialization of access mode and priorities}{64}} +\@writefile{toc}{\contentsline {subsection}{\numberline {3.4.4}Tasks and operations}{64}} +\newlabel{sec:ch6p3tasks}{{3.4.4}{64}} +\@writefile{toc}{\contentsline {section}{\numberline {3.5}Conclusion}{65}} +\newlabel{ch6:conclu}{{3.5}{65}} +\@writefile{toc}{\contentsline {section}{\numberline {3.6}Glossary}{65}} +\@writefile{toc}{\contentsline {section}{Bibliography}{66}} +\@setckpt{Chapters/chapter6/ch6}{ +\setcounter{page}{68} +\setcounter{equation}{0} +\setcounter{enumi}{4} +\setcounter{enumii}{0} +\setcounter{enumiii}{0} +\setcounter{enumiv}{21} +\setcounter{footnote}{0} +\setcounter{mpfootnote}{0} +\setcounter{part}{1} +\setcounter{chapter}{3} +\setcounter{section}{6} +\setcounter{subsection}{0} +\setcounter{subsubsection}{0} +\setcounter{paragraph}{0} +\setcounter{subparagraph}{0} +\setcounter{figure}{7} +\setcounter{table}{0} +\setcounter{numauthors}{0} +\setcounter{parentequation}{0} +\setcounter{subfigure}{0} +\setcounter{lofdepth}{1} +\setcounter{subtable}{0} +\setcounter{lotdepth}{1} +\setcounter{lstnumber}{17} +\setcounter{ContinuedFloat}{0} +\setcounter{float@type}{16} +\setcounter{algorithm}{2} +\setcounter{ALC@unique}{0} +\setcounter{ALC@line}{0} +\setcounter{ALC@rem}{0} +\setcounter{ALC@depth}{0} +\setcounter{AlgoLine}{0} +\setcounter{algocfline}{0} +\setcounter{algocfproc}{0} +\setcounter{algocf}{0} +\setcounter{proposition}{0} +\setcounter{proof}{0} +\setcounter{lstlisting}{23} +} diff --git a/BookGPU/Chapters/chapter6/ch6.tex b/BookGPU/Chapters/chapter6/ch6.tex new file mode 100755 index 0000000..79cd429 --- /dev/null +++ b/BookGPU/Chapters/chapter6/ch6.tex @@ -0,0 +1,89 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Personnal defs +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\newcommand{\Sec}[1]{Section~\ref{#1}} +\newcommand{\Fig}[1]{Figure~\ref{#1}} +\newcommand{\Alg}[1]{Algorithm~\ref{#1}} +\newcommand{\Lst}[1]{Listing~\ref{#1}} +\newcommand{\Tab}[1]{Table~\ref{#1}} +\newcommand{\Equ}[1]{(\ref{#1})} +\def\Reals{\mathbb{R}} + +\definecolor{shadecolor}{rgb}{0.95,0.95,0.95} +\newenvironment{Algo}{\vspace{-1em}\begin{center}\begin{minipage}[h]{0.95\columnwidth}\begin{shaded}\begin{tabbing}% + \hspace{3mm}\=\hspace{3mm}\=\hspace{3mm}\=\hspace{3mm}\=\hspace{3mm}\=\hspace{3mm}\=\hspace{3mm}\= \kill} % + { \end{tabbing}\vspace{-1em}\end{shaded}\end{minipage}\end{center}\vspace{-1em}} + +\lstnewenvironment{Listing}[2]{\lstset{basicstyle=\scriptsize\ttfamily,% + breaklines=true, breakatwhitespace=true, language=C, keywordstyle=\color{black},% + prebreak = \raisebox{0ex}[0ex][0ex]{\ensuremath{\hookleftarrow}},% + commentstyle=\textit, numbersep=1em, numberstyle=\tiny, numbers=left,% + numberblanklines=false, mathescape, escapechar=@, label=#1, caption={#2}}}{} + +\def\N{$\mathbb N$ } +\def\R{$\mathbb R$ } +\def\Z{$\mathbb Z$ } +\def\Q{$\mathbb Q$ } +\def\C{$\mathbb C$ } +\def\affect{$\leftarrow$ } + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Main document +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +\chapterauthor{Sylvain Contassot-Vivier}{Université Lorraine, Loria UMR 7503 \& AlGorille INRIA Project Team, Nancy, France.} +\chapterauthor{Stephane Vialle}{SUPELEC, UMI GT-CNRS 2958 \& AlGorille INRIA Project Team, Metz, France.} +\chapterauthor{Jens Gustedt}{INRIA Nancy -- Grand Est, AlGorille INRIA Project Team, Strasbourg, France.} + +\chapter{Development methodologies for GPU and cluster of GPUs} + +% Introduction +\input{Chapters/chapter6/Intro} + +% Partie 1 : CUDA - MPI synchrone avec recouvrement +\input{Chapters/chapter6/PartieSync} + +% Partie 2 : CUDA - MPI asynchrone avec recouvrement +\input{Chapters/chapter6/PartieAsync} + +% Partie 6 : Analyse prospective +\input{Chapters/chapter6/PartieORWL} + +% Conclusion +\input{Chapters/chapter6/Conclu} + +% Glossaire +\section{Glossary} +\begin{Glossary} +\item[AIAC] Asynchronous Iterations - Asynchronous Communications. +\item[Asynchronous iterations] Iterative process where each element is updated + without waiting for the last updates of the other elements. +\item[Auxiliary computations] Optional computations performed in parallel to the + main computations and used to complete them or speed them up. +\item[BSP parallel scheme] Bulk Synchronous Parallel, a parallel model that uses + a repeated pattern (superstep) composed of: computation, communication, barrier. +\item[GPU stream] Serialized data transfers and computations performed on a same + piece of data. +\item[Message loss/miss] Can be said about a message that is either not + sent or sent but not received (possible with unreliable communication protocols). +\item[Message stamping] Inclusion of a specific value in messages of the same tag to + distinguish them (kind of secondary tag). +\item[ORWL] Ordered Read Write Locks, a programming tool proposing a unified + programming model. +\item[Page-locked data] Data that are locked in cache memory to ensure fast accesses. +\item[Residual] Difference between results of consecutive iterations in an + iterative process. +\item[Streamed GPU sequence] GPU transfers and computations performed + simultaneously via distinct GPU streams. +\end{Glossary} + +% Biblio +\vspace{-2em} +\putbib[Chapters/chapter6/biblio6] + +%%% Local Variables: +%%% mode: latex +%%% fill-column: 80 +%%% ispell-dictionary: "american" +%%% mode: flyspell +%%% TeX-master: "../../BookGPU.tex" +%%% End: diff --git a/BookGPU/Chapters/chapter6/curves/gpuSyncOverlap.pdf b/BookGPU/Chapters/chapter6/curves/gpuSyncOverlap.pdf new file mode 100644 index 0000000000000000000000000000000000000000..76d47cfd483d73802e244bc8dc9c15259ede7ead GIT binary patch literal 7518 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 literal 0 HcmV?d00001 diff --git a/BookGPU/Chapters/chapter6/curves/syncasync.pdf b/BookGPU/Chapters/chapter6/curves/syncasync.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f9c764c3a58369fd7ab33a2a30d19649c1286827 GIT binary patch literal 7072 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 literal 0 HcmV?d00001 diff --git a/BookGPU/Chapters/chapter6/figures/Sync-CompleteInterleaveOverlap.pdf b/BookGPU/Chapters/chapter6/figures/Sync-CompleteInterleaveOverlap.pdf new file mode 100644 index 0000000000000000000000000000000000000000..85d019de9ba7fc77e559a07e50b49c12fc81424e GIT binary patch literal 44466 zcmaI7b9AN8wk{moHabo^PF8Gm(6P-G+qRvKtqwXJ+qP|69h+bJclJJKpL@ss{#fg+ zs)?HQ&S%aw#v1id$cu{8Gc&NkQ4F5N=EJd*Fp=0ATEOx0GJXfzm^zt}06td~8O6=5 zoPZ7_jN(=XPC!whk*zV1pC8WA$pL6!4d<3w?kyc_(`rxnD&e#acc%V>kc-i)mk!** zwqw}buglLL;mp$?!8kU?h!DpcK^=GRBBOqxbmv{RF-jsF;+XApcf$+ww<_Pz+hpu1 zJ0HVK<@?+1{yyWo39rvnr{4S1zQ>qP`$9I6o~{E;@#8=~W`)WDg8(Lf=j-I4fb|UH z0}q<+`;3*rZ_w|!~JedmLxlEpi2rH$EY zDk{BLy|>Dgi=Dv*fk)=~G+p2Ez|Je$xo531;V4C&;k&?!SK+|U``k*tBd5G`<;G5e zfTUHEBE8Kwo!8QjmtSm^z>m_8&c}OKpN9vZ`x~#M^^eb!T=Z;uf2f^MWk1g|PL^rK zKJ4o0!oko`+cLJsk8gVpS1ht$t=bgKJ5#(^%B`(Qzi{|Iy)r}{9qVKmiqy0-uUKlR zb=J2$Tgtvvw)g9L>MohIPLwtJh-S5|GQzh0A%1@?Y&SVwHOVVp%mxD6zJ z=P9^tJG?#A!ZO%=+ga^=o1eTFZ@aEvY4m;kc6j0Y!TNC;D^PJ+K>RH_hu|c~>!w)f z9Xa$hFh_s4%pw~5#h2}!?Da7A<5KyxfH;c~z})YNr#22u=9_6>F+aHCUNyRyTTHJ$ zG}Wyv zv4fv#|4LWgmSLHJt7_=2TH>jsQ__Wc-k#Lju5T^ABwYIYdF6-Ohc+6gVan<0O6-Mu z^>$SM^!}S*45)gqr==Rgqs2?C&OubF`dSStFq)HM%uIQSRaxPTUdvuU@3N!rwktaW zK}v7b!fyIrYRkVJf{V{tGO;D2Eb@1gskGK-VC;(_)dK^Y>Z4yzv7q_UV%Cp` z4go~WiE{=@rvh#D7`{4%<>~PoN5jj^OPzCA$JvpVBrZ>n9%|1XPniduYHVvm{?Xr8 z3)%ZckSy2PBE*)P!erLhrI88t?1|H$yKxaa*@~AW8p@rq&AR<4xCq*U8{n)V4bMiE zipUHb`xB@(6J@lO7?I&r>IL)TSPOlrnP&74OJDM_&uWNoF>b#3RJ1GxI>Rx7oJ*Ty8P;>xnL) zA4x)@-2BnM1bdn~eTWop9J!!QAo*iFSqM0{3IDEK@P}dxzI4Q`mopQrXftSD!T+_j z^(Qo7wQe<4;NqL6BNV&;&AvQ^u_QlW%>_quXcwzoiEa5a|@PkZ7A{K6wO56#r!H524})9b|YYmF2)%9Oz$RHw&q8J88p-i@d>!%RzRRz|BcjA&_P$z`U<`oqh& zHhix$34>{QvV6h#0C4 zLH6QEbCv}xs8x%+)mmB}epE76xFQrMJ9!G$O%xfum+Mlg5WUSuTL(k&Uw_gGBeDIs`2aO_{NyB1sXrsR79Uut*`xK__2-!=>jc>6R2k<5)gM2dlTNn z%2X2J`$>YD>zA`ZdRQWP2>tXuU8C?F6Y0*DlLW9lJHv_XM!jyp(`zQQ#Jl3}dvo43 zIHBHmeV?y#M7IY&xZYonIXE6PIbOMl=Iv&)s~N?+CzgKHU>cR!7(N@E&0EF1Foo{) zYCC&Zb`!Z?#7RI}KxZOtgTPcf_a2n%*d)-+;_prVs;=MMump$&&XzVQ5Q2TsIp2Aedk?r=#wkfq%UkbDjVpxZ>o9qrMji_8ug=;r?Abm%E zO_OdSSeb84Rc-uC_K1wD7#8OeSnD(_Y)>8lUjWL?iHC`j`K98QEPr(iTvCFP71#6v zN!M4xy3HGZBN}BQgGsIL-DklW+Bzv;Un_@F&W^{)nH^+f3eFgoO5~`4u&^@m!+QqXN@lfwo+NF&!9J?UFkWvfGJj5Mf-%xd=BL{4 z(ZF^8^#DkobV^SlENn;0?)1&a`T#MMyA_~P0<{)$7i%0!Bi%GywP z)>1@4mq6AKD3fo(2z=H;l}dp#tmTz8c85x2$JP;gV|tqTo|b-tdU_>REWGB}Vk4F3 zu<^>}cwf-4c37c;f%5D`<9MBC8;6J<>EyRyYYy$knDKefeVKkoW6bM~r?G6a#lQw_ zFleYjcARVu3NAdiebru8|62DY}xdYIW`f*U8dy`J8mb%+x4-(4c3UCA~iwA;UUfTR85Xn@doZam#Aoj7K% z!Fb&D=$BtCSY0xWo`=gH=>jlZf4f7B#$1_kbgh%f#slI_q~s@6(|s#h#DgKcZyxi#DS49AV6B!t#QUGvMCcqp1Yd zBkfPP{OqdenR9tWiX1q^dctIx~w8Z-O9S)=G_gwILd?bSwbs_rs_c7 z9{?uNy=OZZ)h73Ccv<)2Wvb$9zqjgjIic9WJPc2Tv{?>AiL)9`UFnMBP>O4$WjblPW&qTAgVLBeWeoF_S0>yEqKm)c_ z!9l+&(V@45-zQyZK2U8np5+PA1sPXvkh9N6FA6r@DC8j_y@ev;B!pgj8Ipl7#;H0A z!KD%-MUoEuhIvo%aB>J17{p?$P7qY7-k5L0a%K3kJ-~t$FG;Lm^F7f(c2Jr%6w^BS z^p|x1>q_w?q)!=Z5!go!*B8$5gU#Hvd^q~42NpDWj{_n_ylh|3t7pDTzO2XwQr1mV zp*o*kyWa6P^cff`)BHiA2# zjXyJnkr77AuX?P=s~1Ue)X~_o=5`O>J5d6el-lWhx4|~N<&Ig91k-XKK}?1BrkF5! zQ(&jp?aLe@d3F5=2y%moZV*e!G~f4hs`Pi)KlJ0fqQhmt*+Md#^;(?*#p+jsj7^WK zL9PIUQ>q)@%#5K^MHWG@Mz%{z23k;xUOjXw6E&J;EWMK8sbLuY85x6y3-0UXWoctf zO)7%m6vN2+wE#cxwewm(o52H3d2qlaq)beB6Si8a>{`wP?_sk27^b5f~vxM^B}eX=&T<8^tVp-#=Ns0|mzn2b@&39Qc-T3bO}F2*E|_XrcEeO9g_pQ)Mp z@mL%otW!>l?Y?{+2~z_1aeDw2!om4+Y6IiVucgDE%_#JXuP%{u4CC9EUccVbm*6e~ z6RHI&C_2_u90{hWcx!M+Q;7~A4+Wy$jqD)j1ec%Ii%Vuf!Zz_*4v5{=7RGyZi3hw{ z$9jC~i}wy1M-+|2Sd8OnFogwWYs7ar;idX^P$pPGplcx|?U4A|e!U#wg*D1r_A8hR zA`DPJVF&E;&fd|`vEF`(qr%INr`|S6m5}oKyVc|o*vzhpNcgt@&|dc!+=`RDqo8&U zS+J9`cW2%zWF2IM7XJ1K_c99;xs@_10Hd=`2sqBCC$pO*kT{{P*eF8~ln}8+;_QsM zSp9NZxJftl!(QFE`VA@-l=G#lbEkcyTgH~|k z8WwK`CxAw>MBwE3c!owE{J0vZ8z20FC3Oc+v_>1mdUOrWQ?!zMIWl#<^?${g3FjY@vK(ebSr)pv1d81USCa1ed_dLJ36+cdJKrVI;c&{uH4ROh_Ks7WG zU{!7hJ;#uEM@w9NrQ3SW-pQV8bUWcIOs=}i+;P*1zpvqy+A!!cYg*Wbw8Di7fV@EO zhfeWUz$keQpZ4MJ#*3tBs6NMM?lCh|z5}z3ltyuHC<$rBwvKz*^a#!v`7+*Gq{N0A zr?=LB54SYul&E!zc%b+dXF7OUdyQa}BKpxn15Ic8`>sFj>Sz8)*h+rw;dctAAuX zi=cK;81*pkw`>v8F(hJx>oFi*L0BxM;oYaZH>kviuR|5p5KZN?h2dR%ABq=Y;c`(7 zxHRYbu7iL4UO3F5k6ue*;)qxjZp}KzrqIH+ltF#@bc4AMmqM%vJHQ;pJnO_t%iu2> z2|AwtCpoWu05htzzIk5I;=%5H3Wb{&31`Ny;l?d4X%xXK^cpbUYK6&X;7!Zha7`J( z6*}}&T4)gC299R2((VFJ;Co-(5y%D~>-;A9K{Xnn`rDZn?Ig|v-VjxDyT4B~T@;4a zMTqXyW=q%{SxoSCl>z74B_90(F=|HGqZVVO*Cg!J&=<=ts?|U5{fRpnrd`RL>t4_U zNXgA{S<>)sc996kOPaDHZNZ{gwR_Ee)a95HQF7nE{l0uOTSIX`-TsD(&Lur($`aBm z!>1sZqu;I)%ApECUe4FUyx59S8^YWNgDps-vtgIfBRseY%GRRLXU1)pFCtj{O|g#} zTN1Js%n}&~=rP=@zA7ovK55j-zRQi0Im zbY9m8nYpV=CjF*T2)zRnZ93{@Xn_&P#P5Xkec?`R=9!_}@=M(ZS|VlDhj?0t=f)Z} zo5J*#NWR>XV$(>1-RNS0J5==WWqW`O$zs1CA3xph8-49>t-Xd~?7cs6B>s>JmL;^6 z@j7c1>E%R;^?)Y+lt=-%-#eh9t9qCyNp{l%cGYL;l)XwWznQ~)sLMLCxh6~kKX#yC z#F~2rq@yH>LhQrd+*c+e0D_vac3lHiiL0kNRjn>2@@8QBPjU=xaJ z2WZbcwXG~1ah(hRCyQ}*H^V-5HubtVglC@T^tf4>INN$QrVKliCkvh}kg4f{ZV zq59rR@la#!2W#m-lV+Wg_nh1sTi%v5(;^uQYg}`*ql-Re?k=1>a#O@2dNtIUo#S9% z>hG?df6`-B0lnP6?3GCnlyU`u()Eiu&d_nAtIy999~_OEo7pc|mKZu{%NwK2eqb2; z@hg*+R}O{CHX!zB$2vPlRrq%xWIND1+yTDB-(i%n%E6LjpEFH)-<~?d`xyt)xd>2> z07CFUOC@I{lb)CN{hnYBOQ;5DGOTnoPD3@*;AZy#J`&zu-VIaUnQSUA`@29|$>VXH z`a5v`Uf#r?hr4_%y22hA<`0<+v~fxm#w?1>HirT)?2$|&ehI-?%AAM&Tf9J8It0Au zR)+)AcfOeEeuzh{S8Ez+w!Zp(SpwfAOr_4jO{*EH_C>cCEX#D;^G9}&C>zkG{u`eM zH?0n|+rI5qFJP)UlCCRm9^WKjZND&4-G9*w)1-{{#wO+WA8a`e(d7xhZ!LL?mrlJp z;**IsO_7qn#vN#_M+#pdt@74kpRp6|nnvTu5S3226Y8lG&@ugkN4S)baF=k2$lhtMWlMxn2e;9C`= z=9pw(14=9;ScZ}QlmJ}?H||mTx_cw+yR&0Nm8~qoJMSp(k|rz7wKFAw->59oIsgVE zSwz7N@YusUXSvBCTSRdjjWQR+p17M6&Hbt2VA7naK~i!#ko;xQE@j<8pR1*E7hLzYc*`c(vmkwX7fG3T%OQu>eLN3DY}SdI zk=-jSxeC%}Hp{^e=$JL;j_`gq^yFe*f!F?asLWCX$ z@p;1IbaA>q0t9b~soSUU1;knf>!_ZaM_DpO{aoGzwqT;I;KTGbUdC|l9|`epNudbL zvT6<^7D(b!`y(Z?i|CxO7+0Ql>x5A4QVYwcep#IRRk)_iFmJqLPTi9mw6c0*vdnV< zn+L@~N=AAw`)fqlfHmuc&#k7F8<}~epLT_=6(M?q%awWpBLtIZtE*_wDpYF-5j1&F zhJ(8>dX@7?>IY)D1D^ul?XZJFIzBqiH^z9eP=j>Gk)jTfsRtMiD;F^vRv#<%MA!b+G84(-Utc$5v@_bL-)f)i za(r8_ppr_WSv<7JSBAG)DM|a*y?MsG73;{ZFlRpGGb6_137AbDYJDnhEIszUE5F5F z-&|o80bXp}5}v0oFWvjx%wbclF(4e>q+PDVPXveeRp0lZ?Sl!`oTBcshUkW8mY6!#Ppa`!Tw%r$u^sUfIMmT0!tUySfgX zkxk#33FrUh7(9cd`OLeOdIj}h96a^wxdQ>Db=VK*%O_-H#jZG44N3RxWh zH2Lm1>aF-b9SjOQ4s!^+1rqo5jUU2Sxi}s;`G3?zMbX8dDbv~+_Iso1*+As*9F-!5 z2;?7?!tYf@xx=7yv-tZ379DtWYb7Zp1-;T%TrRt9?&xQrI?mIBYRFe#U;m(PYuE<>j-I zjWsrRj~vuD0!t`;=IhQG&Z3-%{xaL8)$pBS>?|O=!Lr*A9s6(^ z;+Y8E#GgZIyNGOdkxry^d+5!Y=n8^n{6a6>l-7%=X#qNN-ddx4+St`(Y3GWCT)lr` zbfk8KD>-E_&$aT`jSH?C($)#?DaQrNQ1k}LVNPmjDUTL^r*b9B7~kT(;xQopfR)W1 z7vg%X+_e?KvC;EErJJD%rwJ5IP2K5YM4cPnCJw=oH?9WBIvmT1dd2Qb*LBI6PEJo) zVDT(Z+BesbZO#T}-@{t#l7WjzB}+ut^LSqv7}3;6Rr_$RQSW8>_*^upCvxRXRe58wA-1acfHRymt6-sQedOS|IjyI*6*TwA6 z+m5ao#h{^yurrul$vqFDCeJEe<=r?%jMBzqz6!y!3Q+9r>@L3phTU2gopk~u(5IU_ zSLNh1rk|yWJ-Xeq5*g5plQ^vvnc6zTv6w8~G(TNPQ~@pA;4;&p4igvTwf<@@xXyvr zpSv$qJz2)&70Q$h$V$K&s)sI}r2CniwKaF&*cm+dv%2V15aKsIn|N$;R(h%Gp3Cfd z$S1xSZ{&$?po4y?Z(ee-L&s;mscVw!+Hnwi%MgGo6}wcY%5ba$4WZ>iqmv2i(DMj{ z5@IsidhMZ{b|5Bxy?^bj*{{`kvSc+1=v3APjp9qhzrd8jws{_jUWB3uqp@SLg)o|N z4N0B56Wz%jXf+sH7Yt$PTG54FetQ@$cK(p|-ysvA=k0-OdZo>4{Eywq3`^){{%8F zx)?67N8bzf>?|G3wP*VM=A^8mGTZSnWK5_=G!zSth*{dLI&izBIi+R$?Ym7$1(I)V zt^+STp$fCGF$PkMip!}0mzk${$f}!vSahvSb(2m$n0y01;Voac$l7$h+T@O>A*>)+ln!h^1m9;! zoLb`!>^vri^you&i zg4*lVZ8>}NXd~W5?!-`+bgVip(fc5HzVi7phvIj2m2t`n@SkV3ZaLQXqSlTEx4hB@ z0Yf{UE^BInm|UD;U^8EG|7@}~CM5uunYwe)3y45{@3EG6ew)B>;hp|YBcPDeR z+^;BYu^*dhsvtOWZ|ouKYnJ+P)glM8IK1i%X&|q*CXau9RbR7<2oTT1Tx2Lkcwpu( ze)id<7$*)xV24`6g&jGG@+)nJ;9BjpiKb)3LUOgsUI*|FapWgx$T&lm@>=5}hbwTapos8_x5=6qStG7g7;?VH>kv#8< z%WK{2jjgigjZR7W!BO{(wg;tDI`b5JA^;tjkpMYU7eC}NM+xe(-+EFUTyk^_H)Ny; zUcR|6I-UW1br-bo9bYw?es+IUxh| zmq(Sz3H(=H#xNt>BXS(%NIpb6>bE!n-U#0j@z(QST*r?zYAf^#`*3X)KdhfO)?lnrb#yuVO2w0WyIZ)P{a-L z^zIFr;4D-3P!Ho49sG(X%+>cb-Pq6ht>=_YZ{J!|3|J%ovt(AcS667?Y24LU*iMDqhGFm+Ou z2hwSvZ|UwWsfbe%_pDD0i8#96oI2%tY^);_I2V3eo8d2V{*YO33*Au^01FjphE&5E zR$x#$<;L9Bepv&xTBHHv5(gVjRqmU}F8H%~$RqxFDA}P(k)--f&_KI~4x%?v&D9Jn z+*wYd)IwLy-857rdLxbAbb0=)zIjUpfexv#8LJ$Ufx08(D5L6qV%5T{URS;M7i7s4 zBUVU4NPNs#f_;1ho2C&G-uZhH^0_fm){Z#sn5!S3^Zg(S;6@MiBW2oVZ601nBL*t^ zB?AJI5AK{=;US;E@+eXFm-)Qm0~Q?@P!#Qpjhj}YWOxY5wB`>l%pv5~_4m@jxJH^@ zy2C=>tY2iv<22|Nx6u&4o0}+#`?}C{)7b)3di}<|it z{JvTC;sdRd9+}M&tRGCJRv1vM2Z}28WS)nG)t)mpJ1M*icVvj;FwHO_oHKEu>M6h0 ze-U$uN20ipMaYuE-;R-emE;VR=E_OzTGjZIyY{er>rE+qxa)IyDow8B^N7Ihp z)Kx|z-FaC?r-x%?xwl@{T-0CZvK8pjQ=vAFyDrH@M^mQ1ha8k%g;hldwOl-~a1VP4 zH)}wII;Hm85Ug6eW`2zwUf6lkN^&ORc5LNaYi2CI2uVO~0tW}<3jXbWqBfXc6`)hf z!3Wv0r>OYyzIZoX-N!=!B>Vy;=EEHCl+V z6t9)6KnFEau}fPZy^x+0*hBcIn5d|A`1}Vtg)Pj(kDRs!3iO&`Z*1u;U5$+CM5V_; z!-@Um=xh5?BCG_x22Km7&N5W91)=GbGZWIcngIZy_&k|j<2P+%89f@bj4V?}g0#K4 ziX*ipyoHIRTeD8Ib>%CkjYbALtz)G;1?W*q{a*ftt*n4m0IYMBqhFvqe#(W|t~3NG*+>9InH`pR$Rz;bhF= z0!f)S%WwJFg^z8gHsw`Oq*0UsnVDJ#q_@ZQ_p2GjmGfVdhLw{%%PUOUXv#?@7ZOuY?Uy`#w zZI7E91BiV!1P_c^|5l)+=)!x_O%6EtLA9PcAn{OWo7a8&drvkUjtE~4F324RFfAR3 z;St$OiR&p5*q~M_JWo`MjT4v=8F8rN6}gp9=0*;xesbMQw^!tGf!;KViW+sSVH}v^1(AnM(E?ehAlHqh5tTxo zT;1r6?d6fyu@&7gGu8sExZ35MeXzKZ@HC~(gt6raC=R(VBRe3LzeO3N;@Q9ss%Sl) zt0>OJ)uY^Sop6kGv8Tj>9ZbC37R*Olql?}`5U}U0qvYpt6aEbW{k$3}@TJ?t)uGF* z9R}6|rlp%iEyq-Y$~Mx3h{tplI=IFfK->UhN>nu>L7V!VdltPzL$hP+l{%?ey~_9% zHL%$$#rM0JjKJeh!Ci`vwL;Ffe$IClzNr|w@vXU3;k7J79pbu{7T=U%J~v(e2{fSV z>U+pzr&&|lY;tRN+dPUBdXo_i=`??e$%*sx46T#ef!mc) zz}Ad3_9{Y8e`UkS%{l4Vn_<}`Sx!QY4P|E+qDk*uTZXaG&^)Vrl;K;N567di#gx?{ ztdFR@ctKfpVwkB6PAo&YPdbuuO3hX-o$GJua3jD|(Q?@s56D)A=uRvx(-oB>Ld_J`)Qr+u#}|g9hvi*rY)S{HMavX@D_+Tdxa@AvX_Bj? zst0>IU-2V^$7G(*D^`6SOmb}BPaiNgbn0kmI~Q)sc}TH}OZZ zz!L*Sdo%jQ`lVuW7|Ca0KH~NISE=xt#7SudGr^Sp52Npjiz872NFNo(`4{_&r6T!c zE7-|7_Aq&t!vuDA5M-R&OflcrT!Y+x4naD=+>*LwxX&bcOqUF#Ea8cy3YXYe>0}a5 zhR0)wHxK?)_eT5C^La)0T|yCMZ6wuRH_B9v#Yn8~^fF220O)jwlH;k8E5&d-_mcoR$W2B-R~nMy-|o&Zsk1m@Bsp!D&?{`HEq#s3Nc1$>>u*COd^nD~QnW zSVwmv6FRX3o-!kjXWv#>QFu-)X!U$v@1%|H%l)xEgP9@o!CeRw3VR39WOs>Z2Ze{d zqE?%p&&x0$=fEQTU_|c9VD$rQr)M(E@DluMrc$Kk*97aaXo-EnT1OGwK@4Z=y7?!KQ2OsnyhEGQ?8UT z-6MGWVdSg#mqyVBP?rk?s_p&@Uxwz!grLQMmFl?p9q6S!!lAT*4z;e7Lo-np%GtYe zQFrZH2$YNJs^;LjOb0c~3R5?(0x%WZiDz}@`c_PI6?atv%M`OmLf&sL2_ z`}>+bPiMe~Z_(r_z(&~jN0=@37U zwMo%B>w)P^mj^Zk<68V`O>5++uX)b2ekeNs0W{P)IL2MnlPWTQHLeN(N=RFZX6bC0 z*0H2MSOR5#n5KwEcN|q3NeCQ+p_#0W+p#k70w~xI={fxp`;dqY3(g;=lCX4-NH`7z zLfFh@W>0pCzMxY1R3gy)Eb@!wY!(B_H8W)2+nLGgCPoeepJeVjWIsX~0n)zspQ@9$ zniOp^(#<1kkYxKR3^#v=##+;u@d1Fqk*P0g!1{HB=+U!aXQR13r9`j_i0uTOEm`S& zClpldv*w_+>}s@hHlJY+&e{}wz_GIKrjWEJcYrm`G+^>h+V2sal*WL z5n0g(cXh1XlEP6e#;>3==5TfN+EP9|n{I%6m-ZsLdJo^@^&G+>A|ajYp;)Pf+a|zw z1vou;hwRs2psZaAX*{kqqtF;{m&3lXM5A47Mf!@3UVxmFB(0qf$MB*5v)OkoJ-;_w zaH$1=LTSN}gBmPJn)RkVo^QZRGZ|4+YE90WR?wt4^Q0sWkvf@-iK-v9+NxIJxd|^Y ze%gsSBEQu-|8%AWKj186D;i^!6~`XU>AMX~4v$yT zV$Xy#V_Pq8Ap7=C3yrT^)_q_5Gao@faZm0@;`{WVYcX_ljqkh;WxaEDHrb9cK7J+l zU~~>q4cY-&AezJDMSmm6Q_@$i_)_EfSb#VVOS62@0HdTL=jioUsWJ!L+s`{lVd+TQ zmuWE3i+QvMKvoOxdd~BtrFuY!X*JnMa@}C&D8T?BgEX5;z-SF?1t-brE=4y`7N)4h zD{WILpuA$&r%Q`bww=SmWqN!=NffL30cO0}TVgY`e;+3fmxUSwMNUeo#;_0l$=$2Dk(x0ciZ=f?y29nG+Ax!E=<&Q+%B3#I}fq`jz>lN7#qj)@BX zNiEu-p~Su9(}C;b7$vLgWm%L{G{P}$ymbFtrzIzphvq}@VG8zji=i@nsom*8KM(Rsg4UF$ zI(Nd}=CCihTS1eIg^qpDPl*f%ovwun(7GAKiRoVEk34OmFa2 zrejclYL0rg*Zp1$%IPF(f__6JvZ-Vr;nqdz+J% zfdf?ujSv+~YCI$c*$axJzw*13+y6F8@IE&h%^xaoH?u+c{7dqn3PZ`+4lm*tWhmYp zD*F{i(r7?>9TTYm5(UQeOj?C&ko#}wh=+`9{R6@`S3+eMT@GpvJ&dNydBhEC3tYbASKteLmaQQbucl$SaoFu85H=8 zTgaV4-aaY4Q`?hha!~JS3_L-;MfX{|IVi56enp1f{=WHAASA^E5PKLxxyR?ALb-7} zp#FCL7u+~z7L8m`>9?~4Fvh~t3KqHX$782o?Pi5B^n_ps@H&<*Xu35D)^sTP`ItA# zSfFj0%wUW-k#_#ng9e_REM=ZEnT5GR28I*}Z0B}&^&moX?QfCz4)~)m{&Xf_!)fDV z8Y29T6zBv(u0J)E&B<9J4gD)%hK>*_t<>zP2wb|7h0Q-eD&;E%0(U8O(-=)?u*VJ& zB~r~7dgckO;|}b=;OB5b9Mi-JGMN~<2t)fA9;rhioNcsOANS1QbB9TScUUPq>7>SO zaH-2!o{`&?WNjAqa({3Vj~tCKXb0xpEJ!cq4W@+bhNS4Y@Pv;{-_F zG}pjInT*Boq`TJ~++h*K9w0)b9HC{Hz$(M-nT)}UGGgS5+05fWL<%LDJ)>zMPTEdb z3G9Zb3pb|Zx4Cu#y#tzU%!Vq)6V48FNZD`LzbL!D-@whM??;;X$o|Ia$r^B)EmdO3 zjtscwJwPVbM;xhg^FOZ%$GUVW9L2L-{+{p35nATD`Bu#iuh4%%Gsxi#Jwq$^GHl)1%Efhu4|m;NJXPz`KrMv;Fn%{h|}(XhS#$Xk+{@ z#@grTFU##Ox9#tfnU#x+1@2#qZ2v=nQ+BrlGRhm60vTn1#^wgXwr(U^OrK&FR#p;D z4js5pnFG+qiG=kpM-Glr5$I^^>|g|R{AAC`JJ=d20iCoMKlybejLJYar%!b;H>Yn( zPM-`q5@weFG7-1^G-v)V`%gk0qsZs{)1xEFUm~6&+~?B2qWY`yU&OpmZ=Ye2{1cZW z38S>Ru_K8V+~24b;dDqoJvje$`5>|84p&OA!Mn11nq8f4lfs^fEvv1LIE#2{Y&a zu{X#6cJM#PfMb-Cmmy*1=l@rb|D5Vy^g%@qxW9Ia|8sg~1pX$Bakj8s)w zh4;h#g2fybCuY0p&ZgsZMf$d6a?q=k#c(K{{ZQkvnS49!$u;i2_SnJO(6`^U$c3$* zvYUCoX*}ulNq6IJzdv1k7%8;c8otr;-8}&hY!?zpNRGd-%yB!tNs1%`AuJAuo7n9v zgnI2`;%ofEHPt{~O$ptTTTKdmqF^MA#8y&G25qrQFyvn`jWg^&W$r}^4Q9z32i|>h zzbB|>`6 zhoQ!{S)!BIl>Y=Z|IJ?PCVB-bg07_!5WW*}Zt2{Qxvus>9zmCGTi5bezufdw1ra*> zukar?4Lm-&MY@d>0rs|xp-X9q_o5idNre;l9d|Mv55olpJGqE&J>bo$!|lpUObe*^vxK>tT!{J(L^%*phhIE`gS>Lo!C zbCvBz^b4Yv?)$-jtEpnmDA*;604h2(waOtlo7dZTTh;kQx$BgcDU-TZpe84=q+U#B z81XMIY4^9^apow zJ(5o-G;s|)u`scWfi~3tjf8*d_5Xx|w7Qsty5|201$tpyE93tg56pji(*FX)KfU6g z{`3!a{vUu~`(Jnfe7gCIi_hHs|K{m0`u-K}eR9vIf?`|9>9XLMU!DnfF0t?7O!u(Hv{}1T> z$FJ1?4L!Jj^+;wC762O;;NKmSnUj+l@E=$#YlC&u>ThP^Wj#DHONk+uUhs)=8T~=; z-bjE>Jm>=8>5CheMRmcFShBG)rST6+tqFpV(#|cTN3zb-PR8%ouv3r7^G_C*@{p#P z5}w&e6xK>fELv;OKlL>w5B>STzj@<3d2{U6;Ce7NmUc7II_??8Bs>%rf*RU_%_ylt zcHinnBRaaMU2BmmugepIvQc9)O8Hbd{9psoRf($D>G9OnV(IgkitI7vg-n`fu|BWG z$XNGKjGE5l`v&~I(D8)1xrn04LF{*T3abCsXU8L42yaaze@F0(4n%`Jh>*_S@8|hpO(KpnuqyY!{ie$j!*{ko{9gw}{4KYWX>P#}Pr(7PsDQh`v!^CB*9!i`T6;YIzz*D9@1RL2F;? zRPJK#QCjfMpBWhX(Vx(tHVcR^ade>gyb;%-Scyc?fBvvk`Xj~?kq0-HJ8re_UW|(xc-tLhpQ!KLn`bWrn%;@$r zVzd9*Q00(7_6OmZSRnaa7=tj~G*lg=H>NlA#>war^)vi?rdi&8#w%UvsdDj+Yxj!y zG(qZ%p;5rhmai7UZ}l@8Kfq%?G}{!pVlpR;%}9?i&cc*>>+ulSM5Gbjj_x_Ph0n7u0dVl z8$&;YolkFvj|F6Xvp1vX{D3$5gn$AneN6stq*M_Cy>O15kASq;xWLSC^kx*b!FmVj zX5DkZInnvxn%ooVyW~6HJlH(#e8P#!><8ej*30zWL>)c~B2^86LV?}_X@(Kga;e@sFOGDD(u=f zDviH87~3islqS_G7uv4+_mrQMFXo_U%~tr{l8!x#_0r$HecXNM#smUrz`RMP~G~MD2j70NRyz(QRu6tL@&| z{psp`w0xxWVtwa+r))hBHTA^!;NeGDWPQ|V^6{73LcSutEQW|nweuTCMI)p6;ZJ2v zb~9w?z^r{b1l0sJgt81~d-Spg!X>d02DnC-W!PCyjING7u&6 zv0Kf^d5*p`+cIqmDm{AAvRxf^4&bF(Zn$Nsg2@hQj;;n z^>g0N-=z8oA@WPqM7HAz@(OnocT@fa)TDYCpHMwaeuiZJB(b3ru&Z~H`{1UZk}JsV zsE4mreU7exb^mwOwXoi^$!)ltdy9V<{f-Nfzv9#QUZ5Js@IpRJE+rZKBv9Nb>_?M$ z5DnmOA_YH(-^b$!Jj(RG-^X~3PC-!%F+*a7#D7|^s1pe8@Z)yRqN1A2dE`guyRRqv>72RwU(OQMTW z8fD4rs0sGQ@8Ie|x1wtFEa2d6C{6B1+w#MBEztc%KtBl@$5)~h)&sRHg}Lnje&HZN zpoM)f>feF>{{*OhC;l_~DwbhqO`tNK*4)8&0A=b2`g=QEYf%1u zIm?zREwe9Zp4F6TY^blRt*Nd`rz$IwiSl?^EE)-if&p3b`@9~v%US9uu~{vm*<>{6 zby|&DQ1KkWDA^I3+b`#${W(4wnm@mSmP4xmu<9H@e@+I_-1FPyZ%Nrpd2*3?;hjV?q zoWuQcM{e$x^}9Rz+o8MtdR<$n?J`|O67ARNA*+X6t~4~TAD7O?EJI2=n)VZGstm~j#X=O-52$Aw0i=9-il-nx2*}S&Y{qpoH@aopf)y4PH4-i*)Z}I z^l8xT^8Vz+?mNasw7NfGSQ}cq>XM!ux2l(pVM)Na?YYwHPC36ULg&`Do}K5`dbr&k z&MRbE+`W6JoZEX*&$+b$8u#`>A2|}4+rN7*415PHVdoMV#v-@$_T=y_FrZAIgFaK? z=`IU(P)Prka!wnX6I#Ff%6@n;?%g?b@z%hY+pQeQpG58sdH2$uP$1Xh3H7dO_wFx2 zyD#2)z@^Bp^J^-S`$bD(S@)aF#f-stZsxMHbu7o4(_H7pXV(?e8HE-AI^^UvGR&kW z1kcn+qsto6?lq0j41T>B?#iu&_i;r|+t$BZYy!AQ@6V|sVo2WoHw5oLH2J6VA*+fI zAtL?_(JaN1vxo-uGx=O1kt;8!2%>I-Hwtr{&B}EZ$t~j~7a9;{NMQL;H>~Zd-lh~R zejq?!@9pCXS`CHV@I^g^vW!-H#*mUq^yWxEt(lmqu`i=F!!tE!Z|@HQ9v?(hzp&>t z(X)SM(P8UY-;~3S|3mdlshS8dFQhQ3;?+lJ#hBY${Q~zJ@)U^E5&%w&IXtZ9gRwbtikbSxLgqkuMfHhR z9rpqH1Onkc;7$N*WOp4XtE@iVY6Jl0Za`*`R3x;QdmrT>5TtPL9SBFOM_=ULfcl?s zuYpuRZ+mUbXsLz{f5bflx`qVO^r2$Sp#vsMHEP|+eH|Wf0^;KkPC_^hfkzv-@1q?M z_CVMNfk$SDB?u`9iz$SAl6w;7`6%3PhByUb1B5*gc(jyz0^nECn0uVN64<-M-2v*g z9n#ymyIJ}@NZpWr2jHLtagRWmrlZC3Ly*$?2aE6rpzMJ3{$hF`z&()O%hYy>`)0Ac zh1<;T+f+>VavR6|lGy5pdKp3*0tdN!Aa@Te-91o35MvNeuVy3dhqM~fYYHhW*Y>eM zh`poj2TEPldtphp!_seumD>(0w;l0NwQZ)&wnCcKEow-H_# zf>?wgL*QWDH^RD8cn;zT2*(-xW{5{1?4>2{tFVr7nAI-s%CRyDkZj$7j8fh597y9B zy1o2>%U8YU%aT?{5fM^TF*Vb+m$BN*4rmP&df5TDuaH8kt6ELm8gxAbf=VC`L#Tz& z4uR*^jD=Iuv)qN~8VyoR(hjnN+rjTp@#%JKeUYn1-5R=0tXu_Zh6Zt|uNgP=YX`K$ zT29o;+O$^Dc577|K(X7yaT1r}TDZkrAEz46PmHOXY9LYO2~9O4`n~#`enNj-ugVD% z!g1lGa9U8wLRwISZlPZo5Qc>jVXvSa5k}OcUq7H9)^nm>)~EG~zFV)7)Oc^}E!=9l zZ6OvR3_uuxz{48%0bJ%Th0q6YqYswiQh+0f5tKv-$02_bQWccU(9R6)%m6h5)C^Dr zF|Fx_&<|mtSSy^ZnYo9yKTT^O#Guv$P!p`}Nr+F=9E62XGD67+B_p&tPQHK{iV({X zx*>23b`k<$4a75b>0*691cB9`W^HEb6nZcDLRl4?h~r!w?~UV;I98flTC0^HL{_V{ zFVq+5E9-ld-w@gm*-*COQGRh~ab$7X;z#+GP)nqxtmRQY6-q@?WvNGbDI`UtGU-u% zPuIS#7rWl<;`_QbbnWQk8sOC(7)zw9SsIMc^w5~gUEN@AokjM+U`XH#_j;ASkfmkZmQTQmUqd8hn&a3C(!Jisys#)B+ z5J+wxgi#0_^!*gnKgC)Xa{CxO2k}V;UtDazm%$~7XYS#Ez^!CtZY6BLm8btb~DmGhd)K_&=VeOj!VF&S%ULp&8?73 zk2TMOWNNJWVMzWt)_k}0JpKn}EDe7)7Ct4l+VL5@fT!iZ7Sq4r1)%Inp!zQWwNt{6 zp=KO`^m}8?H`4ash5H|b@^?^BL)$+BYAj7h@d5__cJaP%jU`vZs1J=Lx5B6oq9hyj zzOm#ffZjcp+y%*9W67%_*)tZQ6TEV)xm;?s;B_cWXxlX?Lg-AoiX+a4p09>8GLRq*ub@ zc=?z#ksQ}R#f!;tf)7diVUcstmf*wEzT`UT=@6@ZGz7Kqsz;kEq=!N)r3WHV9+PfN zK2N8Cu7M|B4%NNM*-}^YlhWMCIEHGa83t2yQd4M1$^f`=94|QVq*N6ir&CEoA5R{Z z%3;K4h)rc#!?UCgsqtndsotbstzNFaNIgqkqpncPYMh;rP1t-m}e6kPJXG)JzlF>;MYVW+;dc zux4GX8K7yfqkVrcSZMSzUO*cG`n=2^jw_D3SnBpIPi_BTYJ!Tv}CtA6s!>QJOGon91S<3w;T8wcYr+msdB z02UP65Dm0R{J;ENHs}AMF+Q;B*K5~Mqe6eE<1z^Sx!bp_cjksy%kuuUzb;xEqW!Da ztf%R!%W}UCUDlpk8)}#LuUhlJRIj1ct3vJj(VC8>J^R-vm$i?rQdV_@R<-vYcx*>o z=Xqo8Iy+X|j{l{z9kjDHI@V*I|4Vfzt$vJ-)k(+dq+>m%JjTZAym$`o?C#mGL34UR zD`n{cqSwKG==TJA=QzZH*=%#n3OH}@Jj)|sTYAvw4WT(XBLrGi(OS_;t6+E1Dibvu z6|0;#%nEp(#g7-OL;zbtb5H^~JFaLy`?qo9#!WPA-kgAV(`F}wZGuf3SkjrBOO@Q_ zTysZG>2L4F^mS}5{@QvJ@x|sho5_ae9nE{1N1OLGt2S@$1+ewS;G03x7u*os5!@3T z4ekpHwBnMU!%FjL@RK078L$a&f?r2F8*?+H@JEZAHgBXq1Ose@P#7+;InmbB8boVA z+QlH5S0EdN5QG{COCYGw46t23gIfaiVo)$?R3Q61SL97 zuKGZ_uDWp?(p8ri(j_YkX~%_yw7Ip~3F%l%jjq)Uf;UFbLi`g5??LzjgnvR%an)Qk z>wI$oYkN1MjR_3XM^M;Aqm7A8H~|?_1@gCugn-MGpybwsB z5rb}|w{52BmkogidqG3?QfV1<=0*DnevbSA#8fqTaSW;Waq@$M9MY+2<`70Mji7oF z>Ivd-9BJ`Y_)_Fdh@Ul2HD4&6X7SlnxiQiZ*vMVILrz9^PygWO)4%({TdTczE-u9` z-0mybP%H@X2GyOYfqQX3q1F~9;bsr&VzJDg-oj$cVt@c#a?m(Bp8x0|1=5U?pf^MV z^|49`2Q(BM;OSxo1cL$MN)uhwN{7`7@p%3-b|YlY9HfmP^XG%K1ISD(A_IxoBJojO zsXJ1}1_D^wbKGxH6xDIRRjl=j7J8^}Kv9>0P0cXQ{r0fdK-*~{yI&`umdV6qB9&@+ zKQSpzB_<{&EY{KtP8D$deFzh;ibvs(z8+NI5Q&6GIbzO88DbSg41afPzo{G1j~E^@ zKWKT#`k*wLIiS;JTp4$t*k|dJt`;{~Hb@T<{{l?vnEB#ML8lzQ<+BGD5~w^GJLVPROUnC3cSMnq4+mZwZJl8dRhCj z?!R=ZQmsRje3H~b=9mS&#cVUW4L-BqBneBnW&9FVuej8*)F!yhKA&G&Liqgxdx@!f zrzi@`WGr&JSRJg1_3~4h+)w`JV?usIen0VAoI^>j7G>* zW*RNlOscS66EurpL62pRWn11qMwhcHjbX`_iDpbJR+~+9Np8QZGVYJXf;yu0>jH6q zG#09lrCRI!_3bEy^fplp%TkGqi7c5#F^x$H#steqmTZ`h5wlJdow^2uO2_e^lrE>? z4ZU6`#G+A`%c;|+4Z{X<+JKK6P8!I7VZuNRsnXI>Cw97}49*0?l_;D_p-QnbS2cO=rfuay^T&B4qNo4fs42~qw_qL&;X+~OYku10X3IH zEs9K3!@jlkJ5(4m(;0_|h`Vt=9>II@aV+3*Tzd!&^GjD!ZZegH7lKlg5W{L_6Sjao zu!N|lJ}0I373kP$U{``_J@AhEloG|+@7(J=>Ew}9agr_QRzys#Hhcx>xmvsz1%X%5 zyaw{XbMW7h8DD`M073Xu1vEG_(GslEY7B%!&*J}v<_i?7$z)b6b!qbs^N4w`nK!#i zpCw^@3NZ$drIdg2lt_`Mr5O+cTQaDC5|pxJ@nF^#shN2Lvk4>Wbp21`g#lahCDWw8 z(Vtr_j~@Aa=TM`cL?XmjbshQjJ>{}rLd-AX>-<;vA0j;(Rop3(;$)4ioTy}i$t8xKOX+#g$LkyG-JFN-bN0FV+Tt;QN-t+CEm2y=cg_=eW z+CV(}@S8y7c3_O3MdMtjVyt_x%IooMkUW0>vpx!`bv;;84nl;%RAMlg5{7aUAJwu% z2;nJ>96wR92!TPhrI$ti@Q0;^;UIDPqdBw{#>viSq^JA|5lB95 zAdOby7b`!h%vW-i%Dg(I@=Yjks8X&>1C8V>pT~1i21EmA5uLM%K@ebu&P+~$I5RZ_ zqcu2e zgGil*qOQVFgY<@EA|{sCV6!P+9} z6W1j8Y%grAp#+fAERa*hpm&-xeA=9;fj~(k(8N$B2?Ygv36MfRv6l#;Alr9>I%KKw zlbUKqFr(pUw63XP-hC1!v8gWEeld45){^h3AHr;bG2J%_H| z{*8vF*!1`A?mGGO)ms}&-2t8Is_Deey_fE|Xm(BK_RFui=b{HsXjLu#6#m&=-{`-2 zW%cDr{|`6avGlH=*SVw=VzO>m{uH-_%b^nF_>VC}tJl!tsOX?q+PpdUNRvT|7oc7uLNhIV?Dhuni zbICmIVreN^s$ETn$S&J1*8{fiX}_l%*BsL3bUz}$CnpVObS0YOYK+v!)nt&SbnQl9 z(Q;}*y`3*XX@{Lo)n?6D`|LaHqxKVa-fs8&oX7A6kB^mPAo*y_Mw54x`PK|w<4Zi4 zz7h2s8b_HYV|L&TjvbCY4$g6=q-0ow)0zN&(@A~lSr z4`mEWMaBq0L#3XwjJl8rH1QNlopWOfGAOCXr!B?kt;>T5@Yfb%< zo5-v;Hf>sSZON|RVEpbMvHJBVntH;G%8kI=48U0bo7;zK$j^(ryR=TB8!yfDswx(n zG-d~=pnp&*X({9j$2ClE(lEVA!}KPNBTR2|Fpbk;pgNReMrne~2TW;#%%7DgZSFum zcC(Ll|MVvj6fM}Y=c`!7{4ogPQP8z$RF#I(41u{c<)8$F zE{(9Pisjh^stWy^!x}~`G)!gD2$WW69E5@#4CK&Iki$_|i+rr5j{$tF(#IZ@LRp4_ zAoJ-nv_(8#SNo-MQ=~S9nNlTZl4&>@rr}Vvu&GWduhZ1^14T*Kb=URR4b+X)sVaD^ zu>3HTa&bB2IFymcwtZz_nQ`md~tthoHHqvp{iJU zYuaDc?nR;M8urBDP{?dH=}H}8^@s-NG}sKAbyV}FhS!Xf7Zgvt#uqM^;@$E7_&}T= zj*rB1aSp}BIEk~(TH>gU_t!qQgNkbj*m-mdP8HG_?FKX(OD0nkQH$0ItJB2^e8k0- zda+7ys@z_j0G+46d@ex8=1kABDK$F2b6M(#QL%t6z5_n~425=Zy z4D>QG|P+SOXbUTSIgJQqi9rqLVZO2YPmkD zvBeCnR==&??u$8Ey?$T21h?t=q@Asrl*GxT#OWoZ4;bWeyiT#&`yIoMeGX1?j5vtn zkMVASvF*xQnm#kHPH3xa+flT)Et;GfqFM<30ZLQG(_$&j6x483NElALqX}LUi$pbX z86|j#%G41Vm#dO8DjM9Vy{!>`prfHdJT#c47#^YAAj-HwY28^IrV3n5bwjBtRA-@r zr5Yql0FWQG4KKX+{)#8@ zlaXhl&sXHbc0s-w-x0n!_F(u?{5|qmcwfbf74M`!smNCtt)K(DiM7m~c2_l3Hl;5Q zUy;(4Ylzp2?UKhF2%<=t2WcdaDG+i<9&aF|kYpqr4ia2K2o66*WTGyQKgvYHQaW9b zYq~)klnl>~f2f%+>=M4z_08&P36;vr|-U2lnNp*4&CJe@Kc{ zgil%L3PE7X z^o!nTSKZWeHOnJ*YAMEle&^Vp?>>p0{ku1Oae>XNefgEqo0`^;>j=ivTZW}%B+@8t z;;yE-mVe*8eLOmS{jEI)au0s|Ydc15RBxXPy30b4Z36gjV;}(+sjf`i=b6aa8xkB5 zvsT?77=V|_v=MlN0pEE3EvAh?<|l_K1@=`@5=xbkY|GIC0X=qr`7*!)Q2g(DRCXTt10@r_wgyd3d>VnYS<0@9g(( zaSnNRTko`uSVo+W0n+aa9l$T)AE|%j`dD+y`v>_9b_!&nb%phI>2`TIbULKA$oP4n zZ!!c4UKjF#XgpU*ivipp7!D8=5Cd|cJ3yan1Q0bBmH}{`2E5p?huSPfrcL#u0W<>2)dV_;v=l(l6C2$(xkj?oE!v+dvNHKwa9gJ zOMGj58-3i}K8&)1y&%v~ddDapaC8Jb9Z>Ti5}g?jVBuj~MkR_AMJL9@j7;r~BGad- zrLh+e;-aLCsH9v}sbkTz)UW|G=!KDYZu-_o7$4mEA63a&ev3X7n!Wadiyqmv`oe}< ze9567V&TL)*tBO+G!?aPk^Bo+Kk~iL+bXvf&LjZ8Cb@f%OkOJB)?uv{xvfG86+tZ! zWC(=&?S4cJ{2+Cetybr8yTe)y!F-oRD{%C%@NH{HJoO<*0tIF9QGg?X<@!*Yb~{FA8`3tI#yjr zlV=QdNIRh=+Hw5iVKIn@gN4@3);cB+bo`-BrQl74PXRn#oy6GfdgODQm>n&}~(>@c=~TKUm4 zkZQyFj1!1A=-@i)-lDT*G_oz@fl#E~xS(AI>;?Oi%|?yzAV^T_JZSkigz2t{S@1q@ zEN=hB+oc+Puofq3OG4i1=i<{x9A%QFn!6_wl|$)iffyToCaqZ?iSQQx+%Nvjsp?ar zR>Sto!u)SkX4O?FjEOQw>!(J}6ql0jH9z40uKByx!{%qKdW|0ggV@epZ@<>@b#Axg z+uXhGr?}@ht${P~#5bSo7E^%*)|%a^jFBkrS~v&b1!e zV;nUS<2aX6Qq&rWFviv5Q~NAfvb0!;#jQkfRNE{&vDqm(iIcGc=Yq)E;%O;89KBG? zenx56nL&6>Q-f!+XFhD1oUtQ{uQAz8wtEDFI^vG%qmGE+(N-XX9U_fORe^P-#)=sU zuE1mqJQmwROmQdn5^GVf=Rte>yVwc*&v1|M~0}uj8f9&3D*5>4F_E zh!CHk-yowTzV-;JgspdXQ(dZZvvZSYlXqL$K;<{R>aEUa!q1ld%JVDldtt#96D!N2 znMfu!t1Ml)GIm95pmMlU|0=?6Z`|AI{iW+y9@XPz_$T3aOWzB>8+)hhk72>9gnVTh zljP9^f>`pX10l2Iu?IrPCnw8&Wi6q_ArcCy?d4@44iJr6V?}Ne04Jrh;*T0ZmM%GCf>TIgY=2AVAN%6Bqu^^X}{- zDpy4da#ge_SFxjSYOR~3$7h)tEz=v0mwKI%vS_@N8ic(N#a!hzIN}M_oO1@gVChyx z^ale{Xciyz%d=2GmJntZVU%ERdk}~l+o1UWBpOlC$x#%HD3ik9^F|ldGJ&YXLj|J0 z9RAOdU;VghsI~55-}?LJ-@LS@n_NG=`Fv5RbL_guj+^G`bdL=8+}CMi`wr9})qZ&Q zmH4J3C=KMPJXM>fWJ(URR9Wiq)|LrPLYJ`B%tu0zSaqm6))DH6JsMNTW0@G~PH)m* zZ+RzH%OwvEglv+>83?(YPLQujq|B%-2L=BxGxnPgXY4m0 z7VS6WHAMvStQayHrKL{Rq%}mt)ZCI1Dl;?rS)eyl=wT*EyV*?JTk6C#9!T#^=h7$B zd|HwjV#ozexNz_vv|4xA@CF;UG5oNZXr0YZ>uj#nneWU9+*x{*%8�_+a8Q`aZJ^ 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 Kg=+kn67@epR>?*H literal 0 HcmV?d00001 diff --git a/BookGPU/Chapters/chapter6/figures/Sync-SeqSequenceOverlap.pdf b/BookGPU/Chapters/chapter6/figures/Sync-SeqSequenceOverlap.pdf new file mode 100644 index 0000000000000000000000000000000000000000..55ee6f676a263e1f5cde720f739d33dad083d567 GIT binary patch literal 55734 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 literal 0 HcmV?d00001 diff --git a/BookGPU/Chapters/chapter6/figures/Sync-StreamSequenceOverlap.pdf b/BookGPU/Chapters/chapter6/figures/Sync-StreamSequenceOverlap.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f19ad316f2add5e334e7f17f1043cfc67aecb9e8 GIT binary patch literal 56256 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�-Zg|U{WHfFAy;W>M5<~dC)QSqrG6xJ@#4B!x4 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 dUBiwCAiOeJL$a^B)V1|A7Di literal 0 HcmV?d00001 diff --git a/BookGPU/Makefile b/BookGPU/Makefile index 43eb90f..56526d8 100644 --- a/BookGPU/Makefile +++ b/BookGPU/Makefile @@ -8,7 +8,8 @@ all: bibtex bu2 bibtex bu3 bibtex bu4 - bibtex bu6 + bibtex bu5 + bibtex bu7 makeindex ${BOOK}.idx pdflatex ${BOOK} pdflatex ${BOOK} -- 2.39.5