Building Jetson Nano libraries on host PC

docker Docker

こちらの記事では、Jetson Nano上でPyTorchライブラリをビルドし、こちらの記事では、Jetson Nano上でOpenCVライブラリをビルドしました。これらのビルドはすべてJetson Nano上で実行しましたが、こちらの記事で解説した方法を使用しJetson Nanoのアーキテクチャであるaarch64バイナリをホストPCで実行しPyTorchとOpenCVライブラリをビルドする方法を解説します。

docker

ビルドにはDockerを使用します。Dockerを使用することでaarch64バイナリが必要とする共有ライブラリなどの環境を構築することができます。なお以下を実行し事前にログインユーザをdockerグループに追加しsudoなしでdockerコマンドを実行できるようにします。

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

再起動しdockerグループへの追加を反映させます。

PyTorch

以下Dockerfileの説明です。

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

ARG MAX_JOBSdocker build時に外から--build-arg MAX_JOBS=$(nproc)などとして渡すことを可能にしています。

# 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 PillowCode language: Dockerfile (dockerfile)

途中repo.download.nvidia.comをapt repositoryに追加し、その後nvidia-cuda nvidia-cudnn8をインストールしています。

以降はこちらの記事で説明したものとほぼ同じになります。

## 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_wheelCode language: Dockerfile (dockerfile)

上記Dockerfileをビルドします。

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

Jetson Nanoでビルドする際はメモリが少ない為MAX_JOBS=2を指定しましたが、ホストPCではこの制限は緩やかです。

ビルドが終わったら、ビルドされたバイナリを取り出します。

$ id=$(docker run -it --rm -d pytorch-build bash)
$ pytorch=$(docker exec -it ${id} find /pytorch/dist -type f | sed -e "s/[\r\n]\+//g")
$ vision=$(docker exec -it ${id} find /pytorch/torchvision/dist -type f | sed -e "s/[\r\n]\+//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.whlCode language: Bash (bash)

手元にtorch-1.11.0a0+gitbc2c6ed-cp38-cp38-linux_aarch64.whltorchvision-0.12.0a0+9b5a3fe-cp38-cp38-linux_aarch64.whlの2つができました。

これら2つをJetson Nanoにコピーしインストールします。以下は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-*.whlCode language: Bash (bash)

OpenCV

以下Dockerfileの説明です。

FROM nvcr.io/nvidia/l4t-base:r32.6.1
ENV DEBIAN_FRONTEND=noninteractive

ARG VER="4.6.0"
ARG PREFIX=/usr/local
ARG MAX_JOBSCode language: Dockerfile (dockerfile)

こちらも、いくつかの変数をdocker build時に--build-argで渡せるようにしています。以降はこちらの記事で説明したものとほぼ同じになります。

#    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 DEBCode language: Dockerfile (dockerfile)

debパッケージを作成するために、cmakeのオプションに、-D CPACK_BINARY_DEB=ON-D CPACK_PACKAGING_INSTALL_PREFIX=${PREFIX}を追加しています。最後に、cpack -G DEBでdebパッケージを作成します。

上記Dockerfileをビルドします。

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

ビルドが終わったら、ビルドされたバイナリを取り出します。

$ 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/[\r\n]\+//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.debCode language: Bash (bash)

これらのdebパッケージをJetson Nanoにコピーしインストールします。以下はJetson Nanoで実行します。

sudo apt update
apt list --installed *opencv*|awk -F/ '/opencv/ {print $1}'|sudo xargs apt remove -y
sudo apt install -y ./OpenCV*.debCode language: Bash (bash)

既存のopencvパッケージを削除してからビルドしたOpenCVパッケージをインストールします。

パフォーマンス

PytorchのDockerビルド、OpenCVのDockerビルドをそれぞれJetson NanoとホストPC上で実行した時間を掲載します。

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

ホストPCでのビルドはPyTorchで4.7倍、OpenCVで1.3倍、高速にビルドすることが可能です。またホストPCでのビルドは機材に依存しないためCI/CDなどのビルドサーバに導入することが可能です。

上記のコードは https://github.com/otamajakusi/build_jetson_nano_libraries にまとめてあります。

以上です。

参照