Building Jetson Nano libraries on host PC

docker Docker

In this article, we built the PyTorch library on the Jetson Nano, and in this article, we built the OpenCV library on the Jetson Nano. All of these builds were performed on the Jetson Nano, but this article explains how to build the PyTorch and OpenCV libraries on the host PC running aarch64 binary, which is the architecture of the Jetson Nano, using the method described in this article.

docker

Docker is used for the build. By using Docker, you can setup a shared library environment required by aarch64 binaries. The following steps are required to add a login user to the docker group so that you can run docker commands without sudo.

sudo groupadd docker sudo gpasswd -a $USER docker
Code language: PHP (php)

Restart the system to reflect the addition to the docker group.

PyTorch

Below is a Dockerfile description.

FROM nvcr.io/nvidia/l4t-base:r32.7.1 ENV DEBIAN_FRONTEND=noninteractive ARG MAX_JOBS=2
Code language: Dockerfile (dockerfile)

ARG MAX_JOBS allows to pass it from outside during docker build as --build-arg MAX_JOBS=$(nproc), etc.

# https://qengineering.eu/install-pytorch-on-jetson-nano.html RUN apt-get update && apt-get install -y \ python3.8 python3.8-dev \ ninja-build git cmake clang \ libopenmpi-dev libomp-dev ccache \ libopenblas-dev libblas-dev libeigen3-dev \ python3-pip libjpeg-dev \ gnupg2 curl RUN apt-key adv --fetch-key http://repo.download.nvidia.com/jetson/jetson-ota-public.asc RUN echo 'deb https://repo.download.nvidia.com/jetson/common r32.7 main\n\ deb https://repo.download.nvidia.com/jetson/t210 r32.7 main' > /etc/apt/sources.list.d/nvidia-l4t-apt-source.list RUN apt-get update && apt-get install -y nvidia-cuda nvidia-cudnn8 RUN python3.8 -m pip install -U pip RUN python3.8 -m pip install -U setuptools RUN python3.8 -m pip install -U wheel mock pillow RUN python3.8 -m pip install scikit-build RUN python3.8 -m pip install cython Pillow
Code language: Dockerfile (dockerfile)

repo.download.nvidia.com is added to the apt repository, and then nvidia-cuda and nvidia-cudnn8 are installed.

The following is almost the same as described in this article.

## download PyTorch v1.11.0 with all its libraries RUN git clone -b v1.11.0 --depth 1 --recursive --recurse-submodules --shallow-submodules https://github.com/pytorch/pytorch.git WORKDIR pytorch RUN python3.8 -m pip install -r requirements.txt COPY pytorch-1.11-jetson.patch . RUN patch -p1 < pytorch-1.11-jetson.patch RUN apt-get install -y software-properties-common lsb-release RUN wget -O - https://apt.kitware.com/keys/kitware-archive-latest.asc 2>/dev/null | gpg --dearmor - | tee /etc/apt/trusted.gpg.d/kitware.gpg >/dev/null RUN apt-add-repository "deb https://apt.kitware.com/ubuntu/ $(lsb_release -cs) main" RUN apt-get update && apt-get install -y cmake ENV BUILD_CAFFE2_OPS=OFF ENV USE_FBGEMM=OFF ENV USE_FAKELOWP=OFF ENV BUILD_TEST=OFF ENV USE_MKLDNN=OFF ENV USE_NNPACK=OFF ENV USE_XNNPACK=OFF ENV USE_QNNPACK=OFF ENV USE_PYTORCH_QNNPACK=OFF ENV USE_CUDA=ON ENV USE_CUDNN=ON ENV TORCH_CUDA_ARCH_LIST="5.3;6.2;7.2" ENV USE_NCCL=OFF ENV USE_SYSTEM_NCCL=OFF ENV USE_OPENCV=OFF ENV MAX_JOBS=$MAX_JOBS # set path to ccache ENV PATH=/usr/lib/ccache:$PATH # set clang compiler ENV CC=clang ENV CXX=clang++ # create symlink to cublas # ln -s /usr/lib/aarch64-linux-gnu/libcublas.so /usr/local/cuda/lib64/libcublas.so # start the build RUN python3.8 setup.py bdist_wheel RUN find /pytorch/dist/ -type f|xargs python3.8 -m pip install # torch vision RUN git clone --depth=1 https://github.com/pytorch/vision torchvision -b v0.12.0 RUN cd torchvision && \ TORCH_CUDA_ARCH_LIST='5.3;6.2;7.2' \ FORCE_CUDA=1 \ python3.8 setup.py bdist_wheel
Code language: Dockerfile (dockerfile)

Build the Dockerfile above.

docker build -t pytorch-build . --build-arg MAX_JOBS=$(nproc)
Code language: Bash (bash)

When building on the Jetson Nano, MAX_JOBS=2 was specified due to limited memory, but on the host PC this restriction is relaxed.

After the build is complete, retrieve the built binaries.

$ id=$(docker run -it --rm -d pytorch-build bash) $ pytorch=$(docker exec -it ${id} find /pytorch/dist -type f | sed -e "s/[rn]+//g") $ vision=$(docker exec -it ${id} find /pytorch/torchvision/dist -type f | sed -e "s/[rn]+//g") $ docker cp ${id}:${pytorch} . $ docker cp ${id}:${vision} . $ docker stop ${id} $ ls Dockerfile *whl Dockerfile torch-1.11.0a0+gitbc2c6ed-cp38-cp38-linux_aarch64.whl torchvision-0.12.0a0+9b5a3fe-cp38-cp38-linux_aarch64.whl
Code language: Bash (bash)

Now we have two on hand: torch-1.11.0a0+gitbc2c6ed-cp38-cp38-linux_aarch64.whl and torchvision-0.12.0a0+9b5a3fe-cp38-cp38-linux_aarch64.whl.

Copy and install these two into the Jetson Nano. The followings are performed on the Jetson Nano.

sudo apt update sudo apt install -y python3.8 python3.8-dev python3-pip libopenmpi-dev libomp-dev libopenblas-dev libblas-dev libeigen3-dev nvidia-cuda nvidia-cudnn8 python3.8 -m pip install -U pip python3.8 -m pip install torch-*.whl torchvision-*.whl
Code language: Bash (bash)

OpenCV

Below is a Dockerfile description.

FROM nvcr.io/nvidia/l4t-base:r32.6.1 ENV DEBIAN_FRONTEND=noninteractive ARG VER="4.6.0" ARG PREFIX=/usr/local ARG MAX_JOBS
Code language: Dockerfile (dockerfile)

This one also allows some variables to be passed with –build-arg at docker build. The followings are almost the same as explained in this article.

# setup RUN cd tmp && mkdir build_opencv WORKDIR /tmp/build_opencv # install_dependencies RUN apt-get update && \ apt-get install -y \ build-essential \ cmake \ git \ gfortran \ libatlas-base-dev \ libavcodec-dev \ libavformat-dev \ libavresample-dev \ libcanberra-gtk3-module \ libdc1394-22-dev \ libeigen3-dev \ libglew-dev \ libgstreamer-plugins-base1.0-dev \ libgstreamer-plugins-good1.0-dev \ libgstreamer1.0-dev \ libgtk-3-dev \ libjpeg-dev \ libjpeg8-dev \ libjpeg-turbo8-dev \ liblapack-dev \ liblapacke-dev \ libopenblas-dev \ libpng-dev \ libpostproc-dev \ libswscale-dev \ libtbb-dev \ libtbb2 \ libtesseract-dev \ libtiff-dev \ libv4l-dev \ libxine2-dev \ libxvidcore-dev \ libx264-dev \ pkg-config \ python3.8-dev \ python3.8-dev \ python3-numpy \ python3-matplotlib \ python3-pip \ qv4l2 \ v4l-utils \ v4l2ucp \ zlib1g-dev # git_source ${VER} RUN git clone --depth 1 --branch ${VER} https://github.com/opencv/opencv.git RUN git clone --depth 1 --branch ${VER} https://github.com/opencv/opencv_contrib.git RUN python3 -m pip install -U pip RUN python3 -m pip uninstall -y numpy RUN python3.8 -m pip install -U pip RUN python3.8 -m pip install setuptools RUN python3.8 -m pip install numpy RUN apt-key adv --fetch-key http://repo.download.nvidia.com/jetson/jetson-ota-public.asc RUN echo 'deb https://repo.download.nvidia.com/jetson/common r32.6 main\n\ deb https://repo.download.nvidia.com/jetson/t210 r32.6 main' > /etc/apt/sources.list.d/nvidia-l4t-apt-source.list RUN apt-get update && apt-get install -y nvidia-cuda nvidia-cudnn8 RUN cd opencv && \ mkdir build && \ cd build && \ cmake \ -D BUILD_EXAMPLES=OFF \ -D BUILD_opencv_python2=OFF \ -D BUILD_opencv_python3=ON \ -D CMAKE_BUILD_TYPE=RELEASE \ -D CMAKE_INSTALL_PREFIX=${PREFIX} \ -D CUDA_ARCH_BIN=5.3,6.2,7.2 \ -D CUDA_ARCH_PTX= \ -D CUDA_FAST_MATH=ON \ -D CUDNN_VERSION='8.0' \ -D EIGEN_INCLUDE_PATH=/usr/include/eigen3 \ -D ENABLE_NEON=ON \ -D OPENCV_DNN_CUDA=ON \ -D OPENCV_ENABLE_NONFREE=ON \ -D OPENCV_EXTRA_MODULES_PATH=/tmp/build_opencv/opencv_contrib/modules \ -D OPENCV_GENERATE_PKGCONFIG=ON \ -D WITH_CUBLAS=ON \ -D WITH_CUDA=ON \ -D WITH_CUDNN=ON \ -D WITH_GSTREAMER=ON \ -D WITH_LIBV4L=ON \ -D WITH_OPENGL=ON \ -D BUILD_PERF_TESTS=OFF \ -D BUILD_TESTS=OFF \ -D PYTHON3_EXECUTABLE=python3.8 \ -D PYTHON3_INCLUDE_PATH=$(python3.8 -c "from distutils.sysconfig import get_python_inc; print(get_python_inc())") \ -D PYTHON3_PACKAGES_PATH=$(python3.8 -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())") \ -D PYTHON3_LIBRARY=/usr/lib/aarch64-linux-gnu/libpython3.8.so \ -D CPACK_BINARY_DEB=ON \ -D CPACK_PACKAGING_INSTALL_PREFIX=${PREFIX} \ .. WORKDIR /tmp/build_opencv/opencv/build RUN make -j${MAX_JOBS} RUN make install RUN cpack -G DEB
Code language: Dockerfile (dockerfile)

To create the deb package, -D CPACK_BINARY_DEB=ON and -D CPACK_PACKAGING_INSTALL_PREFIX=${PREFIX} are added to the cmake options. Finally, cpack -G DEB creates a deb package.

Build the Dockerfile above.

docker build -t opencv-build . --build-arg MAX_JOBS=$(nproc)
Code language: Bash (bash)

After the build is complete, retrieve the built binaries.

$ id=$(docker run -it --rm -d opencv-build bash) $ debs=$(docker exec -it ${id} find /tmp/build_opencv/opencv/build/ -maxdepth 1 -name "*.deb" | sed -e "s/[rn]+//g") $ for deb in $debs; do docker cp ${id}:$deb . done $ docker stop ${id} $ ls Dockerfile *deb Dockerfile OpenCV-4.6.0-aarch64-dev.deb OpenCV-4.6.0-aarch64-libs.deb OpenCV-4.6.0-aarch64-licenses.deb OpenCV-4.6.0-aarch64-main.deb OpenCV-4.6.0-aarch64-python.deb OpenCV-4.6.0-aarch64-scripts.deb
Code language: Bash (bash)

Copy and install these deb packages into Jetson Nano. The following is executed on the Jetson Nano.

sudo apt update sudo apt list --installed | grep -i opencv | xargs apt remove -y sudo apt install -y ./OpenCV*.deb
Code language: Bash (bash)

Remove the existing opencv package and then install the built OpenCV package.

Performance

We will post the times we ran the Docker builds of Pytorch and OpenCV on the Jetson Nano and the host PC, respectively.

Jetson
Nano
Host PC
i9-12900K, 32GB
faster
PyTorchMAX_JOBS=2
real 832m39.333s
user 0m7.352s
sys 0m4.268s
MAX_JOBS=24
real 176m48.354s
user 0m0.852s
sys 0m0.904s
x4.7
OpenCVMAX_JOBS=4
real 161m34.754s
user 0m2.240s
sys 0m1.660s
MAX_JOBS=24
real 117m28.657s
user 0m0.648s
sys 0m0.517s
x1.3

Builds on the host PC are 4.7 times faster on PyTorch and 1.3 times faster on OpenCV. Also, host PC builds do not depend on hardware, so they can be introduced to build servers such as CI/CD.

The above codes are available at https://github.com/otamajakusi/build_jetson_nano_libraries.

That’s all.

Reference