树莓派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, 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);
    }
    
    torch::Tensor dot_product(torch::Tensor input, torch::Tensor weight)
    {
        input = input.to(torch::kFloat32);
        weight = weight.to(torch::kFloat32);
        float *input_ptr = (float *)input.data_ptr();
        float *weight_ptr = (float *)weight.data_ptr();
    
        int width = weight.numel();
        int width_16 = width + (16 - width % 16);//将矩阵长度转换成16的倍数
        SharedArray<float> mapA(width_16), mapB(width_16);
    
        for (int i = 0; i < width_16; ++i)
        {
            if (i < width)
            {
                mapA[i] = input_ptr[i];
                mapB[i] = weight_ptr[i];
            }
            else
            {
                mapA[i] = 0;//不足的补零
                mapB[i] = 0;
            }
        }
        auto k = compile(dotproduct);
    
        k.setNumQPUs(NQPUS);
    
        k(width, &mapA, &mapB);
    
        for (int i = 0; i < width; i++) {
            input_ptr[i] = mapA[i];
        }
        return input;
    }
    
    torch::Tensor dot_add(torch::Tensor input, torch::Tensor weight)
    {
        input = input.to(torch::kFloat32);
        weight = weight.to(torch::kFloat32);
        float *input_ptr = (float *)input.data_ptr();
        float *weight_ptr = (float *)weight.data_ptr();
    
        int width = weight.numel();
        int width_16 = width + (16 - width % 16);
        SharedArray<float> mapA(width_16), mapB(width_16);
    
        for (int i = 0; i < width_16; ++i)
        {
            if (i < width)
            {
                mapA[i] = input_ptr[i];
                mapB[i] = weight_ptr[i];
            }
            else
            {
                mapA[i] = 0;
                mapB[i] = 0;
            }
        }
        auto k = compile(dotadd);
    
        k.setNumQPUs(NQPUS);
    
        k(width, &mapA, &mapB);
    
        for (int i = 0; i < width; i++) {
            input_ptr[i] = mapA[i];
        }
        
        return input;
    }
    
    
    PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
      m.def("add", &dot_add, "dot_add");
      m.def("product", &dot_product, "dot_product");
    }
    
  • 编写二维矩阵(H x W)乘一维矩阵(W x 1)的c++程序,返回一个(H * 1)的矩阵

    • 此程序可用于计算深度学习中的每个案例的score
    //matrix.cpp
    #include <torch/extension.h>
    #include <vector>
    #include <QPULib.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/time.h>
    const int NQPUS = 4;
    struct Cursor
    {
        Ptr<Float> addr;
        Float current, next;
    
        void init(Ptr<Float> p)
        {
            gather(p);
            current = 0;
            addr = p + 16;
        }
    
        void prime()
        {
            receive(next);
            gather(addr);
        }
    
        void advance()
        {
            addr = addr + 16;
            gather(addr);
            current = next;
            receive(next);
        }
    
        void finish()
        {
            receive(next);
        }
    };
    
    void step(Ptr<Float> map, Ptr<Float> weight, Ptr<Float> mapOut, Int pitch, Int width, Int height)
    {
        Cursor row, cursorofweight;
        map = map + pitch * me() + index();
        For(Int y = me(), y < height, y = y + numQPUs())
    
            // 指定存放该行矩阵与权重矩阵计算得到的结果的位置
            // 每行结果存放至16位上
            Ptr<Float>
            p = mapOut + y * 16;
    
        // 初始化Cursor类
        row.init(map);
        row.prime();
        cursorofweight.init(weight);
        cursorofweight.prime();
    
        // 计算该行结果
        Float accumulate = 0;
        For(Int x = 0, x < width, x = x + 16)
            // 每次迭代,都计算本次得到本次结果,并存储,同时取下次计算所需的数据
            row.advance();
       		cursorofweight.advance();
        	accumulate = accumulate + row.current * cursorofweight.current;
    
        End
        // 存储计算结果到p上
        store(accumulate, p);
        // 释放该Cursor
        row.finish();
        cursorofweight.finish();
        map = map + pitch * numQPUs();
    
        End
    }
    torch::Tensor accumartix(torch::Tensor input, torch::Tensor weight)
    {
        input = input.to(torch::kFloat32);
        weight = weight.to(torch::kFloat32);
        int width = weight.numel();
        int width_16 = width + (16 - width % 16);
        int height = input.numel() / width;
    
        float *input_ptr = (float *)input.data_ptr();
        float *weight_ptr = (float *)weight.data_ptr();
        // 创建qpu与cpu交互的矢量
        SharedArray<float> mapA(width_16 * height), mapB(width_16), sumofmartix(16 * height);
        for (int i = 0; i < height; ++i)
        {
            for (int j = 0; j < width_16; ++j)
            {
                if (j < width)
                    mapA[i * width_16 + j] = input_ptr[i * width + j];
                else
                    mapA[i * width_16 + j] = 0;
            }
        }
    
        for (int j = 0; j < height; ++j)
        {
            for (int i = 0; i < 16; ++i)
            {
                sumofmartix[16 * j + i] = 0;
            }
        }
    
        for (int j = 0; j < width_16; ++j)
        {
            if (j < width)
                mapB[j] = weight_ptr[j];
            else
                mapB[j] = 0;
        }
        auto k = compile(step);
    
        k.setNumQPUs(NQPUS);
    
        k(&mapA, &mapB, &sumofmartix, width_16, width, height);
        torch::Tensor ans = torch::zeros(height);
        float *ans_ptr = (float *)ans.data_ptr();
        for (int j = 0; j < height; ++j)
        {
            for (int i = 0; i < 16; ++i)
            {
                ans_ptr[j] += sumofmartix[16 * j + i];
            }
        }
        return ans; 
    }
    
    PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
      m.def("gpu", &accumartix, "accumartix");
    }
    

3. 向pytorch注册c++程序

pytorch官方文档

  • 根据pytorch官方文档,向pytorch注册c++的代码需要两个文件: .cpp文件和setup.py文件

结合QPULib注册c++端程序

  • setup.py 文件需要根据QPULib的编译参数、依赖路径进行修改

    from setuptools import setup, Extension
    from torch.utils import cpp_extension
    from torch.utils.cpp_extension import BuildExtension, CppExtension
    
    setup(
        name='dot_cpp',
        ext_modules=[
            Extension(
                name='dot_cpp',
                sources=['dot.cpp'],
                include_dirs=cpp_extension.include_paths()+["/root/QPULib/Lib", "/root/QPULib/Lib/Source",
                "/root/QPULib/Lib/Target","/root/QPULib/Lib/VideoCore","/root/QPULib/Lib/Common"],
                library_dirs=["."],
                libraries=["qpu"],
                language='c++',
                extra_compile_args = ["-fpermissive","-w","-std=c++0x","-DQPU_MODE"])
                
        ],
        cmdclass={
            'build_ext': BuildExtension
        })
    
    
  • 所有需要注册的文件建构如下

    embedded_project/
    	dot.cpp
    	setup.py
    	libqpu.so
    
  • 在该文件夹下执行

    python3 setup.py install
    
  • 成功编译之后,import模块进行测试

    import torch
    import dot_cpp
    
  • 未报错即可上板子验证

4. 使用raspberry pi,nfs挂载docker image中的file system,验证上述是否成功

环境准备

  • 在实验室主机上安装docker,下载docker镜像,并且使用docker cp指令,将整个file system拷贝出来

    sudo apt update && sudo apt install qemu qemu-user-static binfmt-support
    #这句话是为了能够在x64的处理器上模拟arm处理器
    docker pull registry.cn-beijing.aliyuncs.com/ja1zhou/myrasp:torch
    #可以使用named volume共享container和host之间的文件
    docker volume create --driver local -o o=bind -o type=none -o device="/home/jay/rasp_docker" rasp_volume
    docker run -it --name rasp -v rasp_volume:/root registry.cn-beijing.aliyuncs.com/ja1zhou/myrasp:torch
    #使用docker cp将filesystem拷贝出来
    sudo docker cp rasp:/ ~/home/jay/rasp_docker_filesystem
    
    
  • 通过nfs启动之后,将/home/jay/rasp_docker挂载,并chroot

    mount 192.168.0.101:/home/jay/rasp_docker_filesystem /mnt -o nolock
    cd /mnt
    mount --rbind /dev dev/#这句话是将原filesystem的GPU device挂载到chroot的路径下
    chroot .
    
  • (可选)通过iptables代理所有的流量

    #注,因为在uboot启动的时候,我将树莓派的网关设置为了主机的内网地址,所以只需要设置主机的iptables即可
    #下面的语句在host中输入
    iptables -t nat -A POSTROUTING -s192.168.0.1/255.255.255.0 -j SNAT --to public_ip_of_host
    

正式验证

  • 验证qpu计算的准确性

    # accuracy.py
    import pytorch as torch
    import time
    import dot_cpp
    
    a = torch.randint(100)
    b = torch.randint(100)
    c = a * b
    print("ans in pytorch:")
    print(c)
    d = dot_cpp.product(a, b)
    print("ans in gpu:")
    print(d)
    
    • 结果如下:
    ans in pytorch:
    tensor([-5.9086e-02, -4.3276e+00, -6.5376e-01,  5.0014e-01, -1.2216e-01,
             8.5097e-02, -1.4941e+00,  3.5625e+00,  1.2412e-03,  4.9355e-01,
            -4.8173e-01,  1.3379e-01,  6.8660e-01, -3.0867e-01,  4.1459e-01,
             3.8146e-01,  2.6874e-01, -1.0085e-01, -1.9247e-01, -3.8177e-01,
            -7.2695e-01, -7.9857e-01,  9.2179e-01, -4.4537e-01,  1.2229e+00,
            -1.9606e+00,  2.1500e+00,  6.2939e-02, -2.9404e-02, -1.6333e-01,
             5.8653e-01, -3.0282e-01,  1.7500e+00, -1.9485e+00,  1.0097e+00,
            -2.9966e-01,  5.1717e-01,  8.6291e-01,  1.4203e+00,  1.5049e-01,
             4.0039e-01, -2.1761e-01, -2.7387e-02, -5.7702e-01,  5.4926e-02,
            -2.1086e-01, -2.1043e-01, -4.2422e-01,  3.1212e-02, -3.5714e-01,
             7.3226e-01,  1.7916e+00, -8.3882e-02,  1.7431e+00,  7.5411e-02,
             1.4379e-01, -2.1750e+00,  5.3509e-01,  1.9931e+00, -1.0812e+00,
             9.5756e-01, -2.2465e-01, -2.7048e-01, -5.4887e-01,  4.8681e-01,
            -5.7749e-02,  8.6992e-02, -7.8780e-01,  1.3495e+00, -7.5135e-02,
             6.2448e-01, -1.1303e-02, -1.0266e-01, -1.4959e+00, -1.6517e+00,
             1.1846e-01,  1.5355e+00, -4.2969e-01,  2.9539e-01, -5.9056e-01,
             1.0564e+00, -5.7899e-01,  1.7013e-02,  5.1986e-01, -4.7120e-02,
            -3.4399e-02, -1.4235e-01, -1.4144e+00,  5.1103e-01,  7.2233e-01,
            -6.0687e-01, -8.2988e-01, -2.7205e-01,  1.0952e+00, -9.7423e-02,
             4.9439e-02, -1.7460e-02,  2.0516e-01, -7.8793e-01, -1.8765e+00])
    ans in gpu:
    tensor([-5.9086e-02, -4.3276e+00, -6.5376e-01,  5.0014e-01, -1.2216e-01,
             8.5097e-02, -1.4941e+00,  3.5625e+00,  1.2412e-03,  4.9355e-01,
            -4.8173e-01,  1.3379e-01,  6.8660e-01, -3.0867e-01,  4.1459e-01,
             3.8146e-01,  2.6874e-01, -1.0085e-01, -1.9247e-01, -3.8177e-01,
            -7.2695e-01, -7.9857e-01,  9.2179e-01, -4.4537e-01,  1.2229e+00,
            -1.9606e+00,  2.1500e+00,  6.2939e-02, -2.9404e-02, -1.6333e-01,
             5.8653e-01, -3.0282e-01,  1.7500e+00, -1.9485e+00,  1.0097e+00,
            -2.9966e-01,  5.1717e-01,  8.6291e-01,  1.4203e+00,  1.5049e-01,
             4.0039e-01, -2.1761e-01, -2.7387e-02, -5.7702e-01,  5.4926e-02,
            -2.1086e-01, -2.1043e-01, -4.2422e-01,  3.1212e-02, -3.5714e-01,
             7.3226e-01,  1.7916e+00, -8.3882e-02,  1.7431e+00,  7.5411e-02,
             1.4379e-01, -2.1750e+00,  5.3509e-01,  1.9931e+00, -1.0812e+00,
             9.5756e-01, -2.2465e-01, -2.7048e-01, -5.4887e-01,  4.8681e-01,
            -5.7749e-02,  8.6992e-02, -7.8780e-01,  1.3495e+00, -7.5135e-02,
             6.2448e-01, -1.1303e-02, -1.0266e-01, -1.4959e+00, -1.6517e+00,
             1.1846e-01,  1.5355e+00, -4.2969e-01,  2.9539e-01, -5.9056e-01,
             1.0564e+00, -5.7899e-01,  1.7013e-02,  5.1986e-01, -4.7120e-02,
            -3.4399e-02, -1.4235e-01, -1.4144e+00,  5.1103e-01,  7.2233e-01,
            -6.0687e-01, -8.2988e-01, -2.7205e-01,  1.0952e+00, -9.7423e-02,
             4.9439e-02, -1.7460e-02,  2.0516e-01, -7.8793e-01, -1.8765e+00])
    
  • 验证qpu计算速度

  • 此处我们改写了c++端的算子,使得计算的时间以一定的方式返回python端。同时,编写了c++端cpu的算子进行比较

  • 此处进行比较的时间仅仅是在c++端进行计算所用的时间。实际上在python端调用gpu算子的时候,会在调用过程、cpu与gpu通信过程中消耗一定的时间

  • 此项目展示了在c++端集成、编写算子所能带来的性能提升潜力

    # time.py
    # 此时间只计算了各自在计算该矩阵乘法时所耗费的时间
    # 并通过写入到返回值中来获得
    import torch
    import time
    import cpu_cpp
    import matrix_cpp
    
    for i in range(0,6):
        a = torch.randn(100 *10**i )
        b=  torch.randn(10**i)
        gpu = matrix_cpp.gpu(a,b)
        cpu=cpu_cpp.cpu(a,b)
        print("cpu 100 * 10 ** %d takes %d.%06d"% (i,cpu[0],cpu[1]))
        print("gpu 100 * 10 ** %d takes %d.%06d"% (i,gpu[0],gpu[1]))
    
    • 结果如下:
    cpu 100 * 10 ** 0 takes 0.000004
    gpu 100 * 10 ** 0 takes 0.000164
    cpu 100 * 10 ** 1 takes 0.000023
    gpu 100 * 10 ** 1 takes 0.000169
    cpu 100 * 10 ** 2 takes 0.000206
    gpu 100 * 10 ** 2 takes 0.000171
    cpu 100 * 10 ** 3 takes 0.002116
    gpu 100 * 10 ** 3 takes 0.000388
    cpu 100 * 10 ** 4 takes 0.021245
    gpu 100 * 10 ** 4 takes 0.003079
    cpu 100 * 10 ** 5 takes 0.214486
    gpu 100 * 10 ** 5 takes 0.029622
    

写在最后

本项目为《嵌入式系统》课程项目,结果较为初步。

感谢同组的组员lxk同学和组员cxf同学~

感谢杨老师和吕老师给予的指导和帮助~

上一篇:mysql之DATETIME、DATE和TIMESTAMP简单了解


下一篇:逆向工程核心原理之第13章PE文件格式导入表的初步理解