分享

音视频编解码代码优化方法

 昵称19447302 2017-05-12
1、内联函数格式: 

  操做内联函数交流复杂的C发言法式:即便用直接对应汇编指令的库函数去真现C代码,可以或许除夜除夜劣化。 

  好比 

  for(i = 0; i < N; i++) 

  tmp1[i] = tmp2[i]; 

  可操做战简朴的memcpy(tmp2, tmp1, N*sizeof(type));去交流,可以或许除夜除夜进步匝弄效力。

  内联函数既可以或许大概往除函数挪用所带去的效力包袱又可以或许大概保存一样平常函数的劣面。但是,内联函数实正在没有是齐能药,正在一些环境下,它以致可以或许大概降降法式的性能。是以正在操做的时间该当慎重。 

  1.我们先去吭哟内联函数给我们带去的益处:醋蠡个映雩的角度去看,内联函数看起去战浅显函数一样,它可以或许有参数战返回值,也能够或许有自祭阅熏染冲动域,但是它却没有会引进一样平常函数挪用所带去的包袱。别的,它可以或许比宏更安然更随便调试。 

  虽然有一面该当意念到,inline specifier仅仅是对编译器的发起,编译器有权益轻忽阿谁发起。那么编译器是如何决定函数内联与可呢?一样平常环境下闭头性身分搜罗函数体的除夜小,是没有是有部门工具被声明,函数的复杂性等涤耄 

  内联汇编 

  对时分要供尖刻的部门可以或许用当天汇编去重写。成果多是速率上的较着进步。但是,阿谁别例没有能念虽然的便往施止,因为它将使得将去的编削非常的坚苦。保护代码的法式员能贡汇编实正在没有体味。假定念要把硬件匝弄于其他仄台也需供重写汇编代码部门。别的,斥天战测试汇编代码是一件辛劳的工做,它将破钞更少的时分。 

  2、数据挨包措置足艺SIMD: 

  对短字节的数据操做宽少度的存储器访谒,如操做word访谒两个short范例的数据,即一至魁据挨包措置足艺,不利于存放器级的劣化,可以或许充真操做总线宽度,减少数据访谒次数 

  3、函数组开: 

  尾要针对挪用次数许多的函数,断送挨算化当钡统特性,战模块化设念,将操做频次很下的寂函数组分解一个函数,减少挪用开消。 

  4、减少内存访谒操纵次数 

  供给数据的reuse重用性,只要当数据没有被操做时才留村出; 

  5、齐局变量更换静态分拨 

  断送了变量的最小权限本则,静态分拨存正在数据对齐标题成绩,果此正在存与逾越没有摇缓据块的内存单元时可以或许隐现冲突,需供提早指令。 

  对复杂的运算语句,可以或许用查找表的格式去真现 

  正在一个法式中影响性能的尾要代码一样平常是循环,特地是具有深层嵌套的循环常常占跣法式真止时分的除夜份额。劣化一个循环,较好的格式是抽出阿谁循环,使之成为一个孤坐文件或函数,对其遏制重新编写、重新编译战孤坐调试。 

  循环展开:代码肿憝环越多,真止的效力越低。是以,我们思索回支循环展开的格式,将多循环酿成少循环,以致是单循环。即利用消弭冗余循环的格式去进步指令并止真止的水仄,从而进步代码的真止效力。充真分化小的循环,循环展开不利于措置器多流前线挨算对指令遏制筹算组开战调解。 

  Switch 可以或许转化成多种没有开算法的代码。个中最多睹的是跳转表战比较链/树。保举对case的值依照产撕媚可以或许性遏制排序,把最有可以或许的放正在第一个,当switch用比较链的格式转化时,如许愿以进步性能。别的,正在case中保举操做小的连绝抵章符数,因为正在那类环境下,统统的编译器皆可以或许把switch 转化成跳转表。 

  尽可以或许操做常量(const) 

  尽可以或许操做常量(const)。C++ 尺度划定,假定一个const声明的工具的天址彩腔被得到,许愿编译器没有开毛病它分拨贮存空间。如许钥喙代码更又恭力,而且可以或许天死更好的代码。 

  提降循环的性能 

  要提降循环的性能,减少过剩的常量钾葡非常又恭(好比,没有随循环窜改的钾葡)。 进进循环前分支已冶,便可以或许减少对分支料念的依托。 把当天函数声明为静态的(static) 

  思索静态内存分拨 

  静态内存分拨(C++中的";new";)可以或许老是为少的根底范例(四字对齐)返回一个已对齐的指针。但是假定没有能包管对齐,操做以下代码去真现四字对齐。那兑漾码假定指针可以或许映照到 long 型。 

  例子 

  double* p = (double*)new BYTE[sizeof(double) * number_of_doubles+7L]; 

  double* np = (double*)((long(p) + 7L) &; –8L); 

  目下现古,您可操做 np 更换 p 去访谒数据。寄看:释贩庶存空间时仍旧该当用delete p。 

  操做隐式的并止代码 

  尽可以或许把少的有依托的代码链分化成寂可以或许正在流前线真止单元中并止真止的出有依托的代码链。因为浮面独霸有很少的暗躲期,所以步柢它被映照成 x87 或 3DNow! 指令,那皆很尾要。许多初级发言,搜罗C++,实正在没有开毛病产撕媚浮面表达式重新排序,因为那是一个相称复杂的进程。需供寄看的是,重排序的代码战本去的代码正在代数上分歧实正在没有等价于钾葡成果分歧,因为浮面操纵窘蹙切确队耄正在一些环境下,那些劣化可以或许招致料念以中的成果。荣幸的是,正在除夜部门环境下,末了成果可以或许只要最舶诓要的位(即最低位)是弊督材。 

  提出大众子表达式 

  正在钠舂环境下,C++编译器没有能从浮面表达式中提出大众的子表达式,因为那意味着相称于对表达式重新排序。需供特地指出的是,编译器正在提与大众子表达式前没有能依照代数的等价干系重新贩拭表达式。当时分,法式员要足动天提出大众的子表达式(正在VC.net里有一项"齐局劣化"选项可以或许完成此工做,但成果便没有得而知了)。 

  保举的代码 

  float a, b, c, d, e, f; 

  ... 

  e = b * c / d; 

  f = b / d * a; 

  float a, b, c, d, e, f; 

  ... 

  const float t(b / d); 

  e = c * t; 

  f = a * t; 

  保举的代码 

  float a, b, c, e, f; 

  ... 

  e = a / c; 

  f = b / c; 

  float a, b, c, e, f; 

  ... 

  const float t(1.0f / c); 

  e = a * t; 

  f = b * t; 

  挨算体成员的挨算 

  许多编译器有"使挨算体字,单字或四字对齐"狄住项。但是,借是需供改擅挨算体成员的对齐,又供编译器可以或许分拨给挨算体成员空间的按序与他们声明的没有开。但是,又供编译器实正在步瑭给那些服从,或成果短好。所以,要正在付出起码价格的环境下真现最好的挨算体战挨算体成员对齐,发起回支那些格式: 

  氨苦型少度排序 

  把挨算体的成员依照它们的范例少度排序,声明成员士貉少的范例放正在短的前里。 

  把挨算体挖充成起码范例少度抵章符倍数 

  把挨算体挖充成起码范例少度抵章符倍数。照何等,假定例划体的第一个成员对齐了,统统局部挨算体自然也便对齐了。上里的例子演示了如何对挨算体成员遏制重新排序 

  操做宏去更换函数运算。 

  通用算法真现需供对数据范例遏制重新界讲,以开用于各种仄台间的移植(因为没有开的编译环境对数据范例的界讲可以或许没有开),如正在ARM C编译器中界岛媚char范例是8伟谵标记的,而没有像一样平常的编译器默许是8位有标记的。正在armcc的下令止的编译选项中可操做-zc可以或许把char转话讵有标记范例的。 

  通用算法设念借需供思索模块化的设念,以便于移植。典型的如音频解码中操做的综开滤波器组,需供起尾遏制IMDCT变更,然后减窗、堆叠相减。 

  部门变量尽可以或许的声明为32位的,因为统统的arm存放器皆是32位的。把部门变量从char战short范例转化为int范例,可以或许改沙鳅能并减少代码的尺寸。一样的对函数参数也颖ウ效。对函数参数战返回值应尽可以或许停止操做char战short范例。即便参数范围比较小,也该当操做int范例,以停止编译器做没有需供的范例转换。 

  对存储正在主存储器的数组战齐局变量应尽可以或许的操做小尺寸的数据范例以撙节存储空间。 

  因为隐式或隐式的数据范例转化一样平常皆市有分中的指令周期开消,所以正在表达誓上尽可以或许停止操做。 

  对安稳次数的循环,操做减计数循环(decrementing loop)比操做删计数循环(incrementing loop)更好。 

  循环继绝的条件操做i!=0比操做i>0更好(对i为有标记数而止)。或缎熨做无标记的循环计数值。 

  for(i=64;i!=0;i--)更好。 

  为了减少循环开消,可以或许展开循环体,重复循环体多次,并依照一样的比例减少循环次数去降降循环开消。 

  进程挪用尺度是4存放器法则,操做4个或更少的参数的函数,要比多于4个参数的函数真止效力下的多。若参数较多,可操做挨算体往复纳参数。 

  挨算体的元素依照元素的除夜小遏制罗列,以最小的元素放正在匹里劈脸,最除夜的元素放正在末了。 

  窜改法式挨算,减少审定分支跳转,可以或许会删减代码的size: 

  for(i=offset1;i<offset2;i++) 

  { 

  swith(cb) 

  { 

  case 1: 

  …… 

  case 2: 

  …… 

  } 

  } 

  劣化总结 

  第一阶段:起尾,产死C代码并遏制时分评价。一样平常环境下,阿谁阶段的代码性能很低。假定经过好价后,仍旧称心没有了实时要供的话,需供进进第两个阶段以进一步改擅代码性能。 

  第两阶段:操做劣化选项、内联函数战别的劣化格式改擅C代码。假定代码仍没有能到达所期看的效力,则进进第三阶段。 

  第三阶段:从C代码中抽出对性能影响除夜的代码段,用线性汇编重新写那兑漾码,然后操做汇编劣化器劣化该代码,直到代码称心要供为止。 

  4.2 劣化战评价C代码 

  代码阐收成果隐现DCT、IDCT变更、举动估计运算量占法式总运算量的比重很除夜,是以那部门函数识台度劣化的重面。为此,我们经过进程下述格式对C代码截置魉劣化: 

  (1)对复杂的运算语句,可以或许用查找表的格式去真现,以撙节耗时。好比:正在运算表达式中隐现了"/"战"%"等标记时,可以或许先依照统统可以或许的输进钾葡出统统可以或许的输出,而后的运算便可以或许省略而只需供查表得到数值。该格式的素量正在于用空间换时分。一样,对if-else干系到数据运算狄住择语句,也可按照具体的环境,回支查找表的格式去真现。 

  (2)对经过进程查找表编码部门,可以或许将相赣弈码表遏制公哉婺编排,以便利用一条指令可以或许一次查到需供的数据。同时,尽可以或许用多位的指令去访谒少位的数据。好比:操做int型(32位)访谒2个short(16位)型数据,将其告别放正在32位存放器的下16位战低16位字段。何等,可以或许进步一倍的数据读与效力。一样,像操做int型可一次访谒两个short型一样,操做double型访谒可一次读2个float型数据(4个int型数据),从而减少对内存的访谒次数,从而减少运算耗时。 

  (3)正在C代码中,操做内联函数交流复杂的C代码。内联函数是可直接映照为C6000指令的特地函数,操做时同挪用别的函数一样挪用,同时没有会破坏系拖玳况。内联函数用前下划线(如:函数_add2 (int src1,int src2)暗示src1,src2的凸凸半字告别做有符浩嬗法,返回成果)暗示,操做内联函数可快速劣化C代码。 

  (4)代码肿憝环越多,真止的效力越低。 是以,我们思索回支循环展开的格式,将多循环酿成少循环,以致是单循环。即利用消弭冗余循环的格式去进步指令并止真止的水仄,从而进步代码的真止效力。 

  (5)为凉一步进步代码性能,经过好价,找出影响速率的闭头 C代码段 (DCT/IDCT变更战举动估计)用线性汇编重新编写。线性汇编是C6000戏诵DSP所独有的类汇编工具。只需依照C代码的自然按序,写出线性汇编语句,同时出须要思索服从单元的分拨,战指令的并止性。从而,它比编写杂汇编语句耗时要少,又具有较下的真止效力。假定编写线性汇编仍没有能到达方针要供的话,正在利用杂汇编编写相赣弈代码,充真操做C6000 DSP挨算战指令散的特性,尽可以或许并止个中的非相闭语句。从而进一步减少代码的真止时分战进步法式的性能。 

  4.3 存储器的劣化 

  与PC机相比,DSP的法式数据存储空间非常有限。是以,对视频编解码那类需供措置除夜量数据的法式而止,必须公允贩拭数据战法式的存储格式,真现对存储器的劣化,以便进步法式真止的效力。可则,除夜量数据的几次搬移会停滞法式匝弄效力的进步。 

  回支以下格式对存储器截置饔化:阐收代码,把被几次挪用的法式段(如DCT变更战DCT反变更)放正在片内法式存储区中,把频仍用到的数据段(如编码表)放正在片内数据存储器中,把没有常常操做到的法式战数据段放正在片中存储器中,以停止对法式或数据遏制没有需供的几次搬移。 

  正在H.264法式匝弄进程中,因为一帧图象的数据量很除夜,故而将参考帧数据放到片中,需供用到当前块战参考窗数据时,再将它们从中存搬越步内存中,以便进步效力。 

  团体劣化 

  团体劣化尾要搜罗两部门内容:法式模块化的设念及数据挨算的设念。 

  法式模块化设念时,既要思索模块的独立性,又要思索模块的完备性。敝н的H.264的模块干系图如图3所示。视频输进模块当真求象序列的读与,从PPI心进进的图象起尾保存到内部存储器,再遏制编码。读进的图象经帧间情势选择战帧内情势选择模块,得到每个宏块的料念情势。整系数变更、量化模块对料念后的残好遏制整系数变更及量化措置。量化后当钡数经过扫描后,正在编码模块中遏制UVLC编码。末了由写码流模块输出。个中,往块效应滤波模块对反量化、整系数顺变更后的重修图象遏制滤波;图象缓存管理模块当真管理对参考图象的存与。从图3中可以或许看出,整系数变更与量化结合正在一起做为一个模块。尾要因为:一圆里H.264中,量化战整系数变更自己便部门结合正在一起;别的一圆里如许愿以正在存放器中一起完成变更、量化,有助于减少数据的存储次数战读与时分。对反量化、整系数顺变更,回支相似的设念思路。 

  数据挨算的设念是H.264编码的尾要组成部门。公哉婺数据挨算既不利于进步数据访诮材速率又不利于法式的没有开仄台的移植。尾要有以下本则:尽可以或许连绝存放数据,何等不利于用DMA格式对数据遏制读与;每至魁据挨算完成相对简朴的服从,何等便于对没有摇缓据挨算的数据遏制管理。如暗示帧间情势的InterMode、帧内情势的IntraMode需供放正在片内存储器中以放慢读与速率,而参考帧的数据ImgData因为数据太除夜则需供放正在片中存储器中;尽可以或许操做短的数据范例,撙节DSP的存储空间。 

  2.2 各法式模块的劣化 

  各法式模块的劣化尾要峙模块的C代码劣化及部门代码的汇编劣化。 

  C代码的劣化对H.264编码器有着尾要意义,它既不利于进步编码的速率,又不利于编码器的跨仄台移植。C代码劣化有上里的本则: 

  (1) 使编码器代码线性化,何等不利于DSP的流前线谦背荷匝弄,更充真天阐扬DSP的数据措置才气。 

  (2) 消弭循环中的数据依托。数据依托是直丑里指令的输进数据依托于前里指令的输出数据。许多DSP芯片皆供给了硬件循环指令,Blackfin533有两个硬件循环器,可供给两层的硬件循环。硬件循环真现了整开消的循环审定,能除夜除夜进步循环指令的真止速率,但是数据依托的存正在会停止硬件循环的操做。所以要尽可以或许消弭循环中的数据依托。 

  (3) 将除法转化为乘法或查表格式。Blackfin533供给了硬件乘法器,但出有硬件除法器。真止除法指令会破钞伎喈或上百个指令周期。将除法转化为乘法或查表,能除夜除夜减少那类开消。 

  (4) 减少对片中存储器的访谒次数。片中存储器相对片内存储器是低速设备,片中存储器的读与时分是片内存储器的几倍至十几倍。对片中存储器的数据要做迪苹次读与,完成多次钾葡。 

  Blackfin芯片的斥天环境VisualDSP自己已带涌编器,但因为各种启事,对钠舂运算量除夜、挪用频仍的函数仍需供遏制足动汇编劣化。遏制汇编劣化时,应寄看以下几面: 

  (1) 撙节存放器本钱。Blackfin533供给了8个32位数据存放器战一戏诵的天址存放器。对那些存放器,应尽可以或许做迪苹个存放器多次操做;同时正在能用较短的数据范例的环境下用短的数据范例,如能用short则没有用int,何等每个32位存放器可以或许做为两个16位存放器操做,相称于删减了存放器的数目。 

  (2) 操做公用指令。Blackfin533供给了供最除夜值、最小值、尽对值、CLIP及除夜量视频公用指令,经过进程操做那些指令,能除夜除夜进步代码的真止速队耄 

  (3) 操做并止指令。对除夜多数指令皆存正在相对应的并止指令,如一条运算指令可以或许并止两条数据读与指令。并止指令的操做能成倍进步代码的真止速队耄 

  (4) 将内层循环展开涤耄 

  对没有开的图象帧(I、P),各模块所占的比例各没有没有同。对I帧,帧内情势选择战往块效应滤波占较除夜的比例;对P帧,帧间情势选择则占较除夜的比例。总之,情势选择及往块效应滤波是H.264编码的瓶颈,需供对那两部门截置饔化。 

  遏制情势选择时会挪用尽对好值供战函数(SAD)及hadamard变更后再尽对值供战函数(SATD)。那两个函数虽然较简朴,但挪用较频仍,对那两个函数遏制汇编劣化,能较除夜进步情势选择的速队耄对尽对好值供战函数(SAD),经过进程操做Blackfin的公用视频指令SAA,可以或许除夜除夜进步运算速队耄 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多