本文共 8140 字,大约阅读时间需要 27 分钟。
PINT
- 北京奕斯伟公司软件全栈解决方案的统称。
UAP
- 北京奕斯伟公司芯片的统称。 pint_competition
的工程架构如下所示:
PINT平台的硬件架构示意图如下所示,我们将着重介绍其中的:Mcore和Ncore、脉动阵列(Systolic Array)和Cache。
PINT硬件平台主要包含了Master Core
(简称Mcore
)和Normal Core
(简称Ncore
)两部分。其中,Mcore
只有1个,它的作用有如下几个:
而Ncore
有256个,它按照脉动阵列(Systolic Array
)计算单元进行分组,每8个Ncore
构成一个处理单元(即Process Cell,简称PE),共有32个PE,如上图左上角所示。
上面在介绍Ncore时,提到了脉动阵列,这是个什么东东呢?你如果接触过FPGA,相信一定不会陌生。简单的来说,脉动阵列是一种计算架构,它可以让数据在运算单元的阵列中进行流动,减少访存的次数,并且使得结构更加规整,布线更加统一,提高频率。
可能通过这一句话还不足以理解脉动阵列,我们再来继续深入一点点…
可以看下面这个图,传统计算结构和脉动阵列结构的对比。左边是传统的计算架构,可用于各种形式的计算。CPU、GPU就是这种架构,用寄存器存储数据,一个程序告诉ALU从寄存器中取数,然后执行一种操作(例如加法、乘法或者逻辑操作),然后再把结果写会指定的寄存器中。脉动阵列,第一个ALU取数,经过处理后被传递到下一个ALU同时第二个数据进入第一个ALU。依次类推。在第一个数据到最后一个ALU之后,每个周期都能得到一个结果。这样,脉动阵列可以平衡IO读写与运算,在消耗较小memory带宽的情况下提高吞吐率,有效解决数据存取速度远远大于数据处理速度的结构。
故,PINT平台为了加快AI领域中最常用的矩阵乘法和卷积计算速度,也在硬件中加入了脉动阵列计算单元。
Cache一种容量较小但速度很高的存储器,在PINT平台中,每个Ncore
拥有自己独立的Instruction cache(I-cache)
和Data cache(D-cache)
,同时通过Interconnection Network
连接Shared Cache
,如上面的总架构示意图所示,这样可以加快对DDR的访问效率。
1、Mcore
执行程序main()入口点;
Mcore
进行数据准备,任务分配等串行任务; 3、Mcore
启动ncore的并行计算; 4、Mcores
独立进行分解的任务计算,待所有Ncore
都计算完成之后,返回到Mcore
; 5、Mcore
可以继续进行串行任务,启动Ncore
并行; 如此反复,一个程序中可以多次进行串行-并行任务的切换,具体流程如下图所示:
PINT平台的编程主要分为两个部分:Host端和Device端,我们需要分别进行具体的代码编写
有没有感觉特别像Linux驱动开发的味道呢?
设备侧编程接口封装在pintdev.h
,在文档pintdev.pdf
中有详细介绍,具体程序如下:
pint_kernel.c
如下:
#include "pintdev.h"//API参考 pintdev.pdfint *buf_a;int *buf_b;int *buf_c;int data_len; //数据长度//编写Ncore并行函数void ncore_parallel_add(void){ int cid = pint_core_id(); //获取到每个核(core)的ID int core_num = pint_core_num();//获取core的总数 int loop = ((data_len - 1) / core_num) + 1; // 将数据平均的分配到每个core上执行 for(int i = 0; i < loop; ++i) { int id = core_num * i + cid; if(id < data_len) { buf_c[id] = buf_a[id] + buf_b[id]; } }}//main函数是Mcore调用的int main(void){ //启动并行函数 pint_parallel_start((unsigned int)ncore_parallel_add); pint_printf("complete!!!!!\n");}
ncore_parallel_add
每个Ncore都会调用,但是我们通过每个Ncore的全局ID,将具体的计算任务分配到不同Ncore上!
pint_core_id
函数API说明
2.2.3.7 unsigned int pint_core_id ( ) [inline]return global core id in current deviceReturnsglobal core id in current device [0,255]Definition at line 201 of file pintdev.h.
pint_core_num
函数API说明
2.2.3.9 unsigned int pint_core_num ( ) [inline]return the number of total cores in deviceReturnsthe number of total cores in deviceDefinition at line 234 of file pintdev.h.
pint_parallel_start
API说明
2.3.2.1 #define pint_parallel_start uap_parallel_startstart parallelmcore call pint_parallel_start, all ncores will execute the functionParametersfuncAddr - parallel function addressReturnsvoidDefinition at line 849 of file pintdev.h.
pint_core_id
API说明
2.2.3.7 unsigned int pint_core_id ( ) [inline]return global core id in current deviceReturnsglobal core id in current device [0,255]Definition at line 201 of file pintdev.h.
在主机侧提供方便用户编程的API接口封装在pint_runtime_api.h
中,实现设备侧程序的编译运行,文档pintapi.pdf
中有详细介绍,具体程序如下:
main.c
如下:
#include#include #include #include #include "pint_runtime_api.h"#define TEST_SIZE 400*(1<<20)//API参考 pintapi.pdfint RunTest(){ pintError_t err = pintSuccess; int len = 100; size_t sz = len * sizeof(int); // 需要注意 host memory 和 device memory都在host端申请 // host memory申请用 malloc;device memory申请用 pintMalloc // 1. malloc host memory on cpu. int *iA = (int*)malloc(sz); int *iB = (int*)malloc(sz); int *iC = (int*)malloc(sz); for(int i = 0; i < len; ++i){ iA[i] = rand() % 200; iB[i] = rand() % 200; iC[i] = 0; } // 2. malloc device memory on device. int *d_iA, *d_iB, *d_iC, *d_Test; assert(pintSuccess == pintMalloc((void**)&d_iA, sz)); assert(pintSuccess == pintMalloc((void**)&d_iB, sz)); assert(pintSuccess == pintMalloc((void**)&d_iC, sz)); // 3. copy data from host to device assert(pintSuccess == pintMemcpy(d_iA, 0, iA, 0, sz, pintMemcpyHostToDevice)); assert(pintSuccess == pintMemcpy(d_iB, 0, iB, 0, sz, pintMemcpyHostToDevice)); //创建Program三种方法:BinaryFile、SourceFile、StringFile // 4. build the device main function as a program // 本次采用预编译BinaryFile方式创建Program char *file_path = "pint.pin"; pintProgram_t pint_program; assert(pintSuccess == pintCreateProgram(&pint_program, &file_path, 1, NULL, pintBinaryFile)); //5. set arguments related with device main function arguments // 映射方式进行参数赋值;如果是常用,可知直接采用host端地址 pintArgs_t pint_args[4] = { { "buf_a", d_iA}, { "buf_b", d_iB}, { "buf_c", d_iC}, { "data_len", &len},}; // 6. execute the device main function assert(pintSuccess == pintLaunchProgram(pint_program, pint_args, 4, NULL, NULL)); // 7. copy the result to the host // 拷贝函数带有自动同步功能 assert(pintSuccess == pintMemcpy(iC, 0, d_iC, 0, sz, pintMemcpyDeviceToHost)); // 8. check the result between cpu and pint int flag = 0; for(int i = 0; i < len; ++i){ if(iC[i] != iA[i] + iB[i]){ printf("error %d : %d, %d\n", i, iA[i] + iB[i], iC[i]); flag = -1; break; } } // 9. release the resource. assert(pintSuccess == pintFreeProgram(pint_program)); assert(pintSuccess == pintFree(d_iA)); assert(pintSuccess == pintFree(d_iB)); assert(pintSuccess == pintFree(d_iC)); free(iA); free(iB); free(iC); return flag;}int main(int argc, char *argv[]){ int status = RunTest(); if(status == 0){ printf("SUCCESS!!\n"); } else{ printf("FAILED!!\n"); } return 0;}
CMakeLists.txt
如下:
cmake_minimum_required(VERSION 2.8)project(pint)set(CMAKE_MODULE_PATH /usr/local/pint/cmake)find_package(PintAPI REQUIRED)if(PintAPI_FOUND) pintapi_compile(SOURCE_FILES pint_kernel.c) include_directories(${ PintAPI_INCLUDE_DIRS}) link_directories(${ PintAPI_LIBRARY_DIRS}) add_executable(${ PROJECT_NAME} main.c) target_link_libraries(${ PROJECT_NAME} ${ PintAPI_LIBRARIES})endif()
在/home/CLAY/pint_competition/pint
目录依次执行以下命令:
mkdir buildcd buildcmake ..make./pint
运行结果如下:
CLAY@pytest05:~/pint_competition/pint/build$ ./pint<0:1>complete!!!!!SUCCESS!!
cd /home/CLAY/pint_competition/buildrm -rf *cmake ..makecd ../bin
提交的时候先运行 linux指令 :
w
各个占用状态确定
top //该命令查看cpu占用率pint-smi //该命令查看uap设备占用状态nvidia-smi //该命令查看gpu设备占用状态
完成代码写作和测试之后,提交作品使用submit脚本
cd pint_competitionsubmit_work
运行程序
./pint 6 12 /contest/dataset/dag_data_6_12.b result.b./pint 200 5000 /contest/dataset/dag_data_200_5000.b result.b./pint 1000000 100000000 /contest/dataset/dag_data_1000000_100000000.b result.b./pint 1111111 111111111 /contest/dataset/dag_data_1111111_111111111.b result.b
进行输出结果与正确结果进行对比,如果没有输出,则表示输出结果与正确结果保持一致。
diff result.b /contest/dataset/dag_result_6_12.bdiff result.b /contest/dataset/dag_result_200_5000.bdiff result.b /contest/dataset/dag_result_1000000_100000000.bdiff result.b /contest/dataset/dag_result_1111111_111111111.b
对比结果也可以采用cmp指令:
cmp result.b /contest/dataset/dag_result_6_12.bcmp result.b /contest/dataset/dag_result_200_5000.bcmp result.b /contest/dataset/dag_result_1000000_100000000.bcmp result.b /contest/dataset/dag_result_1111111_111111111.b
运行程序
./cpu 6 12 /contest/dataset/dag_data_6_12.b result.b./cpu 200 5000 /contest/dataset/dag_data_200_5000.b result.b./cpu 1000000 100000000 /contest/dataset/dag_data_1000000_100000000.b result.b./cpu 1111111 111111111 /contest/dataset/dag_data_1111111_111111111.b result.b
进行输出结果与正确结果进行对比,如果没有输出,则表示输出结果与正确结果保持一致。
diff result.b /contest/dataset/dag_result_6_12.bdiff result.b /contest/dataset/dag_result_200_5000.bdiff result.b /contest/dataset/dag_result_1000000_100000000.bdiff result.b /contest/dataset/dag_result_1111111_111111111.b
对比结果也可以采用cmp指令:
cmp result.b /contest/dataset/dag_result_6_12.bcmp result.b /contest/dataset/dag_result_200_5000.bcmp result.b /contest/dataset/dag_result_1000000_100000000.bcmp result.b /contest/dataset/dag_result_1111111_111111111.b
转载地址:http://yjnaf.baihongyu.com/