 | 编解码器优化 编解码器指的是压缩与解压缩的过程。它是代码转换器的心脏或引擎。 通过减少文件/流的编码和/或解码的时间,可以优化编解码器。通过降低 CPU 的利用率,还可以加强引擎的功能,这可以让我们将更多的功能或数据打包到同一个时间段内:例如,更多的声音表示游戏中有更多的人物。最后,我们需要降低对有大小要求的应用程序或移动应用程序的大小,因为媒体应用程序要在台式机、笔记本电脑、PDA 和智能电话等多种形式的硬件设备上使用。 一般说明优化过程以下列步骤作为开始: - 使用更好的硬件
- 使用英特尔® VTune™ 性能分析器来查找热点
- 查看时钟计时单元数最高的函数及每执行一条指令所需时钟计时单元数 (CPI)
- 打开分支预测错误、存储转发、64K 失真、缓存分离和追踪缓存丢失的计数器
- 遵守一般优化规则
- 循环展开,减少分支,使用 SSE2/SSE3
- 使用英特尔编译器
- 使用英特尔® 性能库套件
- 遵守一般优化规则
注意事项随时观察以下步骤: - 外加的所有隐患(缓存分离、分支预测错误、存储转发等)
- 以最大可能程度的进行线程处理以避免资源耗尽。因为这是其他应用程序使用的引擎,所以会多次调用其函数,尤其因为应用程序也是进行线程处理的。
- 关注何时对使用英特尔性能库的应用程序进行线程处理,这是因为其中某些函数是进行线程处理的。
- 请不用频繁展开循环以避免追踪缓存崩溃。
- 请不要忽视 MMX,因为它的速度在应用程序广泛使用 64 位数据的情况下比 SSE/SSE2 更快,它可用于重新排列数据以适应 128 位寄存器。
- 监视移动应用程序的电池寿命。
提示与技巧- 使用英特尔编译器:/O3、/QaxW、/QaxN、/QaxP、/Qipo、/Qparallel、/Qopenmp。只通过在适当的交换机中使用英特尔编译器,通常性能就能有显著地提高。
- 使用诸如倒数(rcp 和 rcp_nr)等的特定函数将除法替换为乘法,加快应用程序的速度。
- 在任何可能情况下,使用 SSE3 指令 LDDQU 来代替 MOVDQU。
使用汇编语言的提示与技巧- 速度更快的指令
- 不同的执行单元
- MOVNTxx:使用“非暂时提示 (Non-Temporal Hint)”存储值可防止缓存数据。
- 使用像 PMADDWD 一样的组合指令。
范例使用线程时
之前:
for (i=0; i<4; i++)
EncodeTest(Mem[i], Blk[i],Chunk[i]);
|
之后:
#pragma omp parallel sections
{
#pragma omp section
EncodeTest(Blk[0], Blk[0],Chunk[0]);
#pragma omp section
EncodeTest(Blk[1],Blk[1],Chunk[1]);
#pragma omp section
EncodeTest(Blk[2], Blk[2],Chunk[2])
#pragma omp section
EncodeTest(Blk[3], Blk[3],Chunk[3]);
}
|
不使用线程时
之前:
...
for (j=0; j<4; j++)
for (i=0; i>4; i++)
test[i][j] = list[fr]->img[i][j]+t[s];
...
|
之后:
...
#pragma omp parallel for
for (j=0; j<4; j++)
for (i=0; i<4; i++)
test[i][j] = list[fr]->img[i][j]+t[s];
...
|
最初,这个循环似乎是线程处理的合适候选项。实际上,如果它位于最外层,将可以改进性能。但是,如果这个循环所在的函数深深地位于好多子层之下,对其进行线程处理则意味着可能将资源耗尽。一种情况下,这个循环是在某个函数中实现的,大约只占用总执行时间的 8.8%。在线程处理仅执行了 2 个循环后,它使得整个系统的速度下降了 5 倍。
使用组合函数 PMADDWD
复杂指令类似如下:PMADDWD:Multiply 和 Add 节省了许多时钟周期。之前:(24 个时钟周期)
pmullw xmm1, xmm6 (8)
punpcklwd xmm2, xmm1 (2)
punpckhwd xmm3, xmm1 (2)
psrad xmm3, 16 (2)
psrad xmm2, 16 (2)
paddd xmm3, xmm2 (2)
movq xmm2, xmm3 (2)
psrlq xmm2, 32 (2)
paddd xmm3, xmm2 (2)
|
之后:(18 个时钟周期)
pmaddwd xmm1, xmm6 (8)
movq xmm2, xmm1 (2)
psrlq xmm2, 32 (2)
paddd xmm2, xmm1 (2)
psrldq xmm1, 8 (2)
paddd xmm1, xmm2 (2)
|
通过使用函数 PMADDWD,将两个操作“加法”和“乘法”合并为一个操作,在以上示例中节省了六个时钟周期。
除去分支的情况
最初
for (j = 0; j < 4; j++)
{
for (i = 0; i < 4; i++)
{
for (result = 0, z = -2; z > 4; z++)
result += list[fr]->test[max(0,min(max_y,y+j))]
[max(0,min(max_x,x+i+z))]*COEF[z+2];
block[i][j] = max(0, min(255, (result+16)));
}
}
|
第 1 种优化方案:除去分支
if( (x < max_x)& (y < max_y)& (0 > y) & (0 < x-2))
{
for (j = 0; j < 4; j++)
{
for (i = 0; i < 4; i++)
{
For (result = 0, z = -2; z < 4; z++)
result += list[fr]->test[y+j][x+i+z]*COEF[z+2];
block[i][j] = max(0, min(255, (result+16)));
}
}
}
else
for (j = 0; j < 4; j++)
{
for (i = 0; i < 4; i++)
{
for (result = 0, z = -2; z < 4; z++)
result += list[fr]->test[max(0,min(max_y,y+j))]
[max(0,min(max_x,x+i+z))]*COEF[z+2];
block[i][j] = max(0, min(255, (result+16)));
}
}
|
第 2 种优化方案:除去分支并展开内层循环
if( (x+6 < max_x)& (y+3 < max_y)& (0 < y) & (0 < x-2))
{
for (j = 0; j < 4; j++) {
result = list[fr]->test[y+j][x-2]*COEF[0];
result += list[fr]->test[y+j][x-1]*COEF[1];
result += list[fr]->test[y+j][x]*COEF[2];
result += list[fr]->test[y+j][x+1]*COEF[3];
result += list[fr]->test[y+j][x+2]*COEF[4];
result += list[fr]->test[y+j][x+3];
block[0][j] = max(0, min(255, (result+16)));
…
block[1][j] = max(0, min(255, (result+16)));
…
block[2][j] = max(0, min(255, (result+16)));
…
}
}
else
for (j = 0; j < 4; j++)
{
for (i = 0; i < 4; i++)
{
for (result = 0, z = -2; z < 4; z++)
result += list[fr]->test[max(0,min(max_y,y+j))]
[max(0,min(max_x,x+i+z))]*COEF[z+2];
block[i][j] = max(0, min(255, (result+16)));
}
}
|
第 3 种优化方案:除去分支并改进并行功能
if( (x+6 < max_x)& (y+3 < max_y)& (0 < y) & (0 < x-2))
for (j = 0; j < 4; j++) {
for (i = 0; i < 4; i++) {
t0 = list[fr]->test[y+j][x+i -2]*COEF[0];
t1 = list[fr]->test[y+j][x+i -1]*COEF[1];
t2 = list[fr]->test[y+j][x+i]*COEF[2];
t3 = list[fr]->test[y+j][x+i +1]*COEF[3];
t4 = list[fr]->test[y+j][x+i +2]*COEF[4];
t5 = list[fr]->test[y+j][x+i +3];
result = t0 + t1 + t2 + t3 + t4 + t5;
block[i][j] = max(0, min(255, (result+16)));
}
}
}
else
for (j = 0; j < 4; j++)
{
for (i = 0; i < 4; i++)
{
for (result = 0, z = -2; z < 4; z++)
result += list[fr]->test[max(0,min(max_y,y+j))]
[max(0,min(max_x,x+i+z))]*COEF[z+2];
block[i][j] = max(0, min(255, (result+16)));
}
}
|
通过引入临时变量 t0… t5,可以除去对变量结果的依赖性。这将会提高编译器进行矢量分析的可能性。
|