Linux内核memcpy的不同实现_libc memcpy-程序员宅基地

技术标签: Linux嵌入式系统专栏  memcpy  linux  内核  

目录

1.概述

2.高级SIMD和浮点寄存器介绍

2.NEON指令

2.1 VLDR

2.2 VLDM

2.3 VSTR

2.4 VSTM

3.ARM架构程序调用寄存器使用规则

3.1.ARM寄存器使用规则

3.2.NEON寄存器使用规则

3.优化代码

3.1.memcpy_libc

3.2.memcpy_1

3.3.memcpy_32

3.4.memcpy_64

3.5.memcpy_gen

3.7.memcpy_neon_128

3.速度测试

3.1.对齐拷贝测试(单位:MiB/s)

3.2.非对齐拷贝测试(单位:MiB/s) 

4.影响拷贝速度的因素

5.结论


1.概述

内存非cache区域拷贝速度很慢,严重影响了系统性能,因此采用多种方法进行优化,主要有对齐拷贝、批量拷贝、减少循环次数、NEON拷贝方法。

2.高级SIMD和浮点寄存器介绍

2.NEON指令

2.1 VLDR

VLDR指令可从内存中将数据加载到扩展寄存器中。

VLDR{<c>}{<q>}{.64} <Dd>, [<Rn> {, #+/-<imm>}]   Encoding T1/A1, immediate form
VLDR{<c>}{<q>}{.64} <Dd>, <label>                Encoding T1/A1, normal literal form
VLDR{<c>}{<q>}{.64} <Dd>, [PC, #+/-<imm>]        Encoding T1/A1, alternative literal form
VLDR{<c>}{<q>}{.32} <Sd>, [<Rn> {, #+/-<imm>}]   Encoding T2/A2, immediate form
VLDR{<c>}{<q>}{.32} <Sd>, <label>                Encoding T2/A2, normal literal form
VLDR{<c>}{<q>}{.32} <Sd>, [PC, #+/-<imm>]        Encoding T2/A2, alternative literal form

 <c>,<q>:是一个可选的条件代码。
.32,.64:是一个可选的数据大小说明符。如果是单精度VFP寄存器,则必须为32;否则必须为64。
Dd:双字(64位)加载的目标寄存器。对于NEON指令,它必须为D寄存器。对于VFP指令,它可以为D或S寄存器。
Sd:单字(32位)加载的目标寄存器。对于NEON指令,它必须为D寄存器。对于VFP指令,它可以为D或S寄存器。
Rn:存放要传送的基址的ARM寄存器,SP可使用。
+/-:偏移量相对于基地址的运算方式,+表示基地址和偏移量相加,-表示基地址和偏移量相减,+可以省略,#0和#-0生成不同的指令。
imm:是一个可选的数值表达式。在汇编时,该表达式的值必须为一个数字常数。 该值必须是4的倍数,并在0 - 1020的范围内。该值与基址相加得到用于传送的地址。
label:要加载数据项的标签。编译器自动计算指令的Align(PC, 4)值到此标签的偏移量。允许的值是4的倍数,在-1020到1020的范围内。

2.2 VLDM

VLDM指令可以将连续内存地址中的数据加载到扩展寄存器中。

VLDM{<mode>}{<c>}{<q>}{.<size>} <Rn>{!}, <list>

<mode>:IA Increment After,连续地址的起始地址在Rn寄存器中,先加载数据,然后Rn寄存器中的地址在增大,这是缺省的方式,DB Decrement Before,连续地址的起始地址在Rn寄存器中,Rn寄存器中的地址先减小,再加载数据
<c>,<q>:是一个可选的条件代码。
<size>:是一个可选的数据大小说明符。取32或64,和<list>中寄存器的位宽一致。
Rn:存放要传送的基址的ARM寄存器,由ARM指令设置,SP可使用。
!:表示Rn寄存器中的内容变化时要将变化的值写入Rn寄存器中。
<list>:加载的扩展寄存器列表。至少包含一个寄存器,如果包含了64位寄存器,最多不超过16个寄存器。

2.3 VSTR

VSTR指令将扩展寄存器中的数据保存到内存中。

VSTR{<c>}{<q>}{.64} <Dd>, [<Rn>{, #+/-<imm>}] Encoding T1/A1
VSTR{<c>}{<q>}{.32} <Sd>, [<Rn>{, #+/-<imm>}] Encoding T2/A2

<c>,<q>:是一个可选的条件代码。
.32,.64:是一个可选的数据大小说明符。如果是单精度VFP寄存器,则必须为32;否则必须为64。
Dd:双字(64位)加载的目标寄存器。对于NEON指令,它必须为D寄存器。对于VFP指令,它可以为D或S寄存器。
Sd:但字(32位)加载的目标寄存器。对于NEON指令,它必须为D寄存器。对于VFP指令,它可以为D或S寄存器。
Rn:存放要传送的基址的ARM寄存器,SP可使用。
+/-:偏移量相对于基地址的运算方式,+表示基地址和偏移量相加,-表示基地址和偏移量相减,+可以省略,#0和#-0生成不同的指令。
imm:是一个可选的数值表达式。在汇编时,该表达式的值必须为一个数字常数。 该值必须是4的倍数,并在0 - 1020的范围内。该值与基址相加得到用于传送的地址。

2.4 VSTM

VSTM指令可将扩展寄存器列表中的数据保存到连续的内存中。

<mode>:IA Increment After,连续地址的起始地址在Rn寄存器中,先保存数据,然后Rn寄存器中的地址在增大,这是缺省的方式,DB Decrement Before,连续地址的起始地址在Rn寄存器中,Rn寄存器中的地址先减小,再保存数据
<c>,<q>:是一个可选的条件代码。
<size>:是一个可选的数据大小说明符。取32或64,和<list>中寄存器的位宽一致。
Rn:存放要传送的基址的ARM寄存器,由ARM指令设置,SP可使用。
!:表示Rn寄存器中的内容变化时要将变化的值写入Rn寄存器中。
<list>:保存的扩展寄存器列表。至少包含一个寄存器,如果包含了64位寄存器,最多不超过16个寄存器。

3.ARM架构程序调用寄存器使用规则

3.1.ARM寄存器使用规则

(1)子程序间通过寄存器R0-R3传递参数,被调用的子程序在返回前无须恢复寄存器R0-R3的内容,参数多余4个时,使用栈传递,参数入栈的顺序与参数顺序相反。
(2)在子程序中,使用寄存器R4-R11保存局部变量,如果子程序中使用了R4-R11的寄存器,则必须在使用之前保存这些寄存器,子程序返回之前恢复这些寄存器,在子程序中没有使用这些寄存器,则无须保存。
(3)寄存器R12用作子程序间的scratch寄存器,记作IP,在子程序间的链接代码段中常有这种规则。
(4)寄存器R13用作数据栈指针,记作SP。在子程序中,寄存器R13不能用作其它用途。
(5)寄存器R14用作连接寄存器,记作IR。用于保存子程序的返回地址,如果在子程序中保存了返回地址,寄存器R14可用于其他用途。
(6)寄存器R15是程序计数器,记作PC。不能用作其他用途。

3.2.NEON寄存器使用规则

(1)NEON S16-S31(D8-D15,Q4-Q7)寄存器在子程序中必须保存,S0-S15(D0-D7,Q4-Q7)和Q8-Q15在子程序中无须保存

3.3.子程序返回寄存器使用规则
(1)结果为一个32位整数时,可以通过寄存器R0返回。
(2)结果为一个64位整数时,可通过寄存器R0和R1返回,依次类推。
(3)结果为一个浮点数时,可以通过浮点运算部件的寄存器F0、D0或者S0来返回。
(4)结果为复合型的浮点数时,可以通过寄存器F0-Fn或者D0-Dn返回。
(5)对于位数更多的结果,需要内存来传递。

3.优化代码

PLD为arm预加载执行的宏定义,下面汇编编写的函数中都使用到了。

    #if 1
    #define PLD(code...)	code
    #else
    #define PLD(code...)
    #endif

3.1.memcpy_libc

memcpy_libc为libc的库函数memcpy。

3.2.memcpy_1

一次循环只拷贝一个字节,可用于对齐拷贝和非对齐拷贝。

    void *memcpy_1(void *dest, const void *src, size_t count)
    {
        char *tmp = dest;
        const char *s = src;

        while (count--)
            *tmp++ = *s++;
        return dest;
    }

3.3.memcpy_32

memcpy_32一次循环拷贝32字节,适用于32字节对齐拷贝。

    @ void* memcpy_32(void*, const void*, size_t);
    @ 声明符号为全局作用域
    .global memcpy_32
    @ 4字节对齐
    .align 4
    @ 声明memcpy_32类型为函数
    .type memcpy_32, %function
    memcpy_32:
        @ r4-r12, lr寄存器入栈
        push        {r4-r11, lr}
        @ memcpy的返回值为目标存储区域的首地址,即第一个参数值,将返回值保存到r3寄存器中
        mov         r3, r0
    0:
        @ 数据预取指令,会将数据提前加载到cache中
        PLD( pld  [r1, #128] )
        @ r2为memcpy的第3个参数,表示拷贝多少个字节数,首先减去32,
        @ s:决定指令的操作是否影响CPSR的值
        subs        r2, r2, #32
        @ r1为memcpy的第2个参数,表示源数据的地址,将源地址中的数据加载到r4-r11寄存器中
        @ 每加载一个寄存器,r1中的地址增加4,总共8个寄存器,共32字节数据
        ldmia       r1!, {r4-r11}
        @ 将r4-r11中保存的源数据加载到r1寄存器指向的内存地址,每加载一个寄存器,r1指向的地址加4
        stmia       r0!, {r4-r11}
        @stmia       r0!, {r8-r11}
        @ gt为条件码,带符号数大于,Z=0且N=V
        bgt         0b
        @ 函数退出时将返回值保存到r0中
        mov         r0, r3
        pop         {r4-r11, pc}
    .type memcpy_32, %function
    @函数体的大小,.-memcpy_32中的.代表当前指令的地址,
    @即点.减去标号memcpy_32,标号代表当前指令的地址
    .size memcpy_32, .-memcpy_32

3.4.memcpy_64

memcpy_64一次循环拷贝64字节,适用于64字节对齐拷贝。

    @ void* memcpy_64(void*, const void*, size_t);
    .global memcpy_64
    .align 4
    .type memcpy_64, %function
    memcpy_64:
        push        {r4-r11, lr}
        mov         r3, r0
    0:
        PLD( pld  [r1, #256] )
        subs        r2, r2, #64
        ldmia       r1!, {r4-r11}
        PLD( pld  [r1, #256] )
        stmia       r0!, {r4-r7}
        stmia       r0!, {r8-r11}
        ldmia       r1!, {r4-r11}
        stmia       r0!, {r4-r7}
        stmia       r0!, {r8-r11}
        bgt         0b
        mov         r0, r3
        pop         {r4-r11, pc}
    .type memcpy_64, %function
    .size memcpy_64, .-memcpy_64

3.5.memcpy_gen

        memcpy_gen是通用的内存拷贝函数,可根据源地址和目的地址是否对齐,采用不同的拷贝方法,适用于对齐和非对齐拷贝。此代码参考自Linux内核。
(1)判断拷贝的字节数是否小于4字节,若小于4字节,则直接进行单字节拷贝
(2)判断目的地址是否时按4字节对齐,若没有,则进入目的地址未对齐的处理逻辑
(3)判断源地址是否按4字节对齐,若没有,则进入源地址未对齐的处理逻辑
(4)若目的地址和源地址按4字节对齐,则进入目的地址和源地址对齐的处理逻辑
目的地址和源地址对齐的处理逻辑:
(1)若拷贝的字节数大于等于32字节,则将超过32字节的数据进行批量拷贝,每次拷贝32字节
(2)若剩下的数据小于32字节,则进行4字节拷贝
(3)若剩下的数据小于4字节,则进行单字节拷贝
目的地址未对齐的处理逻辑:
(1)先将未对齐的字节进行单字节拷贝,使目的地址按4字节对齐
(2)若剩余的数据小于4字节,则进行单字节拷贝
(3)此时若源地址也按4字节对齐,则进入目的地址和源地址对齐的处理逻辑
(4)若源地址未按4字节对齐,则进入源地址未对齐的处理逻辑
源地址未对齐的处理逻辑:
(1)将源地址中的数据加载的寄存器中,进行逻辑移位
(2)将低地址的源数据逻辑左移,移到寄存器的低位,将高地址的数据逻辑右移,移到寄存器的高位
(3)将两个寄存器的数据进行或操作,实现了低地址和高地址数据在寄存器中的拼接
(4)将拼接的数据加载到目的地址中

	#define LDR1W_SHIFT	0
	#define STR1W_SHIFT	0
	#if __BYTE_ORDER == __BIG_ENDIAN  
	// 大端
	#define lspull          lsl
	#define lspush          lsr
	#elif __BYTE_ORDER == __LITTLE_ENDIAN 
	// 小端,一般都是小端
	#define lspull          lsr
	#define lspush          lsl
	#else
	#error "unknow byte order"
	#endif
	
		.macro ldr1w ptr reg abort
		ldr \reg, [\ptr], #4
		.endm
	
		.macro ldr4w ptr reg1 reg2 reg3 reg4 abort
		ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4}
		.endm
	
		.macro ldr8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
		ldmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8}
		.endm
	
		.macro ldr1b ptr reg cond=al abort
		ldr\cond\()b \reg, [\ptr], #1
		.endm
	
		.macro str1w ptr reg abort
		str \reg, [\ptr], #4
		.endm
	
		.macro str8w ptr reg1 reg2 reg3 reg4 reg5 reg6 reg7 reg8 abort
		stmia \ptr!, {\reg1, \reg2, \reg3, \reg4, \reg5, \reg6, \reg7, \reg8}
		.endm
	
		.macro str1b ptr reg cond=al abort
		str\cond\()b \reg, [\ptr], #1
		.endm
	
		.macro enter reg1 reg2
		stmdb sp!, {r0, \reg1, \reg2}
		.endm
	
		.macro exit reg1 reg2
		ldmfd sp!, {r0, \reg1, \reg2}
		.endm
	
	@ void* memcpy_gen(void*, const void*, size_t);
	@ 声明符号为全局作用域
	.global memcpy_gen
	@ 4字节对齐
	.align 4
	@ 声明memcpy_gen类型为函数
	.type memcpy_gen, %function
	memcpy_gen:
		@ 将r0 r4 lr寄存器保存到栈中,r0是函数的返回值
		enter	r4, lr
	    @ 拷贝的字节数减4并保存到r2中,运算结果影响CPSR中的标志位
		subs	r2, r2, #4
	    @ blt:带符号数小于
	    @ 如果拷贝的字节数小于4,则跳转到标号8处
		blt	8f
		@ r0保存的是目的地址,r0和3与,判断r0的低两位是否是0,不是0,则是未对齐的地址
	    @ s:决定指令的操作是否影响CPSR的值
		ands	ip, r0, #3  @ 测试目的地址是否是按4字节对齐
		PLD( pld  [r1, #0] )
		bne	9f  @ 目的地址没有按4字节对齐,则跳转到标号9处
		ands	ip, r1, #3  @ 测试源地址是否是按4字节对齐
		bne	10f  @ 源地址没有按4字节对齐,则跳转到标号10处
	
	1:	subs	r2, r2, #(28)
		stmfd	sp!, {r5 - r8}
		blt	5f
		PLD( pld  [r1, #0] )
	
	2:	subs	r2, r2, #96
		PLD( pld  [r1, #28] )
		blt	4f
		PLD( pld  [r1, #60] )
		PLD( pld  [r1, #92] )
	
	3:	PLD( pld  [r1, #124] )
	4:	ldr8w	r1, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f
		subs	r2, r2, #32
		str8w	r0, r3, r4, r5, r6, r7, r8, ip, lr, abort=20f
		bge	3b
		cmn	r2, #96
		bge	4b
	
	5:	ands	ip, r2, #28
		rsb	ip, ip, #32
	#if LDR1W_SHIFT > 0
			lsl	ip, ip, #LDR1W_SHIFT
	#endif
		addne	pc, pc, ip		@ C is always clear here
		b	7f
	6:
	    @ .rept:重复定义伪操作, 格式如下:
	    @ .rept     重复次数
	    @ 数据定义
	    @ .endr     结束重复定义
	    @ 例如:
	    @  .rept 3
	    @  .byte 0x23
	    @  .endr
		.rept	(1 << LDR1W_SHIFT)
		nop
		.endr
	
		ldr1w	r1, r3, abort=20f
		ldr1w	r1, r4, abort=20f
		ldr1w	r1, r5, abort=20f
		ldr1w	r1, r6, abort=20f
		ldr1w	r1, r7, abort=20f
		ldr1w	r1, r8, abort=20f
		ldr1w	r1, lr, abort=20f
	
	#if LDR1W_SHIFT < STR1W_SHIFT
			lsl	ip, ip, #STR1W_SHIFT - LDR1W_SHIFT
	#elif LDR1W_SHIFT > STR1W_SHIFT
			lsr	ip, ip, #LDR1W_SHIFT - STR1W_SHIFT
	#endif
		add	pc, pc, ip
		nop
		.rept	(1 << STR1W_SHIFT)
		nop
		.endr
	
		str1w	r0, r3, abort=20f
		str1w	r0, r4, abort=20f
		str1w	r0, r5, abort=20f
		str1w	r0, r6, abort=20f
		str1w	r0, r7, abort=20f
		str1w	r0, r8, abort=20f
		str1w	r0, lr, abort=20f
	
	7:	ldmfd	sp!, {r5 - r8}
	
	8:	@ 处理拷贝的字节数小于4字节,此时r2中的值为负数,可能取值为-1 -2 -3
		@ lsl 逻辑左移,将r2左移31位保存带r2中
		movs	r2, r2, lsl #31
		@ ne 不相等,Z=0,r2为-1或-3时拷贝数据
		ldr1b	r1, r3, ne, abort=21f
		@ cs 无符号数大于等于,C=1
		@ 对于包含移位操作的非加/减法运算指令,C中包含最后一次被溢出的位的数值
		ldr1b	r1, r4, cs, abort=21f
		ldr1b	r1, ip, cs, abort=21f
		str1b	r0, r3, ne, abort=21f
		str1b	r0, r4, cs, abort=21f
		str1b	r0, ip, cs, abort=21f
		exit	r4, pc
	
	9:  @ 处理目的地址未按4字节对齐的情况
	
		@ rsb 逆向减法指令,等价于ip=4-ip,同时根据操作结果更新CPSR中相应的条件标志
		@ 当rsb的运算结果为负数时,N=1,为正数或0时,N=0
		@ 当rsb的运算结果符号位溢出时,V=1
		rsb	ip, ip, #4  @ 当地址不按4字节对齐的时候,ip的值取值可能为1、2、3
		@ 对于cmp指令,Z=1表示进行比较的两个数大小相等
		cmp	ip, #2  @ cmp影响
		@ gt:带符号数大于,Z=0且N=V
		ldr1b	r1, r3, gt, abort=21f  @ ip为3时将数据加载到r3寄存器中,同时r1=r1+1
		@ ge:带符号数大于等于,N=1且V=1或N=0且V=0
		ldr1b	r1, r4, ge, abort=21f  @ ip为2时将数据加载到r4寄存器中,同时r1=r1+1
		ldr1b	r1, lr, abort=21f      @ ip为1时将数据加载到lr寄存器中,同时r1=r1+1
		str1b	r0, r3, gt, abort=21f
		str1b	r0, r4, ge, abort=21f
		subs	r2, r2, ip  @ 更新拷贝的字节数
		str1b	r0, lr, abort=21f
		@ 带符号数小于,N=1且V=0或N=0且V=1
		blt	8b
		ands	ip, r1, #3  @ 测试源地址是否是4字节对齐的情况
		@ eq 相等,Z=1,r1中的地址已按4字节对齐,则跳转到标号1处,否则继续向下执行
		beq	1b
	
	10:	@ 处理源地址未按4字节对齐的情况
	
		@ bic指令用于清除操作数1的某些位,并把结果放置到目的寄存器中
		@ 将r1的低2位清0
		bic	r1, r1, #3
		@ ip保存了r1的低两位,源地址的低2位与2比较
		cmp	ip, #2
		@ 无条件执行,将r1寄存器指向的4字节数据加载到lr寄存器中,拷贝完r1=r1+4
		ldr1w	r1, lr, abort=21f
		@ eq 相等,Z=1
		@ ip寄存器和2相等
		beq	17f
		@ gt:带符号数大于,Z=0且N=V
		@ ip寄存器大于2
		bgt	18f
	
		.macro	forward_copy_shift pull push
		@ r2寄存器减去28
		subs	r2, r2, #28
		@ 带符号数小于,N=1且V=0或N=0且V=1,r2小于28跳转到14处
		blt	14f
	11:	@ 保存r5-r9寄存器,同时更新sp寄存器,fd:满递减
		stmfd	sp!, {r5 - r9}
		PLD( pld  [r1, #0] )
		subs	r2, r2, #96  @ r2减去96
		PLD( pld  [r1, #28] )
		blt	13f  @ 带符号数小于,N=1且V=0或N=0且V=1,r2小于96跳转到13处
		PLD( pld  [r1, #60] )
		PLD( pld  [r1, #92] )
	
	12:	PLD( pld  [r1, #124] )
	13:	@ lspull(小端) = lsr:逻辑右移
		@ lspush(小端) = lsr:逻辑左移
		ldr4w	r1, r4, r5, r6, r7, abort=19f
		@ lr逻辑右移pull位并存储到r3寄存器中
		mov	r3, lr, lspull #\pull
		subs	r2, r2, #32
		ldr4w	r1, r8, r9, ip, lr, abort=19f
		@ r4逻辑左移push位,然后和r3或,最后将结果保存到r3中
		@ 将r4的push位保存到r3的(32-push)位
		orr	r3, r3, r4, lspush #\push
		mov	r4, r4, lspull #\pull
		orr	r4, r4, r5, lspush #\push
		mov	r5, r5, lspull #\pull
		orr	r5, r5, r6, lspush #\push
		mov	r6, r6, lspull #\pull
		orr	r6, r6, r7, lspush #\push
		mov	r7, r7, lspull #\pull
		orr	r7, r7, r8, lspush #\push
		mov	r8, r8, lspull #\pull
		orr	r8, r8, r9, lspush #\push
		mov	r9, r9, lspull #\pull
		orr	r9, r9, ip, lspush #\push
		mov	ip, ip, lspull #\pull
		orr	ip, ip, lr, lspush #\push
		str8w	r0, r3, r4, r5, r6, r7, r8, r9, ip, , abort=19f
		bge	12b
		cmn	r2, #96
		bge	13b
		ldmfd	sp!, {r5 - r9}
	
	14:	ands	ip, r2, #28
		beq	16f
	
	15:	mov	r3, lr, lspull #\pull
		ldr1w	r1, lr, abort=21f
		subs	ip, ip, #4
		orr	r3, r3, lr, lspush #\push
		str1w	r0, r3, abort=21f
		bgt	15b
	
	16:	sub	r1, r1, #(\push / 8)
		b	8b
		.endm
	
		forward_copy_shift	pull=8	push=24
	
	17:	forward_copy_shift	pull=16	push=16
	
	18:	forward_copy_shift	pull=24	push=8
	
		.macro	copy_abort_preamble
	19:	ldmfd	sp!, {r5 - r9}
		b	21f
	20:	ldmfd	sp!, {r5 - r8}
	21:
		.endm
	
		.macro	copy_abort_end
		ldmfd	sp!, {r4, pc}
		.endm
	
	.type memcpy_gen, %function
	 @函数体的大小,.-memcpy_gen中的.代表当前指令的地址,
	 @即点.减去标号memcpy_gen,标号代表当前指令的地址
	.size memcpy_gen, .-memcpy_gen

3.7.memcpy_neon_128

memcpy_neon_128使用NEON寄存器进行加速拷贝,一次拷贝128字节,适用于128字节的对齐拷贝。

    @ void* memcpy_neon_128(void*, const void*, size_t);
    @ 声明符号为全局作用域
    .global memcpy_neon_128
    @ 4字节对齐
    .align 4
    @ 声明memcpy_neno类型为函数
    .type memcpy_neon_128, %function
    memcpy_neon_128:
        @ 保存neon寄存器
        vpush.64 {d8-d15}
        @ 保存返回值
        mov		r3, r0
    0:
        PLD( pld  [r1, #512] )	
        subs    r2,  r2, #128
        vldmia.64  r1!,  {d0-d15}
        vstmia.64  r0!,  {d0-d15}
        bgt     0b
        @ 函数退出时将返回值保存到r0中
        mov		r0, r3
        vpop.64 {d8-d15}
        @ 将函数的返回地址保存搭配pc中,退出函数
        mov     pc, lr 
    .type memcpy_neon_128, %function
    @函数体的大小,.-memcpy_neon_128中的.代表当前指令的地址,
    @即点.减去标号memcpy_neon_128,标号代表当前指令的地址
    .size memcpy_neon_128, .-memcpy_neon_128

3.速度测试

3.1.对齐拷贝测试(单位:MiB/s)

3.2.非对齐拷贝测试(单位:MiB/s) 

4.影响拷贝速度的因素

(1)一次循环拷贝数据的字节数
(2)地址是否对齐
(3)pld预取对uncache拷贝影响很小

5.结论

(1)大批量数据拷贝时,应将源地址和目的地址按32字节、64字节或128字节对齐
(2)按32字节、64字节或128字节对齐的数据,使用neon寄存器进行批量拷贝,若无neon寄存器,则使用arm寄存器进行批量拷贝
(3)若拷贝的字节数小于要求的字节数,则使用通用的方法进行拷贝
(4)uncache区域拷贝,预加载pld指令对拷贝速度的提升有限

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/smartvxworks/article/details/125621901

智能推荐

稀疏编码的数学基础与理论分析-程序员宅基地

文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...

EasyGBS国标流媒体服务器GB28181国标方案安装使用文档-程序员宅基地

文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档

【Web】记录巅峰极客2023 BabyURL题目复现——Jackson原生链_原生jackson 反序列化链子-程序员宅基地

文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子

一文搞懂SpringCloud,详解干货,做好笔记_spring cloud-程序员宅基地

文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud

Js实现图片点击切换与轮播-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换

tensorflow-gpu版本安装教程(过程详细)_tensorflow gpu版本安装-程序员宅基地

文章浏览阅读10w+次,点赞245次,收藏1.5k次。在开始安装前,如果你的电脑装过tensorflow,请先把他们卸载干净,包括依赖的包(tensorflow-estimator、tensorboard、tensorflow、keras-applications、keras-preprocessing),不然后续安装了tensorflow-gpu可能会出现找不到cuda的问题。cuda、cudnn。..._tensorflow gpu版本安装

随便推点

物联网时代 权限滥用漏洞的攻击及防御-程序员宅基地

文章浏览阅读243次。0x00 简介权限滥用漏洞一般归类于逻辑问题,是指服务端功能开放过多或权限限制不严格,导致攻击者可以通过直接或间接调用的方式达到攻击效果。随着物联网时代的到来,这种漏洞已经屡见不鲜,各种漏洞组合利用也是千奇百怪、五花八门,这里总结漏洞是为了更好地应对和预防,如有不妥之处还请业内人士多多指教。0x01 背景2014年4月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者

Visual Odometry and Depth Calculation--Epipolar Geometry--Direct Method--PnP_normalized plane coordinates-程序员宅基地

文章浏览阅读786次。A. Epipolar geometry and triangulationThe epipolar geometry mainly adopts the feature point method, such as SIFT, SURF and ORB, etc. to obtain the feature points corresponding to two frames of images. As shown in Figure 1, let the first image be ​ and th_normalized plane coordinates

开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先抽取关系)_语义角色增强的关系抽取-程序员宅基地

文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景​ 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取

10个顶尖响应式HTML5网页_html欢迎页面-程序员宅基地

文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面

计算机二级 考试科目,2018全国计算机等级考试调整,一、二级都增加了考试科目...-程序员宅基地

文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思

conan简单使用_apt install conan-程序员宅基地

文章浏览阅读240次。conan简单使用。_apt install conan