{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Before we begin, let's execute the cell below to display information about the CUDA driver and GPUs running on the server by running the `nvidia-smi` command. To do this, execute the cell block below by giving it focus (clicking on it with your mouse), and hitting Ctrl-Enter, or pressing the play button in the toolbar above. If all goes well, you should see some output returned below the grey cell." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!nvidia-smi" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Learning objectives\n", "The **goal** of this lab is to:\n", "\n", "- Learn how to run the same code on both a multicore CPU and a GPU using Fortran Standard Parallelism\n", "- Understand steps required to make a sequential code parallel using do-concurrent constructs\n", "\n", "We do not intend to cover:\n", "- Detailed optimization techniques and mapping of do-concurrent constructs to CUDA Fortran\n", "\n", "\n", "# Fortran Standard Parallelism\n", "\n", "ISO Standard Fortran 2008 introduced the DO CONCURRENT construct to allow you to express loop-level parallelism, one of the various mechanisms for expressing parallelism directly in the Fortran language. \n", "\n", "Fortran developers have been able to accelerate their programs using CUDA Fortran, OpenACC or OpenMP. Now with the support of DO CONCURRENT on GPU with NVIDIA HPC SDK, compiler automatically accelerates loops using DO CONCURRENT, allowing developers to get the benefit of acclerating on NVIDIA GPUs using ISO Standard Fortran without any extensions, directives, or non-standard libraries. You can now write standard Fortran, remaining fully portable to other compilers and systems, and still benefit from the full power of NVIDIA GPUs\n", "\n", "For our code to make *Pair Calculation* all that’s required is expressing loops with DO CONCURRENT. The example below will introduce you to the syntax of DO CONCURRENT \n", "\n", "Sample vector addition codeis shown in code below:\n", "\n", "```fortran\n", " subroutine vec_addition(x,y,n)\n", " real :: x(:), y(:)\n", " integer :: n, i \n", " do i = 1, n \n", " y(i) = x(i)+y(i)\n", " enddo \n", " end subroutine vec_addition\n", "```\n", "\n", "In order to make use of ISO Fortran DO CONCURRENT we need to replace the `do` loop with `do concurrent` as shown in code below\n", "\n", "```fortran\n", " subroutine vec_addition(x,y,n)\n", " real :: x(:), y(:)\n", " integer :: n, i \n", " do concurrent (i = 1: n) \n", " y(i) = x(i)+y(i)\n", " enddo \n", " end subroutine vec_addition\n", "```\n", "\n", "By changing the DO loop to DO CONCURRENT, you are telling the compiler that there are no data dependencies between the n loop iterations. This leaves the compiler free to generate instructions that the iterations can be executed in any order and simultaneously. The compiler parallelizes the loop even if there are data dependencies, resulting in race conditions and likely incorrect results. It’s your responsibility to ensure that the loop is safe to be parallelized.\n", "\n", "## Nested Loop Parallelism\n", "\n", "Nested loops are a common code pattern encountered in HPC applications. A simple example might look like the following:\n", "\n", "```fortran\n", " do i=2, n-1\n", " do j=2, m-1\n", " a(i,j) = w0 * b(i,j) \n", " enddo\n", " enddo\n", "```\n", "\n", "It is straightforward to write such patterns with a single DO CONCURRENT statement, as in the following example. It is easier to read, and the compiler has more information available for optimization.\n", "\n", "```fortran\n", " do concurrent(i=2 : n-1, j=2 : m-1)\n", " a(i,j) = w0 * b(i,j) \n", " enddo\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, lets start modifying the original code and add DO-CONCURRENT. Click on the [rdf.f90](../../source_code/doconcurrent/rdf.f90) link and modify `rdf.f90`. Remember to **SAVE** your code after changes, before running below cells." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Compile and Run for Multicore\n", "\n", "Now that we have added a DO-CONCURRENT code, let us try compile the code. We will be using NVIDIA HPC SDK for this exercise. The flags used for enabling DO-CONCURRENT are as follows:\n", "\n", "- `stdpar` : This flag tell the compiler to enable Parallel DO-CONCURRENT for a respective target\n", "- `stdpar=multicore` will allow us to compile our code for a multicore\n", "- `stdpar` will allow us to compile our code for a NVIDIA GPU (Default is NVIDIA)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Compile the code for muticore\n", "!cd ../../source_code/doconcurrent && nvfortran -stdpar=multicore -Minfo -o rdf nvtx.f90 rdf.f90 -I/opt/nvidia/hpc_sdk/Linux_x86_64/21.3/cuda/11.2/include -L/opt/nvidia/hpc_sdk/Linux_x86_64/21.3/cuda/11.2/lib64 -lnvToolsExt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you compile this code with **-Minfo**, you can see how the compiler performs the parallelization.\n", "```\n", "rdf:\n", " 80, Memory zero idiom, loop replaced by call to __c_mzero8\n", " 92, Generating Multicore code\n", " 92, Loop parallelized across CPU threads\n", "```\n", "\n", "Make sure to validate the output by running the executable and validate the output." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Run the multicore code\n", "!cd ../../source_code/doconcurrent && ./rdf && cat Pair_entropy.dat" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The output entropy value should be the following:\n", "\n", "```\n", "s2 : -2.452690945278331 \n", "s2bond : -24.37502820694527 \n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#profile and see output of nsys\n", "!cd ../../source_code/doconcurrent && nsys profile -t nvtx --stats=true --force-overwrite true -o rdf_doconcurrent_multicore ./rdf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's checkout the profiler's report. Download and save the report file by holding down Shift and Right-Clicking [Here](../../source_code/doconcurrent/rdf_doconcurrent_multicore.qdrep) and open it via the GUI. Have a look at the example expected profiler report below:\n", "\n", "\n", "\n", "\n", "### Compile and run for Nvidia GPU\n", "\n", "Without changing the code now let us try to recompile the code for NVIDIA GPU and rerun.\n", "GPU acceleration of DO-CONCURRENT is enabled with the `-⁠stdpar` command-line option to NVC++. If `-⁠stdpar `is specified, almost all loops with DO-CONCURRENT are compiled for offloading to run in parallel on an NVIDIA GPU.\n", "\n", " **Understand and analyze** the solution present at:\n", "\n", "[RDF Code](../../source_code/doconcurrent/SOLUTION/rdf.f90)\n", "\n", "Open the downloaded files for inspection. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#compile for Tesla GPU\n", "!cd ../../source_code/doconcurrent && nvfortran -stdpar=gpu -Minfo -acc -o rdf nvtx.f90 rdf.f90 -L/opt/nvidia/hpc_sdk/Linux_x86_64/21.3/cuda/11.2/lib64 -lnvToolsExt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If you compile this code with -Minfo, you can see how the compiler performs the parallelization.\n", "```rdf:\n", " 80, Memory zero idiom, loop replaced by call to __c_mzero8\n", " 92, Generating Tesla code\n", " 92, Loop parallelized across CUDA thread blocks, CUDA threads(4) blockidx%y threadidx%y\n", " Loop parallelized across CUDA thread blocks, CUDA threads(32) ! blockidx%x threadidx%x\n", "```\n", "\n", "Make sure to validate the output by running the executable and validate the output." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#Run on NVIDIA GPU\n", "!cd ../../source_code/doconcurrent && ./rdf && cat Pair_entropy.dat" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The output entropy value should be the following:\n", "\n", "```\n", "s2 : -2.452690945278331 \n", "s2bond : -24.37502820694527 \n", "```" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "#profile and see output of nvptx\n", "!cd ../../source_code/doconcurrent && nsys profile -t nvtx,cuda --stats=true --force-overwrite true -o rdf_doconcurrent_gpu ./rdf" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's checkout the profiler's report. Download and save the report file by holding down Shift and Right-Clicking [Here](../../source_code/doconcurrent/rdf_doconcurrent_gpu.qdrep) and open it via the GUI. Have a look at the example expected profiler report below:\n", "\n", "\n", "\n", "If you inspect the output of the profiler closer, you can see the usage of *Unified Memory* annotated with green rectangle which was explained in previous sections.\n", "\n", "Moreover, if you compare the NVTX marker `Pair_Calculation` (from the NVTX row) in both multicore and GPU version, you can see how much improvement you achieved. In the *example screenshot*, we were able to reduce that range from 1.57 seconds to 26 mseconds.\n", "\n", "Feel free to checkout the [solution](../../source_code/doconcurrent/SOLUTION/rdf.f90) to help you understand better or compare your implementation with the sample solution." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# ISO Standard Fortran Analysis\n", "\n", "**Usage Scenarios**\n", "- DO-CONCURRENT is part of the standard language and provides a good start for accelerating code on accelerators like GPU and multicores.\n", "\n", "**Limitations/Constraints**\n", "It is key to understand that it is not an alternative to CUDA. *DO-CONCURRENT* provides highest portability and can be seen as the first step to porting on GPU. The general abstraction limits the optimization functionalities. For example, DO-CONCURRENT implementation is currently dependent on Unified memory. Moreover, one does not have control over thread management and that will limit the performance improvement.\n", "\n", "\n", "**Which Compilers Support DO-CONCURRENT on GPUs and Multicore?**\n", "1. NVIDIA GPU: As of Jan 2021 the compiler that support DO-CONCURRENT on NVIDIA GPU is from NVIDIA. \n", "2. x86 Multicore: Other compilers like intel compiler has an implementation on a multicore CPU." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Post-Lab Summary\n", "\n", "If you would like to download this lab for later viewing, it is recommend you go to your browsers File menu (not the Jupyter notebook file menu) and save the complete web page. This will ensure the images are copied down as well. You can also execute the following cell block to create a zip-file of the files you've been working on, and download it with the link below." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "%%bash\n", "cd ..\n", "rm -f nways_files.zip\n", "zip -r nways_files.zip *" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**After** executing the above zip command, you should be able to download and save the zip file by holding down Shift and Right-Clicking [Here](../nways_files.zip).\n", "\n", "Let us now go back to parallelizing our code using other approaches.\n", "\n", "**IMPORTANT**: Please click on **HOME** to go back to the main notebook for *N ways of GPU programming for MD* code.\n", "\n", "-----\n", "\n", "#

HOME

\n", "\n", "-----\n", "\n", "\n", "# Links and Resources\n", "[Do-Concurrent Guide](https://developer.nvidia.com/blog/accelerating-fortran-do-concurrent-with-gpus-and-the-nvidia-hpc-sdk/)\n", "\n", "[NVIDIA Nsight System](https://docs.nvidia.com/nsight-systems/)\n", "\n", "\n", "**NOTE**: To be able to see the Nsight System profiler output, please download Nsight System latest version from [here](https://developer.nvidia.com/nsight-systems).\n", "\n", "Don't forget to check out additional [OpenACC Resources](https://www.openacc.org/resources) and join our [OpenACC Slack Channel](https://www.openacc.org/community#slack) to share your experience and get more help from the community.\n", "\n", "--- \n", "\n", "## Licensing \n", "\n", "This material is released by OpenACC-Standard.org, in collaboration with NVIDIA Corporation, under the Creative Commons Attribution 4.0 International (CC BY 4.0)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.6.9" } }, "nbformat": 4, "nbformat_minor": 4 }