复制数组时的gcc优化

时间:2011-05-08 19:00:21

标签: c optimization compiler-construction assembly

我需要分析一个执行大量数组副本的应用程序,所以我最终分析了这个非常简单的函数:

typedef unsigned char UChar;
void copy_mem(UChar *src, UChar *dst, unsigned int len) {
        UChar *end = src + len;
        while (src < end)
                *dst++ = *src++;
}

我正在使用英特尔VTune进行实际分析,从那里我发现使用gcc -O3和“普通”gcc(4.4)进行编译时存在显着差异。

为了理解原因和方法,我得到了两个汇编的汇编输出。

未经优化的版本就是这个:

.L3:
        movl    8(%ebp), %eax
        movzbl  (%eax), %edx
        movl    12(%ebp), %eax
        movb    %dl, (%eax)
        addl    $1, 12(%ebp)
        addl    $1, 8(%ebp)
.L2:
        movl    8(%ebp), %eax
        cmpl    -4(%ebp), %eax
        jb      .L3
        leave

所以我看到它首先从* src加载dword并将低位字节放入edx,然后将其存储到* dst并更新指针:简单就足够了。

然后我看到了优化版本,我什么都不懂。

编辑here有优化的程序集。

因此我的问题是:gcc在这个函数中可以做什么样的优化?

5 个答案:

答案 0 :(得分:2)

您的未优化函数每字节移动字节数!

如果你首先对长度进行校准,那么你可以一次移动4个字节,其余的1..3个字节会移动。如果可以确保正确的(4字节)内存对齐,则复制功能也应该更快。 并且不需要在堆栈上递增指针,您可以使用寄存器。 所有这些都会大大提高功能的速度。

或使用像memmove这样的专用mem移动功能!

答案 1 :(得分:2)

优化的代码非常混乱,但我可以发现3个循环(靠近L6,L13和L12)。我认为gcc做的是@GJ建议的(我赞成他)。 L6附近的循环每次移动4个字节,而循环#2仅移动一个字节,有时仅在循环#1之后执行。我仍然无法获得循环#3,因为它与循环#2相同。

答案 2 :(得分:1)

优化的类型取决于函数及其属性,如果函数被标记为内联,并且足够小,它将被转换为MOV的循环和展开循环,这比{{REP快。基于1}}的变体(它可以避免寄存器溢出)。对于未知大小,您可以获得REP MOVS系列指令(从最大字长开始,以减少常量大小的循环数量,否则它将使用您复制的数据单元的大小)。

如果SSE被启用,它很可能会使用未展开的未对齐移动(MOVDQU),其中长度允许或循环未对齐移动(如果它将使用时间预取,则不知道,其中的增益取决于块尺寸)如果长度足够大。如果源/ dest正确对齐,它将尝试使用更快对齐的变体。

就目前而言,如果没有内联,那么你可以获得最好的功能MOVSB

答案 3 :(得分:0)

gcc可以生成的最快的x86汇编指令是rep movsd,它一次可以复制4个字节。 memcpy中的标准libc函数<string.h>,以及memcpy的{​​{1}}特殊内联函数以及<string.h>中的许多其他函数可为您提供最快的结果。

答案 4 :(得分:0)

您也可以在此处使用restrict