技术标签: Linux嵌入式系统专栏 memcpy linux 内核
目录
内存非cache区域拷贝速度很慢,严重影响了系统性能,因此采用多种方法进行优化,主要有对齐拷贝、批量拷贝、减少循环次数、NEON拷贝方法。
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的范围内。
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个寄存器。
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的范围内。该值与基址相加得到用于传送的地址。
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个寄存器。
(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。不能用作其他用途。
(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)对于位数更多的结果,需要内存来传递。
PLD为arm预加载执行的宏定义,下面汇编编写的函数中都使用到了。
#if 1
#define PLD(code...) code
#else
#define PLD(code...)
#endif
memcpy_libc为libc的库函数memcpy。
一次循环只拷贝一个字节,可用于对齐拷贝和非对齐拷贝。
void *memcpy_1(void *dest, const void *src, size_t count)
{
char *tmp = dest;
const char *s = src;
while (count--)
*tmp++ = *s++;
return dest;
}
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
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
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
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
(1)一次循环拷贝数据的字节数
(2)地址是否对齐
(3)pld预取对uncache拷贝影响很小
(1)大批量数据拷贝时,应将源地址和目的地址按32字节、64字节或128字节对齐
(2)按32字节、64字节或128字节对齐的数据,使用neon寄存器进行批量拷贝,若无neon寄存器,则使用arm寄存器进行批量拷贝
(3)若拷贝的字节数小于要求的字节数,则使用通用的方法进行拷贝
(4)uncache区域拷贝,预加载pld指令对拷贝速度的提升有限
文章浏览阅读290次,点赞8次,收藏10次。1.背景介绍稀疏编码是一种用于处理稀疏数据的编码技术,其主要应用于信息传输、存储和处理等领域。稀疏数据是指数据中大部分元素为零或近似于零的数据,例如文本、图像、音频、视频等。稀疏编码的核心思想是将稀疏数据表示为非零元素和它们对应的位置信息,从而减少存储空间和计算复杂度。稀疏编码的研究起源于1990年代,随着大数据时代的到来,稀疏编码技术的应用范围和影响力不断扩大。目前,稀疏编码已经成为计算...
文章浏览阅读217次。EasyGBS - GB28181 国标方案安装使用文档下载安装包下载,正式使用需商业授权, 功能一致在线演示在线API架构图EasySIPCMSSIP 中心信令服务, 单节点, 自带一个 Redis Server, 随 EasySIPCMS 自启动, 不需要手动运行EasySIPSMSSIP 流媒体服务, 根..._easygbs-windows-2.6.0-23042316使用文档
文章浏览阅读1.2k次,点赞27次,收藏7次。2023巅峰极客 BabyURL之前AliyunCTF Bypassit I这题考查了这样一条链子:其实就是Jackson的原生反序列化利用今天复现的这题也是大同小异,一起来整一下。_原生jackson 反序列化链子
文章浏览阅读734次,点赞9次,收藏7次。微服务架构简单的说就是将单体应用进一步拆分,拆分成更小的服务,每个服务都是一个可以独立运行的项目。这么多小服务,如何管理他们?(服务治理 注册中心[服务注册 发现 剔除])这么多小服务,他们之间如何通讯?这么多小服务,客户端怎么访问他们?(网关)这么多小服务,一旦出现问题了,应该如何自处理?(容错)这么多小服务,一旦出现问题了,应该如何排错?(链路追踪)对于上面的问题,是任何一个微服务设计者都不能绕过去的,因此大部分的微服务产品都针对每一个问题提供了相应的组件来解决它们。_spring cloud
文章浏览阅读5.9k次,点赞6次,收藏20次。Js实现图片点击切换与轮播图片点击切换<!DOCTYPE html><html> <head> <meta charset="UTF-8"> <title></title> <script type="text/ja..._点击图片进行轮播图切换
文章浏览阅读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月,在比特币飞涨的时代某网站曾经..._使用物联网漏洞的使用者
文章浏览阅读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
文章浏览阅读708次,点赞2次,收藏3次。开放信息抽取(OIE)系统(三)-- 第二代开放信息抽取系统(人工规则, rule-based, 先关系再实体)一.第二代开放信息抽取系统背景 第一代开放信息抽取系统(Open Information Extraction, OIE, learning-based, 自学习, 先抽取实体)通常抽取大量冗余信息,为了消除这些冗余信息,诞生了第二代开放信息抽取系统。二.第二代开放信息抽取系统历史第二代开放信息抽取系统着眼于解决第一代系统的三大问题: 大量非信息性提取(即省略关键信息的提取)、_语义角色增强的关系抽取
文章浏览阅读1.1w次,点赞6次,收藏51次。快速完成网页设计,10个顶尖响应式HTML5网页模板助你一臂之力为了寻找一个优质的网页模板,网页设计师和开发者往往可能会花上大半天的时间。不过幸运的是,现在的网页设计师和开发人员已经开始共享HTML5,Bootstrap和CSS3中的免费网页模板资源。鉴于网站模板的灵活性和强大的功能,现在广大设计师和开发者对html5网站的实际需求日益增长。为了造福大众,Mockplus的小伙伴整理了2018年最..._html欢迎页面
文章浏览阅读282次。原标题:2018全国计算机等级考试调整,一、二级都增加了考试科目全国计算机等级考试将于9月15-17日举行。在备考的最后冲刺阶段,小编为大家整理了今年新公布的全国计算机等级考试调整方案,希望对备考的小伙伴有所帮助,快随小编往下看吧!从2018年3月开始,全国计算机等级考试实施2018版考试大纲,并按新体系开考各个考试级别。具体调整内容如下:一、考试级别及科目1.一级新增“网络安全素质教育”科目(代..._计算机二级增报科目什么意思
文章浏览阅读240次。conan简单使用。_apt install conan