asm函数strlen
以char - Array的形式接收到字符串的链接。为此,该函数可以在通用寄存器上使用SWAR,但不使用xmm寄存器或SSE指令。
函数检查位操作:(v - 0x01010101) & ~(v & 0x80808080)
以8字节为步长,如果字包含零字节,则表示字符串的结束。如果是,则逐字节迭代,直到零,以避免页面错误。
对齐的工作方式如下GNU Libc implementation:
for (char_ptr = str; ((unsigned long int) char_ptr & (sizeof (longword) - 1)) != 0; ++char_ptr){
if (*char_ptr == '\0'){
return char_ptr - str;
}
}
有什么办法可以让它更快吗?
; rdi <- const *char
; rax <- counter + return value
; r10 <- current array for computation
; rcx,r8 <- Bitmask
; rsi <- Arr for calculation
strlen:
PUSH rbp
SUB rsp,8
XOR rax,rax
MOV r8,31
alignment:
CMP byte [rdi+rax],0
JE end
MOV rsi,rdi
ADD rsi,rax
AND rsi,r8
CMP rsi,0
JE while_parallel
INC rax
JMP alignment
while_parallel:
MOV rcx,0x01010101
MOV r8,0x80808080
while_parallel_loop:
MOV r10,[rdi+rax]
MOV rsi,r10
NOT r10
AND r10,r8
SUB rsi,rcx
AND rsi,r10
CMP rsi,0
JNE while_single
ADD rax,8
JMP while_parallel_loop
while_single:
CMP byte [rdi+rax],0
JE end
INC rax
JMP while_single
end:
ADD rsp,8
POP rbp
RET
请注意,我不打算使用任何SSE指令或xmm寄存器。
1条答案
按热度按时间9vw9lbht1#
问题中的代码似乎有一个缺陷:Mycroft的魔法常数还没有扩展到64位。关于效率:各种x86-64调用约定主要是基于寄存器的,因此不必为简单函数维护堆栈帧。发布的代码中的主循环似乎有点过于复杂;注意,大多数x86指令设置可用于分支的标志。达到下一个
QWORD
边界的逐字节处理可能会受益于完全展开。下面是为Windows编写的x86-64代码,其中包含了这些建议。将其调整到Linux使用的System V ABI只需要循环交换寄存器。请注意,在具有BMI指令集扩展的CPU上,通过将
not rcx
与以下and rcx, r9
合并为andn rcx, rcx, r9
,可以将64位处理循环减少一条指令。我使用下面的ISO-C99测试脚手架来检查上面的
strglen
实现的正确性。我使用Microsoft Macro Assembler和Intel C/C++编译器构建,如下所示:ml64 -c strglen.asm
、icl /Qstd=c99 /Ox /W4 strlen.c strglen.obj
。我还用dumpbin /disasm strglen.obj
检查了生成的机器代码,主要是为了确保对齐指令按预期工作,以及通过REPT
宏展开的循环正确工作,因为我在过去的20年里没有使用过它。