C 性能挑战:NAL单元包装
从我在过去看到的情况来看,StackOverflow似乎喜欢编程挑战,例如,它得到了几十个响应。这是一个优化挑战:取一个非常简单的函数,看看你是否能想出一个更聪明的方法来实现它 我有一个函数,我一直想进一步优化,但我总是发现我的优化有一些漏洞,导致不正确的输出-一些罕见的特殊情况下,他们失败了。但是,考虑到功能,我一直认为应该可以做得更好 该函数获取一个输入数据流(从熵的角度来看,实际上是随机位),并将其包装到一个NAL单元中。这涉及到放置转义码:00 00、00 00 01、00 00 02或00 00 03的任何字节序列都将替换为00 00 03 XX,其中XX是原始序列的最后一个字节。正如人们可以猜测的那样,考虑到这种序列的可能性,这些数据在每400万字节的输入中只能放置1个。因此,这是一个挑战,人们搜索大量数据,除了极少数情况外,几乎什么也不做。然而,因为“做某事”涉及到插入字节,这使得事情变得有点棘手。当前未优化的代码如下所示: src和dst是指向字节数组的指针,end是指向输入数据末尾的指针C 性能挑战:NAL单元包装,c,performance,optimization,C,Performance,Optimization,从我在过去看到的情况来看,StackOverflow似乎喜欢编程挑战,例如,它得到了几十个响应。这是一个优化挑战:取一个非常简单的函数,看看你是否能想出一个更聪明的方法来实现它 我有一个函数,我一直想进一步优化,但我总是发现我的优化有一些漏洞,导致不正确的输出-一些罕见的特殊情况下,他们失败了。但是,考虑到功能,我一直认为应该可以做得更好 该函数获取一个输入数据流(从熵的角度来看,实际上是随机位),并将其包装到一个NAL单元中。这涉及到放置转义码:00 00、00 00 01、00 00 02或
int i_count = 0;
while( src < end )
{
if( i_count == 2 && *src <= 0x03 )
{
*dst++ = 0x03;
i_count = 0;
}
if( *src == 0 )
i_count++;
else
i_count = 0;
*dst++ = *src++;
}
int i_count=0;
while(src 如果(i_count==2&&*src对代码应用明显的优化:
#define unlikely(x) __builtin_expect((x),0)
while( src < end )
{
const char s = *src++;
if( unlikely(i_count==2 && s<=0x03) )
{
*dst++ = 0x03;
i_count = 0;
}
if( unlikely(s==0) )
i_count++;
else
i_count = 0;
*dst++ = s;
}
#定义不太可能的(x)uu内置的(x),0)
while(src 如果(不太可能)(i_count==2&&sMike F,非常感谢“不太可能”的建议:以下速度比原来的速度快10%左右:
#define unlikely(x) __builtin_expect((x),0)
while( src < end )
{
if( unlikely(i_count == 2) && unlikely(*src <= 0x03) )
{
*dst++ = 0x03;
i_count = 0;
}
if( unlikely(*src == 0) )
i_count++;
else
i_count = 0;
*dst++ = *src++;
}
#定义不太可能的(x)uu内置的(x),0)
while(srcwhile(src 如果((src[0]==0)和&(src[1]==0)和&(src[2]Hmm…像这样的东西怎么样
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
while( likely(src < end) )
{
//Copy non-zero run
int runlen = strlen( src );
if( unlikely(src+runlen >= end) )
{
memcpy( dest, src, end-src );
dest += end-src;
src = end;
break;
}
memcpy( dest, src, runlen );
src += runlen;
dest += runlen;
//Deal with 0 byte
if( unlikely(src[1]==0 && src[2]<=3 && src<=end-3) )
{
*dest++ = 0;
*dest++ = 0;
*dest++ = 3;
*dest++ = *src++;
}
else
{
*dest++ = 0;
src++;
}
}
#定义可能的(x)uu内置期望((x),1)
#定义不可能的(x)\内置的\u预期((x),0)
while(可能(src=end))
{
memcpy(目的地、目的地、目的地);
dest+=端部src;
src=结束;
打破
}
memcpy(目的地、src、runlen);
src+=runlen;
dest+=runlen;
//处理0字节
如果(不太可能)(src[1]==0&&src[2]我先前答案的更快版本:
while (src < end-2)
{
if (src[0] == 0)
{
if (src[1] == 0)
{
if (src[2] <= 3)
{
dst[0] = 0;
dst[1] = 0;
dst[2] = 3;
dst[3] = src[2];
src += 3;
dst += 4;
}
else
{
dst[0] = 0;
dst[1] = 0;
dst[2] = src[2];
src += 3;
dst += 3;
}
}
else
{
dst[0] = 0;
dst[1] = src[1];
src += 2;
dst += 2;
}
}
else
*dst++ = *src++;
}
while (src < end)
*dst++ = *src++;
while(src if(src[2]像这样的测试完全取决于你的编译器和处理器/内存设置。在我的系统上,我改进的版本与Mike F的strlen/memcpy版本的速度完全相同。在我看来,只要你一个字节一个字节地复制,你在访问内存时就会遇到字对齐问题,而这些问题会发生我在拖你的后腿
因此,我建议如下(对于伪代码,很抱歉,我的C/C++基本上被遗忘了):
- 搜索以查找下一个插入点
[搜索算法是个不错的选择,因为它是次线性的(不需要检查每个字节)]
- 块复制未更改的块
[我的理解是GCC和其他优秀的C++编译器可以将 MycPy](/Cord>)直接调用到正确的处理器指令中,得到接近最优的性能[ ]。
- 插入修改过的代码
- 重复,直到完成
这实际上是一个很好的观点——因为它们是字节指针,C编译器必须假设它们可以互相别名,因为char*没有严格的别名规则。我必须尝试一下,看看它是否有帮助。不幸的是,您的更改实际上降低了10%左右的性能(!?).当然,我必须查看反汇编以了解确切的原因。我将猜测它的原因,因为第一个if语句提前终止,所以“s”在循环的最后一行之前几乎不需要加载。在这一点上,有没有办法让C编译器知道src和dst不能互相别名,即使它们是char*指针?你是对的,否则将更改函数的输出;这是不正确的。在char上的Unsigned和signed没有关系。我认为限制这在GCC中是否存在。另外,性能下降是因为's'的签名(不正确)吗?您确定这不是指dst[3]=src[2]令人印象深刻,比我的Upper和Mark Ransom快了40%以上。我不能保证它会给出正确的结果,因为我需要在一个非常大的数据集上进行测试以确保,但初步测试表明它可能是正确的。不,你还没有完成;是的,Mark在主循环之外处理最后3个字节的方法可能更好。是的,它是正确的很高兴合并strlen和memcpy。memccpy在这方面是完美的,但正如我的一位同事所说,你不能依靠libc实现让真正晦涩的函数变得快速(而且,毫不奇怪,它比你的方法慢)。是的,strlcpy也可以做这项工作,同样的反对意见(我甚至不认为它在glibc中)。这里没有更快,可能只是因为if的初始条件仅发生在256个案例中的1个,因此额外分支的好处几乎为零。使用MS VC6运行此操作,100次通过相同的1000000个随机字节,需要0.287秒。我原来的需要0.345秒。你的需要0.442秒。应该提到我有一个AMDx64处理器,这可能也会产生不同。MSVC++编译器与GCC的性能特征也有很大的不同,这可能是造成差异的主要原因。啊,snap,刚刚评论了同样的事情。我要说的是,在strlen/memcpy版本中,它更依赖于您的libc。一个好的libc将提供更好的分辨率结果比这里发布的任何基于字节的天真版本都要糟糕,但糟糕的libc可能会更糟糕。
#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)
while( likely(src < end) )
{
//Copy non-zero run
int runlen = strlen( src );
if( unlikely(src+runlen >= end) )
{
memcpy( dest, src, end-src );
dest += end-src;
src = end;
break;
}
memcpy( dest, src, runlen );
src += runlen;
dest += runlen;
//Deal with 0 byte
if( unlikely(src[1]==0 && src[2]<=3 && src<=end-3) )
{
*dest++ = 0;
*dest++ = 0;
*dest++ = 3;
*dest++ = *src++;
}
else
{
*dest++ = 0;
src++;
}
}
while (src < end-2)
{
if (src[0] == 0)
{
if (src[1] == 0)
{
if (src[2] <= 3)
{
dst[0] = 0;
dst[1] = 0;
dst[2] = 3;
dst[3] = src[2];
src += 3;
dst += 4;
}
else
{
dst[0] = 0;
dst[1] = 0;
dst[2] = src[2];
src += 3;
dst += 3;
}
}
else
{
dst[0] = 0;
dst[1] = src[1];
src += 2;
dst += 2;
}
}
else
*dst++ = *src++;
}
while (src < end)
*dst++ = *src++;