cache-coherent shared-memory system

我们最平常使用的很多x86、arm芯片都属于多核共享内存系统,这种系统表现为多个核心能直接对同一内存进行读写访问。尽管内存的存取速度已经非常快,但是仍然不足以与CPU的处理速度相比,因此为了提高CPU的利用率,一般会在芯片的每个核心内提供cache。cache像内存一样用于数据的存取,虽然它的大小有限,但速度比内存更快,接近于CPU的速度,因此它会充当CPU与内存的中间站角色,用于缓冲数据。

每当CPU要从内存读取数据来处理的时候,会先查看cache内是否存在该数据,存在的话就直接从cache中读取,否则会从内存中读取该数据,同时也会连同所需数据的周边数据一起读到cache中;而当CPU在处理完成并且把数据写回内存时,会先把数据写回cache,由于此时其他核心也有可能存在该数据的副本,因此会令其他核心的cache中的数据副本无效并且更新成新的数据副本,这种cache写回策略被成为cache coherence

          

在这种系统中,如果CPU是处理的是一系列连续的数据,那么cache中预先读取的相邻数据将会很好地被派上用场,从而减少了CPU向内存读取数据的次数,因此能提高CPU的数据处理效率。

2D-Wave实现

在多核共享内存系统中,这里的2D-Wave算法实现类似于前一篇文章描述的Task Pool,但其中一个明显的差异就是在共享内存系统中不需要一个主核心来管理任务池,因为任务池是存放在共享内存中的,它能被所有核心访问。核心P需要调用tp_submit(fun_ptr, <param>)来把任务提交到任务池。其中参数fun_ptr就是待执行的函数(任务),参数<param>是传给该函数的相关参数。类比到我们日常使用的函数就是pthread_create。

原子操作

既然涉及到了共享内存的多任务处理,不得不说的就是原子操作。如TP中的宏块依赖表,它位于共享内存中也就意味着它能被各个任务(核心)访问,如果不对这种访问进行原子操作,就有可能导致程序执行发生错误。

有宏块MB(i, j),它在宏块依赖表里面的依赖计数为dep_count[i][j],初始状态为dep_count[i][j]=2。

线程1为MB(i, j)的右上方宏块的解码线程,当宏块解码完毕后需要对dep_count[i][j]进行减一操作。

线程2为MB(i, j)的左方宏块的解码线程,当宏块解码完毕后需要对dep_count[i][j]进行减一操作。

对dep_count[i][j]的减一操作在汇编上可以被分解为以下几步:

  1. 把dep_count[i][j]的值从内存中读取出来,存放到寄存器R,记为R = load(dep_count[i][j])
  2. 减法操作,R = R – 1
  3. 把寄存器R内的数值写回到内存dep_count[i][j],记为dep_count[i][j] = stroe(R)

由于对内存上的数值进行减一操作可以被分为三步指令,因此在多任务系统中可能会出现下面的情况

最终结果是,虽然MB(i, j)的两个依赖都被解码完成,但由于它的依赖计数dep_count[i][j]不为零,那么程序会认为该宏块的依赖没有解码完成,也就不会开始解码MB(i, j)。为了避免出现这种情况,我们就有必要对这一减法运算加上原子操作,该原子操作用atomic_dec来标记。得到的伪代码如下

/*注意,此处x代表纵轴,y代表横轴*/
void decode_mb(int x, int y)
{
/* ... the actural work (实际的解码操作) */ /* check and submit right MB (如果右宏块准备就绪则提交解码任务) */
if (y < WIDTH-1)
{
atomic_dec(dep_count[x][y+1]);
if (dep_count[x][y+1] == 0)
{
tp_submit(decode_mb, x, y+1);
}
} /* check and submit down-left MB (如果左下宏块准备就绪则提交解码任务) */
if (x < HEIGHT-1 && y != 0)
{
atomic_dec(dep_count[x+1][y-1]);
if (dep_count[x+1][y-1] == 0)
{
tp_submit(decode_mb, x+1, y-1);
}
}
}

优化

前面讲了,在含有cache的多核共享内存系统中,从内存读取数据进入核心处理时会顺带预读取所需数据的相邻部分进入cache,以此来加快数据的处理速度。按照cache系统的这种特性,如果cache预读的数据量(cache line)大于宏块的宽度(16 pixel)的话,那么在核心读取一个宏块的像素数据进行处理时,它的相邻宏块也被预读取进入了cache,此时,如果该核心在下一次解码任务中需要解码的恰好是这个被预读了的相邻宏块,将会省去从内存中读取像素数据这一步骤,从而提高解码效率。因此优化的方向就是在Task Pool方案的基础上使得核心尽可能地去解码相邻宏块。

按照上面的思路,下面展示一种叫做tail submit的优化方案

/*注意,此处x代表纵轴,y代表横轴*/
void decode_mb(int x, int y)
{
down_left_avail = 0;
right_avail = 0;
do {
/* ... the actural work (实际的解码作业) */ if (x < HEIGHT-1 && y != 0)
{
atomic_dec(dep_count[x+1][y-1]);
if (dep_count[x+1][y-1] == 0)
down_left_avail = 1;
}
if (y < WIDTH-1)
{
atomic_dec(dep_count[x][y+1]);
if (dep_count[x][y+1] == 0)
right_avail = 1;
} /* give priority to right MB */
/* 当前线程优先解码右宏块 */
if (down_left_avail && right_avail)
{
/* 提交左下宏块给其他线程解码,自己解码右宏块 */
tp_submit(decode_mb, x+1, y-1);
y++;
}
else if (down_left_avail)
{
x++; y--;
}
else if (right_avail)
{
y++;
}
}while (down_left_avail || right_avail);
}

如上述伪代码所示,在解码任务decode_mb中添加了循环,每次循环后都会判断当前宏块的右宏块以及左下宏块是否准备就绪,从而去解码它们。这一循环使得一个核心在执行解码任务时,能更连续地解码宏块,而不是每解码完一个宏块就把下一个宏块的解码交给其他线程,这样能够节省tp_submit的开销。另外,如果相关宏块准备就绪的话,从代码中可以看到一个宏块解码完成后,会在下一个循环中更优先解码它的右宏块,这就达到了优化的目的。

关于h.264并行解码算法的更详细分析请参考Ben Juurlink · Mauricio Alvarez-Mesa · Chi Ching Chi · Arnaldo Azevedo · Cor Meenderinck · Alex Ramirez:《Scalable Parallel Programming Applied to H.264/AVC Decoding》

h.264并行解码算法2D-Wave实现(基于多核共享内存系统)的更多相关文章

  1. h.264并行解码算法3D-Wave实现(基于多核共享内存系统)

    3D-Wave算法是2D-Wave的扩展.3D-Wave相对于只在帧内并行的2D-Wave来说,多了帧间并行,不用等待前一帧完成解码后才开始下一帧的解码,而是只要宏块的帧间参考部分以及帧内依赖宏块解码 ...

  2. h.264并行解码算法2D-Wave实现(基于多核非共享内存系统)

    在<Scalable Parallel Programming Applied to H.264/AVC Decoding>书中,作者基于双芯片18核的Cell BE系统实现了2D-Wav ...

  3. h.264并行解码算法分析

    并行算法类型可以分为两类 Function-level Decomposition,按照功能模块进行并行 Data-level Decomposition,按照数据划分进行并行 Function-le ...

  4. h.264并行熵解码

    在前面讨论并行解码的章节中,我们专注于讨论解码的宏块重建部分,甚至把宏块重建描述成宏块解码,这是因为在解码工作中,宏块重建确实占了相当大的比重,不过解码还包含其它的部分,按照解码流程可粗略分为: 读取 ...

  5. 让dcef3支持mp3和h.264 mp4解码播放

    嵌入式Chromium框架(简称CEF) 是一个由Marshall Greenblatt在2008建立的开源项目,它主要目的是开发一个基于Google Chromium的Webbrowser控件.CE ...

  6. h.264硬件解码

    // H264HWDecoder.m // H264EncoderDecoder // // Created by lujunjie on 2016/11/28. // Copyright © 201 ...

  7. 音视频编解码技术(一):MPEG-4/H.264 AVC 编解码标准

    一.H264 概述 H.264,通常也被称之为H.264/AVC(或者H.264/MPEG-4 AVC或MPEG-4/H.264 AVC) 1. H.264视频编解码的意义 H.264的出现就是为了创 ...

  8. 【视频编解码·学习笔记】2. H.264简介

    一.H.264视频编码标准 H.264视频编码标准是ITU-T与MPEG合作产生的又一巨大成果,自颁布之日起就在业界产生了巨大影响.严格地讲,H.264标准是属于MPEG-4家族的一部分,即MPEG- ...

  9. H.264视频的RTP荷载格式

    Status of This Memo This document specifies an Internet standards track protocol for the   Internet ...

随机推荐

  1. 基于VMware为CentOS 6.5配置两个网卡

    为CentOS 6.5配置两块网卡,一块是eth0,一块是eth1,下面以master为例 1.选择“master”-->“编辑虚拟机设置”,如下所示 2.单击“添加”,如下 3.选择“网络适配 ...

  2. hdu2035java

    人见人爱A^B Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Sub ...

  3. [Eclipse]The type XXX cannot be resolved. It is indirectly referenced from required .class files

    在Eclipse中遇到The type XXX cannot be resolved. It is indirectly referenced from required .class files错误 ...

  4. RedHat7搭建MongoDB集群

    下载RPM安装包# wget -c -r -N -np -nd -L -nH https://repo.mongodb.org/yum/redhat/7/mongodb-org/stable/x86_ ...

  5. '[linux下tomcat 配置

    tomcat目录结构 bin ——Tomcat执行脚本目录 conf ——Tomcat配置文件 lib ——Tomcat运行需要的库文件(JARS) logs ——Tomcat执行时的LOG文件 te ...

  6. Spring MVC 中的 forward 和 redirect

    Spring MVC 中,我们在返回逻辑视图时,框架会通过 viewResolver 来解析得到具体的 View,然后向浏览器渲染.假设逻辑视图名为 hello,通过配置,我们配置某个 ViewRes ...

  7. Oracle学习第三讲

    关联查询 笛卡尔积 指做关联操作的每个表的每一行都和其他表的每一行组合,假设两个表的记录条数分别为x和y,笛卡尔积将返回x*y条记录 例如:select count(*) from emp; sele ...

  8. zoom与transform:scale的区别

    一. zoom特性 1. zoom是IE的私有属性,但目前除Firefox不支持外,其他浏览器支持尚好. 2.定义: zoom即变焦,可改变元素尺寸,属于真实尺寸.zoom:百分值/数值/normal ...

  9. 将CString(unicode)转换为char*(ANSI)

    1.将CString(unicode)转换为char*(ANSI) CString strServIP; pChat->GetDlgItemText(IDC_IP,strServIP); ] = ...

  10. Visual Studio 2008中添加运行按钮 转载

    在Visual Studio 2008中添加运行按钮 默认情况下,VS2008中的工具栏上没有运行按钮,只有调试(Debug)按钮,可按照以下方法添加 1.点击菜单Tools(工具)->Cust ...