Skip to content

Commit

Permalink
Feature: Passing arguments to NVCC compiler (#26)
Browse files Browse the repository at this point in the history
* Add option to give nvcc extra arguments

* Add test for nvcc options that changes c++ dialect from c++17 to c++14

* Add make and the english language pack to devcontainer to be able to build the documentation

* Update documentation config to automatically import the current version of the package

* Document new --compiler-args argument

* Improve tests coverage by testing for bad arguments and the error output during a failed compilation

* Add IPython to docs requirements to allow the __version__ import for readthedocs env

* Change devcontainer base image to have the latest CUDA toolkit

* Mock the nsight compute tool with a bash script

* Add test to compile with opencv

* Add new page to documentation that contains a new notebook that explains compiling with external libraries

* Add autodocstring vscode extension to devcontainer

* Add function that modifies the default profiler/compiler arguments to allow reusing them in multiple magic command calls

* Update pylint exceptions

* Update contributing instructions

* Change version from 1.0.3 to 1.1.0 due to adding features in a backward-compatible manner

* Install latest CUDA toolkit on the test runner to pass the OpenCV compilation test

* Install opencv in test runner and update code coverage install

* Add CUDA bin to PATH in test and coverage runners

* Add cuda bin to path variable in .bashrc

* Update way to set environment variable PATH in github action

* Change devcontainer base image back to ubuntu:22.04 to match the environment from the test runner
  • Loading branch information
cosminc98 authored Feb 12, 2024
1 parent 5cd2258 commit 781ff5b
Show file tree
Hide file tree
Showing 19 changed files with 424 additions and 51 deletions.
26 changes: 20 additions & 6 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,29 @@
FROM ubuntu
FROM ubuntu:22.04

ARG VENV_PATH=/opt/dev-venv
ENV VENV_ACTIVATE=${VENV_PATH}/bin/activate
ENV DEBIAN_FRONTEND="noninteractive"

# install the latest CUDA toolkit (https://developer.nvidia.com/cuda-downloads)
RUN apt update
RUN apt install -y python3.10-venv nvidia-cuda-toolkit gcc vim git
RUN apt install -y wget
RUN wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
RUN dpkg -i cuda-keyring_1.1-1_all.deb
RUN apt update
RUN apt -y install cuda-toolkit-12-3
RUN echo "PATH=\"\$PATH:/usr/local/cuda/bin\"" >> ~/.bashrc

# install OpenCV to test compilation with external libraries
RUN apt install -y libopencv-dev pkg-config

# the mkdir command bypasses a profiler error, which allows us to run it with
# host code only to at least check that the profiler parameters are correctly
# provided; without this line, some tests will fail
RUN mkdir -p /usr/lib/x86_64-linux-gnu/nsight-compute/sections
# make & language-pack-en are for documentation
RUN apt install -y \
gcc \
git \
language-pack-en \
make \
python3.10-venv \
vim

# we create the virtualenv here so that the devcontainer.json setting
# python.defaultInterpreterPath can be used to find it; if we do it in the
Expand Down
6 changes: 4 additions & 2 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,12 @@
"ms-python.isort",
"ms-python.flake8",
"ms-python.black-formatter",
"ryanluker.vscode-coverage-gutters"
"ryanluker.vscode-coverage-gutters",
"njpwerner.autodocstring"
],
"settings": {
"python.defaultInterpreterPath": "/opt/dev-venv/bin/python"
"python.defaultInterpreterPath": "/opt/dev-venv/bin/python",
"autoDocstring.docstringFormat": "google-notypes"
}
}
}
Expand Down
31 changes: 22 additions & 9 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,19 @@ jobs:
with:
python-version: ${{ matrix.python-version }}

# the mkdir command bypasses a profiler error, which allows us to run it
# with host code only to at least check that the profiler parameters are
# correctly provided
- name: Install CUDA tools
- name: Install CUDA toolkit
run: |
sudo apt update
sudo apt install nvidia-cuda-toolkit
sudo mkdir -p /usr/lib/x86_64-linux-gnu/nsight-compute/sections
sudo apt install -y wget
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update
sudo apt -y install cuda-toolkit-12-3
echo "PATH=$PATH:/usr/local/cuda/bin" >> $GITHUB_ENV
- name: Install OpenCV
run: |
sudo apt install -y libopencv-dev pkg-config
- name: Install Python dependencies
run: |
Expand Down Expand Up @@ -65,11 +70,19 @@ jobs:
with:
python-version: "3.10"

- name: Install CUDA tools
- name: Install CUDA toolkit
run: |
sudo apt update
sudo apt install nvidia-cuda-toolkit
sudo mkdir -p /usr/lib/x86_64-linux-gnu/nsight-compute/sections
sudo apt install -y wget
wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64/cuda-keyring_1.1-1_all.deb
sudo dpkg -i cuda-keyring_1.1-1_all.deb
sudo apt update
sudo apt -y install cuda-toolkit-12-3
echo "PATH=$PATH:/usr/local/cuda/bin" >> $GITHUB_ENV
- name: Install OpenCV
run: |
sudo apt install -y libopencv-dev pkg-config
- name: Install Python dependencies
run: |
Expand Down
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Here are just a few of the things that nvcc4jupyter does well:

- [Easily run CUDA C++ code](https://nvcc4jupyter.readthedocs.io/en/latest/usage.html#hello-world)
- [Profile your code with NVIDIA Nsight Compute](https://nvcc4jupyter.readthedocs.io/en/latest/usage.html#profiling)
- [Compile your code with external libraries (e.g. OpenCV)](https://nvcc4jupyter.readthedocs.io/en/latest/notebooks.html#compiling-with-external-libraries)
- [Share code between different programs in the same notebook / split your code into multiple files for improved readability](https://nvcc4jupyter.readthedocs.io/en/latest/usage.html#groups)

## Install
Expand Down Expand Up @@ -88,13 +89,14 @@ The official documentation is hosted on [readthedocs](https://nvcc4jupyter.readt

## Contributing

Install the package with the development dependencies:
```bash
pip install .[dev]
```
The recommended setup for development is using the devcontainer in GitHub
Codespaces or locally in VSCode.

As a developer, make sure you install the pre-commit hook before commiting any changes:
If not using the devcontainer you need to install the package with the
development dependencies and install the pre-commit hook before commiting any
changes:
```bash
pip install .[dev]
pre-commit install
```

Expand Down
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
sphinx==7.1.2
sphinx-rtd-theme==1.3.0rc1
IPython>=8.19.0
11 changes: 9 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,18 @@
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information

import os
import sys

sys.path.append(os.path.join("..", ".."))
from nvcc4jupyter.__init__ import __version__ # noqa: E402

project = "nvcc4jupyter"
copyright = "2024, Andrei Nechaev & Cosmin Stefan Ciocan"
author = "Andrei Nechaev & Cosmin Stefan Ciocan"
release = "1.0.1"
version = "1.0.1"
release = __version__
version = __version__


# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ which provides CUDA capable GPUs with the CUDA toolkit already installed.
:caption: Contents:

usage
notebooks
magics
25 changes: 21 additions & 4 deletions docs/source/magics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,40 @@ Usage
- ``%%cuda``: Compile and run this cell.
- ``%%cuda -p``: Also runs the Nsight Compute profiler.
- ``%%cuda -p -a "<SPACE SEPARATED PROFILER ARGS>"``: Also runs the Nsight Compute profiler.
- ``%%cude -c "<SPACE SEPARATED COMPILER ARGS"``: Passes additional arguments to "nvcc".
- ``%%cuda -t``: Outputs the "timeit" built-in magic results.

Options
-------

.. _timeit:

-t, --timeit
Boolean. If set, returns the output of the "timeit" built-in
ipython magic instead of stdout.

.. _profile:

-p, --profile
Boolean. If set, runs the NVIDIA Nsight Compute profiler whose
output is appended to standard output.

.. _profiler_args:

-a, --profiler-args
String. Optional profiler arguments that can be space separated
by wrapping them in double quotes. See all options here:
`Nsight Compute CLI <https://docs.nvidia.com/nsight-compute/NsightComputeCli/index.html#command-line-options>`_

.. _compiler_args:

-c, --compiler-args
String. Optional compiler arguments that can be space separated
by wrapping them in double quotes. They will be passed to "nvcc".
See all options here:
`NVCC Options <https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html#nvcc-command-options>`_


.. note::
If both "\-\-profile" and "\-\-timeit" are used then no profiling is
done.
Expand All @@ -47,10 +63,11 @@ Examples
--------
::

# compile, run, and profile the code in the cell with the Nsight
# compute profiler while collecting only metrics from the
# "MemoryWorkloadAnalysis" section.
%%cuda --profile --profiler-args "--section MemoryWorkloadAnalysis"
# compile, run, and profile the code in the cell with the Nsight compute
# profiler while collecting only metrics from the "MemoryWorkloadAnalysis"
# section; also provides the "--optimize 3" option to "nvcc" during
# compilation to optimize host code
%%cuda -p -a "--section MemoryWorkloadAnalysis" -c "--optimize 3"

------

Expand Down
34 changes: 34 additions & 0 deletions docs/source/notebooks.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
*********
Notebooks
*********

This page provides a list of useful Jupyter notebooks written with the
**nvcc4jupyter** library.

.. note::
These notebooks are written for Google's Colab, but you may run them in
other environments by installing all expected dependencies. If running in
Colab, make sure to set the runtime type to a GPU instance (at the time of
writing this, T4 is the GPU offered for free by Colab).

------

.. _compiling_with_external_libraries:

Compiling with external libraries
=================================

[`NOTEBOOK <https://colab.research.google.com/drive/1iuY46DCwv4hy3SqDhJgFeO8kgpHnzjTh?usp=sharing>`_]

If you need to compile CUDA C++ code that uses external libraries in the host
code (e.g. OpenCV for reading and writing images to disk) then this section is
for you.

To achieve this, use the :ref:`compiler-args <compiler_args>` option of the
:ref:`cuda <cuda_magic>` magic command to pass the correct compiler options
of the OpenCV library to **nvcc** for it to link the OpenCV code with the
code in your Jupyter cell. Those compiler options can be provided by the
`pkg-config <https://www.freedesktop.org/wiki/Software/pkg-config/>`_ tool.

In the notebook we show how to use OpenCV to load an image, blur it with a CUDA
kernel, and then save it back to disk using OpenCV again.
44 changes: 44 additions & 0 deletions docs/source/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -255,3 +255,47 @@ Running the cell above will compile and execute the vector addition code in the
SM Active Cycles cycle 383.65
Compute (SM) Throughput % 1.19
----------------------- ------------- ------------
Compiler arguments
------------------

In the same way profiler arguments can be passed to the profiling tool,
compiling arguments can be passed to **nvcc**:

.. code-block:: c++

%cuda_group_run --group "vector_add" --compiler-args "--optimize 3"

Running the cell above will compile and execute the vector addition code in the
"vector_add" group. During compilation, **nvcc** receives the "\-\-optimize"
option which specifies the optimization level for host code.

Set default arguments
---------------------

In the case where you execute multiple magic commands with the same compiler or
profiler arguments you can avoid writing them every time by setting the default
arguments:

.. code-block:: python
from nvcc4jupyter import set_defaults
set_defaults(compiler_args="--optimize 3", profiler_args="--section SpeedOfLight")
The same effect can be achieved by running "set_defaults" once for each config
due to the fact that the default value is not changed if an a value is not
given to the "set_defaults" function.

.. code-block:: python
from nvcc4jupyter import set_defaults
set_defaults(compiler_args="--optimize 3")
set_defaults(profiler_args="--section SpeedOfLight")
Now we can run the following cell without specifying the compiler and profiler
arguments once again.

.. code-block:: c++

%cuda_group_run --group "vector_add" --profile
3 changes: 2 additions & 1 deletion nvcc4jupyter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
nvcc4jupyter: CUDA C++ plugin for Jupyter Notebook
"""

from .parsers import set_defaults # noqa: F401
from .plugin import NVCCPlugin, load_ipython_extension # noqa: F401

__version__ = "1.0.3"
__version__ = "1.1.0"
50 changes: 49 additions & 1 deletion nvcc4jupyter/parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,39 @@
"""

import argparse
from typing import Callable, Optional

_default_profiler_args: str = ""
_default_compiler_args: str = ""


def set_defaults(
compiler_args: Optional[str] = None, profiler_args: Optional[str] = None
) -> None:
"""
Set the default values for various arguments of the magic commands. These
values will be used if the user does not explicitly provide those arguments
to override this behaviour on a cell by cell basis.
Args:
compiler_args: If not None, this value becomes the new default compiler
config. Defaults to "".
profiler_args: If not None, this value becomes the new default profiler
config. Defaults to "".
"""

# pylint: disable=global-statement
global _default_compiler_args
global _default_profiler_args
if compiler_args is not None:
_default_compiler_args = compiler_args
if profiler_args is not None:
_default_profiler_args = profiler_args


def str_to_lambda(arg: str) -> Callable[[], str]:
"""Convert argparse string to lambda"""
return lambda: arg


def get_parser_cuda() -> argparse.ArgumentParser:
Expand All @@ -18,7 +51,22 @@ def get_parser_cuda() -> argparse.ArgumentParser:
)
parser.add_argument("-t", "--timeit", action="store_true")
parser.add_argument("-p", "--profile", action="store_true")
parser.add_argument("-a", "--profiler-args", type=str, default="")

# --profiler-args and --compiler-args values are lambda functions to allow
# changing the default value at runtime
parser.add_argument(
"-a",
"--profiler-args",
type=str_to_lambda,
default=lambda: _default_profiler_args,
)
parser.add_argument(
"-c",
"--compiler-args",
type=str_to_lambda,
default=lambda: _default_compiler_args,
)

return parser


Expand Down
Loading

0 comments on commit 781ff5b

Please sign in to comment.