树莓派3B 实现通过GPU加速pytorch计算

嵌入式系统 树莓派3B 实现通过GPU加速pytorch计算

本项目仅用于学术用途

总思路

  • 交叉编译环境为raspberry pi 3B docker镜像
  • 使用github上开源的GPU库QPULib
  • 在树莓派上安装pytorch,通过交叉编译加速过程
  • 通过pytorch提供的接口编译并且注册c++端的程序
  • 通过nfs mount docker镜像的file system,直接运行pytorch

具体实现

1. 安装交叉编译环境

具体过程收到这篇博客启发,找到这篇博客的过程如下
  • 在搜索引擎搜索预编译的树莓派3B python wheel,搜索到pytorch论坛中的这条帖子
  • 在这条帖子中,名为choonkiatlee的用户提到了其编译好的wheel以及编译使用的docker镜像
  • 在这名用户的github博客界面中,找到了上述文章
在我的主机(windows电脑上)安装docker for windows,并进行一系列镜像加速托管配置
托管服务
  • 登录阿里云,点击容器镜像服务

  • 选择个人实例

  • 在仓库管理中创建个人命名空间,我的情况下是ja1zhou

  • 创建访问凭证,选择固定密码,这个密码为本地进行登录时进行验证的密码

  • 在主机上启动docker engine,并在终端输入

    sudo docker login --username=your_username registry.cn-beijing.aliyuncs.com
    #之后输入设置的密码
    

    成功登录阿里云

  • 镜像仓库中创建新的仓库名称,我的情况下为myrasp

镜像加速
  • 根据阿里云官方文档进行镜像加速配置

  • 直接修改docker for windows的daemon.json文件,添加

    {
         
      "registry-mirrors": ["https://your_server_name.mirror.aliyuncs.com"]
    }
    
拉取工作镜像,配置shared volume,并设置工作环境
  • docker pull choonkiatlee/raspbian:build
    docker volume create --driver local -o o=bind -o type=none -o device="C:\Users\MyUsername\Downloads\rasp_docker" rasp_docker
    #这条命令的目的是直接将下载中的诸多文件,比如wheel和QPULib,与docker进行共享
    #将torch-1.4.0a0+7f73f1d-cp37-cp37m-linux_armv7l.whl下载并且放置在创建的volume中
    docker run -it --name rasp -v rasp_docker:/root/rasp_docker choonkiatlee/raspbian:build
    
  • 进入docker容器之后,需要输入以下的命令进行配置,下述命令是对上文博客的补充

    #docker自带的python版本为3.7.3
    apt-get update && apt-get install -y python3-numpy
    #torch运行需要numpy支持
    cd /root/rasp_docker
    pip3 install torch-1.4.0a0+7f73f1d-cp37-cp37m-linux_armv7l.whl
    
  • 进行验证

    root@3288e690face:~/rasp_docker# python3
    Python 3.7.3 (default, Jan 22 2021, 20:04:44) 
    [GCC 8.3.0] on linux
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import torch
    >>> torch.__version__
    '1.4.0a0+7f73f1d'
    
  • torch已经成功安装

  • 将这个版本的docker镜像打包,作为后续开发的起点

    docker commit rasp registry.cn-beijing.aliyuncs.com/ja1zhou/myrasp:torch
    docker push registry.cn-beijing.aliyuncs.com/ja1zhou/myrasp:torch
    
(可选)从源码编译pytorch(暂未成功复现
  • 设置代理,允许代软件通过内网、公网防火墙

  • 设置代理软件,允许来自局域网的连接

  • 在docker镜像中,设置环境变量

    export all_proxy="http://host_ip:host_port"
    
  • 下载并进行准备工作

    cd /root
    git clone https://github.com/pytorch/pytorch.git
    git checkout v1.4.0
    git submodule sync
    git submodule update --init --recursive
    apt install -y python3-cffi python3-numpy libatlas-base-dev
    pip3 install cython wheel pyyaml pillow
    #选择不编译一切附加模块
    export USE_CUDA=0
    export USE_CUDNN=0
    export USE_MKLDNN=0
    export USE_METAL=0
    export USE_NCCL=OFF
    export USE_NNPACK=0
    export USE_QNNPACK=0
    export USE_DISTRIBUTED=0
    export BUILD_TEST=0
    export MAX_JOBS=8
    python3 setup.py install
    
  • 报错,是与submodule protobuf相关,当时作者也遇到了这个问题,但是不知道他具体编译的时候使用的是protobuf的哪个tag,已经在github上面提交了issue

2. 下载QPULib,根据其提供的功能进行代码编写

QPULib代码结构
QPULib/
  Lib/
  	Subdirectories/
  		*.cpp
  		*.h
  	*.cpp
  	*.h
  Doc/
  	irrelevant
  Tests/
  	*.cpp
  	Makefile
QPU加速原理
  • QPU 是由 Broadcom 开发的矢量处理器,其指令可对 32 位整数或浮点值的 16 元素向量进行操作。例如,给定两个 16 元素向量

    10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

    20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35

    QPU 的整数加法指令计算第三个向量

    30 32 34 36 38 40 42 44 46 48 50 52 54 56 58 60

    其中输出中的每个元素是输入中对应的两个元素的总和。

    • 每个 16 元素向量由四个四分之一的矢量部分组成。

    • QPU 每个时钟周期处理一个矢量部分,而 QPU 指令需要四个连续的时钟周期来提供完整的16位的结果矢量,这就是“QPU”名称的由来。

    • Pi 总共包含 12 个 QPU,每个都以 250MHz 的频率运行。这是每秒 750M 向量指令的最大吞吐量(250M 周期除以 4 个周期每条指令乘以 12 个QPU)。或者:每秒 12B 次操作(750M 指令乘以 16 个向量元素)。在某些情况下,QPU 指令可以一次提供两个结果,因此 Pi 的 QPU 通常以 24 GFLOPS 进行工作。

    • QPU 是 Raspberry Pi 图形管道的一部分。如果想在 Pi 上制作高效的图形,那么可能需要 OpenGL ES。但是,如果只想尝试加速 Pi 项目的非图形部分,那么 QPULib 值得一看。

  • 而为了避免计算时候的阻塞,可以通过引入流水线的方式。

    • 在计算该次数据的同时,去取下一次计算所需要的数据
  • 同时,可以引入多QPU并行计算的方式,及每次采取多QPU计算不同区域的数据,从而实现GPU的高效利用。

  • 具体阅读Makefile,发现编译时Include目录为Lib/目录,会先将Lib/下的所有*.cpp编译成*.o

  • 最后,将*.o和链接到可执行文件

编译动态链接库
  • 按照这个思路,先将所有Lib/下的cpp文件编译为.o文件,并将之编译成动态链接库,省去在向pytorch注册时候的重复编译,将Makefile改写如下

    # Root directory of QPULib repository
    ROOT = ../Lib
    
    # Compiler and default flags
    CXX = g++
    CXX_FLAGS = -fpermissive -Wconversion -std=c++0x -I $(ROOT)
    
    # Object directory
    OBJ_DIR = obj
    
    # Debug mode
    ifeq ($(DEBUG), 1)
      CXX_FLAGS += -DDEBUG
      OBJ_DIR := $(OBJ_DIR)-debug
    endif
    
    # QPU or emulation mode
    ifeq ($(QPU), 1)
      CXX_FLAGS += -DQPU_MODE
      OBJ_DIR := $(OBJ_DIR)-qpu
    else
      CXX_FLAGS += -DEMULATION_MODE
    endif
    
    # Object files
    OBJ =                         \
      Kernel.o                    \
      Source/Syntax.o             \
      Source/Int.o                \
      Source/Float.o              \
      Source/Stmt.o               \
      Source/Pretty.o             \
      Source/Translate.o          \
      Source/Interpreter.o        \
      Source/Gen.o                \
      Target/Syntax.o             \
      Target/SmallLiteral.o       \
      Target/Pretty.o             \
      Target/RemoveLabels.o       \
      Target/CFG.o                \
      Target/Liveness.o           \
      Target/RegAlloc.o           \
      Target/ReachingDefs.o       \
      Target/Subst.o              \
      Target/LiveRangeSplit.o     \
      Target/Satisfy.o            \
      Target/LoadStore.o          \
      Target/Emulator.o           \
      Target/Encode.o             \
      VideoCore/Mailbox.o         \
      VideoCore/Invoke.o          \
      VideoCore/VideoCore.o
    
    # Top-level targets
    
    .PHONY: top clean
    LIB = $(patsubst %,$(OBJ_DIR)/%,$(OBJ))
    top: $(LIB)
            @$(CXX) $(CXX_FLAGS) -shared -fPIC $^ -o libqpu.so
    
    clean:
            rm -rf obj obj-debug obj-qpu obj-debug-qpu
            rm -f Tri GCD Print MultiTri AutoTest OET Hello ReqRecv Rot3D ID *.o
            rm -f HeatMap
            rm -f libqpu.so
    # Intermediate targets
    
    $(OBJ_DIR)/%.o: $(ROOT)/%.cpp $(OBJ_DIR)
            @echo Compiling $<
            @$(CXX) -c -o $@ $< $(CXX_FLAGS)
    
    %.o: %.cpp
            @echo Compiling $<
            @$(CXX) -c -o $@ $< $(CXX_FLAGS)
    
    $(OBJ_DIR):
            @mkdir -p $(OBJ_DIR)
            @mkdir -p $(OBJ_DIR)/Source
            @mkdir -p $(OBJ_DIR)/Target
            @mkdir -p $(OBJ_DIR)/VideoCore
    
  • 通过Makefile文件可知,会将所有Lib/下的文件打包成为libqpu.so动态链接库

  • 添加动态链接库到系统库

    vim /etc/ld.so.conf
    #在该文件新增一行,为libqpu.so的路径
    /root/embedded_project/
    #:wq保存退出
    ldconfig#刷新动态库路径
    
编写调用GPU进行并行计算的C++代码
  • 编写流水线式的多qpu并行运算的矩阵c++运算程序

    • 该程序主要实现了等大小的矩阵点乘、点加,并将结果返回
    //"dot.cpp"
    #include <torch/extension.h>
    #include <vector>
    #include <QPULib.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    const int NQPUS = 4; //调用的qpu数
    
    void dotproduct(Int n, Ptr<Float> x, Ptr<Float> y)
    {
         
        Int inc = numQPUs() << 4;
        Ptr<Float> p = x + index() + (me() << 4);
        Ptr<Float> q = y + index() + (me() << 4);
        gather(p); gather(q);
    
        Float xOld, yOld;
        For (Int i = 0, i < n, i = i+inc)
            gather(p+inc); gather(q+inc);//获取下次运算需要的数据
            receive(xOld); receive(yOld);//计算之前拿到的数据
            store(xOld * yOld, p);
            p = p+inc; q = q+inc;
        End
    
        receive(xOld); receive(yOld);
    }
    
    void dotadd(Int n
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值