You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于通用寄存器SWAR的SIMD strlen函数页错误问题求助

SWAR实现的汇编strlen页错误问题解决

我实现了一个基于通用寄存器SWAR技术(不使用XMM寄存器或SSE指令)的汇编strlen函数,接收字符数组形式的字符串指针。它以8字节为步长通过位操作检测字符串结束符0,检测到可能包含结束符时转为逐字节检测。逻辑可行,但运行时出现页错误异常,即便最后阶段是逐字节迭代的。

原汇编代码:

; rdi <- const *char
        ; rax <- counter + return value
        ; rbx <- current array
        ; rcx <- Bitmask
        ; rsi <- Arr for calculation
       
    strlen:
        PUSH rbx
        PUSH rbp
        MOV  rbp,rsp
        SUB rsp,8      ; calling convention
    
        XOR rax,rax
        MOV rcx,0x7F7F7F7F7F7F7F7F
    
    while_parallel: ;checks if next 8 byte contains end - zero 
        MOV rbx,[rdi+rax]
        MOV rsi,rbx
    
        AND rsi,rcx
        ADD rsi,rcx
        OR  rsi,rbx
        OR  rsi,rcx
        NOT rsi
    
        CMP rsi, qword 0
        JNE while_single
        ADD rax,8
        JMP while_parallel
    
    while_single:  ;check if next byte is zero
        CMP [rdi+rax],byte 0
        JE  end
        INC rax
        JMP while_single
   end:
    ADD rsp,8
    POP rbp
    POP rbx

   RET

注:不能使用任何SSE指令或XMM寄存器。


问题根源

页错误的核心原因是并行检测阶段直接读取8字节数据,而字符串末尾可能跨页:比如字符串最后几个字节在当前页的末尾,下一个字节属于未映射的内存页,此时一次性读取8字节就会触发页错误——哪怕逐字节读取时只会访问到有效内存,不会碰无效页。

另外原SWAR检测逻辑存在错误,可能漏检零字节,导致循环继续读取越界内存,进一步加剧页错误概率。

解决方案

  1. 添加页边界检查:每次读取8字节前,先计算当前位置到4KB页末尾的剩余字节数,若不足8字节则直接切换到逐字节检测,避免跨页读取无效内存。
  2. 修正SWAR零字节检测逻辑:使用行业通用的可靠检测公式,确保准确识别包含零字节的8块数据。

修正后的汇编代码

; rdi <- const char*  输入字符串指针
; rax <- 返回值       字符串长度
strlen:
    PUSH rbx
    PUSH rbp
    MOV  rbp, rsp
    SUB  rsp, 8        ; 符合System V AMD64调用约定的栈对齐要求

    XOR  rax, rax
    MOV  rcx, 0x0101010101010101  ; SWAR检测用掩码1
    MOV  rdx, 0x8080808080808080  ; SWAR检测用掩码2

while_parallel:
    ; 计算当前位置到4KB页末尾的剩余字节数
    MOV  rbx, rdi
    ADD  rbx, rax
    AND  rbx, 0xFF8               ; 取当前地址的页内偏移(4KB页大小)
    MOV  rsi, 0x1000
    SUB  rsi, rbx                 ; rsi = 页内剩余字节数
    CMP  rsi, 8
    JB   while_single             ; 剩余不足8字节,直接逐字节检测

    ; SWAR方式检测8字节块中是否存在零字节
    MOV  rbx, [rdi + rax]
    MOV  rsi, rbx
    SUB  rsi, rcx                 ; 每个字节减0x01,零字节会溢出为0xFF
    NOT  rbx
    AND  rsi, rbx                 ; (b-1) & ~b ,仅当b=0时结果为0xFF
    AND  rsi, rdx                 ; 仅保留每个字节的最高位,非零则存在零字节
    CMP  rsi, 0
    JNE  while_single             ; 检测到零字节,切换到逐字节定位

    ADD  rax, 8
    JMP  while_parallel

while_single:
    ; 逐字节检测结束符
    CMP  byte [rdi + rax], 0
    JE   end
    INC  rax
    JMP  while_single

end:
    ADD  rsp, 8
    POP  rbp
    POP  rbx
    RET

说明

  • 页边界检查基于4KB标准页大小,若系统使用其他页大小,需调整0xFF8和0x1000对应的值。
  • 修正后的SWAR检测逻辑通过(val - 0x0101010101010101) & ~val & 0x8080808080808080判断零字节,是通用且可靠的无SIMD零检测方案。

内容的提问来源于stack exchange,提问作者HeapUnderStop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 05:27:47