TI的多核DSP支持OpenMP,可以方便地使用多核来加速一些计算过程。

我使用的DSP是TI的6678,编译器可以是7.4或者8.x版本。7.4版本的编译器只支持OpenMP3.0版本,而8.x版本的编译器支持OpenMP3.0和部分的OpenMP4.0的功能。因为我使用的是7.4版本的编译器,所以后面只对OpenMP3.0相关的设置进行介绍。

Specification里面介绍了OpenMP实现的机制,重点理解第一章里的“Excution Model”和“Memory Model”。Summary Card是一个总结,相当于一个API手册,可以用它方便地查找命令的语法。
实际OpenMP的使用除了查阅这两个文档意外还有一些需要注意的地方。

OpenMP模块

TI对OpenMP的支持,我在官网上只找到了这个SDK里有一个omp的模块,这个2.01.02.06应该是在网上能找到的最新的也是唯一的版本了。
在这里插入图片描述
下载并安装完成后,可以在安装路径下找到omp_1_01_03_02这个package。在它的doc目录下也有一个文档“User_Guide.pdf"介绍了这个package的使用方式。

RTSC设置

在这里插入图片描述
一般是直接用SDK中的OpenMP就行,但我这边有一些问题,所以用的是重新编译的OpenMP,它依赖的Package的版本和XDCTools的版本也都很重要。从SDK的Release notes里可以找到需要的版本号。主要有下面这三个:

  • IPC 1.24.3.32
  • PDK 1.1.2.6
  • SYS/BIOS 6.33.6.50

使用重新编译的Package需要去掉原来的Packge的勾选,然后添加新的Package的目录。

OpenMP模块中有两个属性条目,用于地址映射。这个映射关系是始终成立的。
在这里插入图片描述
它会将noncachedMsmcAlias作为起始地址,长度为4MB的地址空间映射到mpaxRaddr作为起始地址的4MB物理空间中。这个两个属性是由默认值的,默认会将0xA0000000开始的4MB映射到0x0C000000开始的4MB空间中。也就是原先DDR中的一部分空间映射到了MSMC上。访问0xA0000000开始的4MB空间相当于在访问MSMC。

额外的存储开销

为了支持OpenMP的功能,同步各个核,需要一些空间存放状态,从而对各个核进行调度。这部分共享的状态/代码,都存放在MSMCSRAM_NOCACHE这块地址空间中。所以带OpenMP功能的工程的platform里需要有一个定义“MSMCSRAM_NOCACHE”的存储区域。这部分存储空间的数据是随时都有可能被其它核改写的,所以不能被Cache。
在这里插入图片描述
记得前面做的地址映射吗,MSMC的物理地址被映射到了另一块地址上。所以L1D如果作为Cache,直接通过物理地址访问MSMC中的数据是能够被Cache的;而通过另一块地址访问则不能。
可以看到MSMCSRAM_NOCACHE中的一大半都已经被用了,而这部分是不包含任何用户自己的代码的。所以相当于是一个固有的额外存储开销。
MSMCSRAM_NOCACHE中还剩下一些空间没有用。所以可以把它设成platform的默认datamemory,来充分利用这部分空间。但实际上这部分空间只剩1MB左右了,如果临时变量、全局变量比较多就不太方便了。
OpenMP的程序都需要把代码和数据放在共享的存储空间中,而在MSMC空间有限的情况下,大部分数据都只能放在DDR里,代码可以放在MSMCSRAM里。

不同于单核的heap,多核系统中的共享堆区可以通过IPC中的SharedRegion这个模块实现。它使得用户在堆区中的变量能够被不同的核共享访问。
HeapOmp管理一个Local Heap和一个Shared Heap,在Shared Heap还没建立时,先使用Local Heap。用户在堆区分配和释放空间默认都会在Shared Heap上进行操作。

OpenMP的使用

在这里插入图片描述
在工程设置中添加omp的编译选项。

在调试界面通过Variables窗口看到的变量值和实际值不同,但是用printf打印输出变量值能看到实际值。我猜想这部分问题是由于MPAX的设置不当造成的,因为前面的地址映射是对于每个Core来说的,而对于调试器来说,它并不知道这样的地址映射。对于这个问题的解决方式并不清楚。

结构体/类成员

结构体中的成员或者类成员(除静态成员以外)都不能作为共享变量。包括类的成员函数,也是不能在OpenMP的代码块中执行的。这应该是OpenMP 3.0中的限制,需要特别注意。
在这里插入图片描述

parallel for

#pragma omp parallel for
    for(int i = 0; i<N; ++i){

  parallel和for是两条独立的命令,他们可以分开使用。parallel命令产生多个线程,for命令以当前线程数执行for循环。两者结合起来就是用一定的线程数执行for循环。

firstprivate/private/shared

#pragma omp parallel for firstprivate(a), private(b), shared(c)
    for(int i = 0; i<N; ++i){

  在OpenMP的代码块中声明的变量都是private,firstprivate,private和shared是parallel for的子命令,它们后面的括号中需要放前面声明过的变量。
  firstprivate后面跟的变量列表中的变量是在每个thread中用相同的初始值初始化;private则是每个thread的变量都是不一样的;shared就是共享的变量。
  如果某个变量是一个类的对象,firstprivate相当于调用拷贝构造函数;private相当于调用默认构造函数;shared则不会重新生成新的变量,每个thread都访问同一个变量。

collapse

#pragma omp parallel for collapse(2)
    for(int i = 0; i<2; ++i){
        for(int j = 0; j<4; ++j){

  如果是多重循环,可以把它“打平”,从而更好地平行化。比如我有8核,可以做到8个thread并行,而第一重循环只有2,如果不用collapse子命令,只有2个thread会被安排任务;collapse(2)会将两重循环一起考虑,从而将循环体中的代码更加均匀地分到各个thread。

reduction

#pragma omp parallel for reduction(+:sum)
    for(int i = 0; i<N; ++i){
        sum += i;

  reduction子命令可以实现分散的运算最后汇总。比如在某些时候,我们需要对变量求和,可以用多个thread分别计算部分和,最后再对这些部分和求和。

单核与多核性能对比

  引入OpenMP后,可以使程序部分并行化。每个核拷贝一份共享的变量,到自己的存储空间(threadprivate memory)进行运算,最后再写回。我觉得比较好的做法是每个thread共享指向堆区的一个指针,这样可以减少数据的搬运,而让每个thread都能对数据进行独立的修改。
  原本的单核程序可能是在L2和MSMC上运行,而使用OpenMP的程序可能受到空间限制,把程序放在DDR上运行,这样就会慢很多,反而起不到加速的效果。而每个核之间的同步也有额外的开销,所以加速效果可能达不到预期。
  实际测试发现,如果在platform里直接设置L2 Cache,OpenMP可能都跑不通,会出现任务栈溢出的错误。后来我就没有在platform上直接设置L2 Cache,而是在运行的过程中设置L2 Cache,这样做的效果也不是很理想。在DDR上开辟堆区,如果没有L2 Cache实际计算的效率非常低。源操作数和目的操作数均位于DDR上,我用4核OpenMP+L2 Cache计算8000次浮点乘法,平均每次用时80ns;而用单核+L2 Cache仅需3点几个ns。所以如果是想加速算法,真的不建议用OpenMP。

再linux下输入top,在输入1,再跑openmp的时候,发现只有一个CPU再运作,怀疑是CMakefile的问题,只加-openmp不行,加入 find_package(OpenMP) if (OPENMP_FOUND) set (CMAKE_C_FLAGS “${CMAKE_C_FLAGS} KaTeX parse error: Double subscript at position 10: {OpenMP_C_̲FLAGS}") se…{CMAKE_CXX_FLAGS} OpenMPCX.
再Linux下使用gcc编译器,O2优化,编译出的openmp并行比不用openmp的串行程序还要慢。然后换了intel2020的icc编译器,原有程序的基础上使用icc编译,发现速度提升飞快,比串行快了大约8s,由于我的程序中并行for循环使用并不是很多,所以效果不是很明显,但解决了并行效率低的问题。 另外gcc是免费的,icc是收费的而且装好要20多个G以上,据说icc的bug比较多。
教程目录:http://blog.csdn.net/tostq/article/details/51245979 本节我们将运行第一个多核DSP程序,熟悉CCS开发环境,学会使用CCS调试工具,主要内容如下: (1)新建CCS项目 (2)导入Target 仿真模块 (3)使用调试工具 一、新建CCS项目 选择File/New/CCS Project 二、新建项目对话框
int layer_for = 2; int each_task_size_for = 100000 / 20; int num_threads = omp_get_max_threads(); // int num_threads_limit = omp_get_thread_limit();//编不过? printf("多线程:%d\t%d\n", num_threads); omp_set_num_threads(num_threads); #pragma omp parallel num_th..
一. 这么学DSP比较有效 在开始C6678的架构讲解之前,我想拉出一点篇幅,给大家谈一下,根据我个人的理解,怎么样才能比较快的学好DSP。 (1)学习DSP,首先要与学MCU区分开,毕竟这是两个完全不同的架构,而且DSP与MCU的设计思路完全不一样。MCU是为通用的控制而设计,DSP则是专为高速应用而设计; (2)从硬件的角度来考虑,要玩转DSP,首先需要仔细阅读其数据手册和用户指南。数据手册中,重点阅读DSP的技术指标,最关键的是对芯片的电源需求做详细的了解,其中对核电压的需求一定要认真了解,这是DSP
       IPC是SYS/BIOS处理核间通信的组件        IPC的几种应用方式(下面中文名字是自行翻译,旁边有英文=_=||,另外下面的配图中的蓝色表示需要调用模块的APIs,而红色模块表示仅仅需要配置(如在.cfg中配置),而灰色模块表示是非必须的):        (1)最小使用(Minimal use):这种情况是通过核间的通知机制(notification)来实施的.
b'OpenMP在Linux上的使用方式是什么?' OpenMP是一种并行编程的API,可以在共享内存系统中进行并行计算。在Linux上使用OpenMP可以先安装支持OpenMP的编译器(如GCC),然后通过编译时加上编译选项“-fopenmp”来开启OpenMP的支持。在程序中可以使用pragma语句指定并行化的区域,例如#pragma omp parallel和#pragma omp for等。通过这些指令,程序能够自动利用多个CPU核心进行并行计算,提高程序的性能。 ### 回答2: ### 回答3: