技术标签: 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指令对拷贝速度的提升有限
文章浏览阅读2.9k次,点赞8次,收藏14次。测试主要做什么?这完全都体现在测试流程中,同时测试流程是面试问题中出现频率最高的,这不仅是因为测试流程很重要,而是在面试过程中这短短的半小时到一个小时的时间,通过测试流程就可以判断出应聘者是否合适,故在测试流程中包含了测试工作的核心内容,例如需求分析,测试用例的设计,测试执行,缺陷等重要的过程。..._测试过程管理中包含哪些过程
文章浏览阅读870次,点赞16次,收藏19次。1.背景介绍政府数字化政务是指政府利用数字技术、互联网、大数据、人工智能等新技术手段,对政府政务进行数字化改革,提高政府工作效率,提升政府服务质量的过程。随着人工智能(AI)和机器学习(ML)技术的快速发展,政府数字化政务中的人工智能与机器学习应用也逐渐成为政府改革的重要内容。政府数字化政务的人工智能与机器学习应用涉及多个领域,包括政策决策、政府服务、公共安全、社会治理等。在这些领域,人工...
文章浏览阅读219次,点赞2次,收藏4次。系统主要的用户为用户、管理员,他们的具体权限如下:用户:用户登录后可以对管理员上传的学习视频进行学习。用户可以选择题型进行练习。用户选择小程序提供的考研科目进行相关训练。用户可以进行水平测试,并且查看相关成绩用户可以进行错题集的整理管理员:管理员登录后可管理个人基本信息管理员登录后可管理个人基本信息管理员可以上传、发布考研的相关例题及其分析,并对题型进行管理管理员可以进行查看、搜索考研题目及错题情况。_mysql刷题软件
文章浏览阅读1.4k次。myelipse里有UML1和UML2两种方式,UML2功能更强大,但是两者生成过程差别不大1.建立Test工程,如下图,uml包存放uml类图package com.zz.domain;public class User {private int id;private String name;public int getId() {return id;}public void setId(int..._根据以下java代码画出类图
文章浏览阅读174次。需求:一个topic包含很多个表信息,需要自动根据json字符串中的字段来写入到hive不同的表对应的路径中。发送到Kafka中的数据原本最外层原本没有pkDay和project,只有data和name。因为担心data里面会空值,所以根同事商量,让他们在最外层添加了project和pkDay字段。pkDay字段用于表的自动分区,proejct和name合起来用于自动拼接hive表的名称为 ..._flume拦截器自定义开发 kafka
文章浏览阅读380次。原标题:Java Spring中同时访问多种不同数据库 多样的工作要求,可以使用不同的工作方法,只要能获得结果,就不会徒劳。开发企业应用时我们常常遇到要同时访问多种不同数据库的问题,有时是必须把数据归档到某种数据仓库中,有时是要把数据变更推送到第三方数据库中。使用Spring框架时,使用单一数据库是非常容易的,但如果要同时访问多个数据库的话事件就变得复杂多了。本文以在Spring框架下开发一个Sp..._根据输入的不同连接不同的数据库
文章浏览阅读3.6k次,点赞9次,收藏25次。本案例描述了晶振屏蔽以及开关电源变压器屏蔽对系统稳定工作的影响, 硬件设计时应考虑。_eft电路图
文章浏览阅读1.1k次。对于物料价格的更改,可以采取不同的手段:首先,我们来介绍MR21的方式。 需要说明的是,如果要对某一产品进行价格修改,必须满足的前提条件是: ■ 1、必须对价格生效的物料期间与对应会计期间进行开启; ■ 2、该产品在该物料期间未发生物料移动。执行MR21,例如更改物料1180051689的价格为20000元,系统提示“对于物料1180051689 存在一个当前或未来标准价格”,这是因为已经对该..._mr21 对于物料 zba89121 存在一个当前或未来标准价格
文章浏览阅读7.4k次,点赞3次,收藏13次。[文章导读]联想启天M420是一款商用台式电脑,预装的是win10系统,用户还是喜欢win7系统,该台式机采用的intel 8代i5 8500CPU,在安装安装win7时有很多问题,在安装win7时要在BIOS中“关闭安全启动”和“开启兼容模式”,并且安装过程中usb不能使用,要采用联想win7新机型安装,且默认采用的uefi+gpt模式,要改成legacy+mbr引导,那么联想启天M420台式电..._启天m420刷bios
文章浏览阅读2.7k次,点赞2次,收藏9次。一,为什么要冗余数据互联网数据量很大的业务场景,往往数据库需要进行水平切分来降低单库数据量。水平切分会有一个patition key,通过patition key的查询能..._保证冗余性
文章浏览阅读88次。是时候闭环Java应用了 原创 2016-08-16 张开涛 你曾经因为部署/上线而痛苦吗?你曾经因为要去运维那改配置而烦恼吗?在我接触过的一些部署/上线方式中,曾碰到过以下一些问题:1、程序代码和依赖都是人工上传到服务器,不是通过工具进行部署和发布;2、目录结构没有规范,jar启动时通过-classpath任意指定;3、fat jar,把程序代码、配置文件和依赖jar都打包到一个jar中,改配置..._那么需要把上面的defaultjavatyperesolver类打包到插件中
文章浏览阅读909次。1.得下载一个番茄插件,按alt+g才可以有函数跳转功能。2.不安装番茄插件,按F12也可以有跳转功能。3.进公司的VS工程是D:\sync\build\win路径,.sln才是打开工程的方式,一个是VS2005打开的,一个是VS2013打开的。4.公司库里的线程接口,在CmThreadManager.h 里,这个里面是我们的线程库,可以直接拿来用。CreateUserTaskThre..._番茄助手颜色