如何解决尽可能快地将整数转换为汇编中的字符串64 位,NASM
我在 64 位 NASM 中编写了一个 printint 函数,它将一个整数打印到 STDOUT。不过它真的很慢,在做了一些基准测试之后,我确定将整数转换为字符串是迄今为止最慢的部分。
我目前将整数转换为字符串的策略如下:
- 如果数字为 0,则跳到特殊情况。
- 如果数字是负数,打印一个负号,把它变成正整数,然后继续
- 创建一个 10 字节的缓冲区(足以容纳所有 32 位整数)和一个指向缓冲区后面的指针
- 检查数字是否为0;如果是,我们就完成了。
- 将数字除以 10,将余数转换为 ASCII
- 将剩余部分放入缓冲区(从后到前)
- 递减缓冲区指针
- 循环回到第 4 步
我试过谷歌搜索其他人是如何做的,它或多或少与我所做的相似,除以 10 直到数字为 0。
相关代码如下:
printint: ; num in edi
push rbp ; save base pointer
mov rbp,rsp ; place base pointer on stack
sub rsp,20 ; align stack to keep 20 bytes for buffering
cmp edi,0 ; compare num to 0
je _printint_zero ; 0 is special case
cmp edi,0
jg _printint_pos ; don't print negative sign if positive
; print a negative sign (code not relevant)
xor edi,-1 ; convert into positive integer
add edi,1
_printint_pos:
mov rbx,rsp ; set rbx to point to the end of the buffer
add rbx,17
mov qword [rsp+8],0 ; clear the buffer
mov word [rsp+16],0 ; 10 bytes from [8,18)
_printint_loop:
cmp edi,0 ; compare edi to 0
je _printint_done ; if edi == 0 then we are done
xor edx,edx ; prepare eax and edx for division
mov eax,edi
mov ecx,10
div ecx ; divide and remainder by 10
mov edi,eax ; move quotient back to edi
add dl,48 ; convert remainder to ascii
mov byte [rbx],dl ; move remainder to buffer
dec rbx ; shift 1 position to the left in buffer
jmp _printint_loop
_printint_done:
; print the buffer (code not relevant)
mov rsp,rbp ; restore stack and base pointers
pop rbp
ret
我该如何优化它才能让它运行得更快?或者,是否有更好的方法将整数转换为字符串?
我不想使用 printf 或 C 标准库中的任何其他函数
解决方法
事实证明我对瓶颈的来源是错误的。我的基准是有缺陷的。尽管诸如幻数乘法和更好的循环之类的微优化确实有所帮助,但最大的瓶颈是系统调用。
通过使用缓冲读写(缓冲区大小为 16 kB),我能够实现比 scanf 和 printf 更快地读取和打印整数的目标。
创建输出缓冲区将一个特定的基准测试速度提高了 4 倍以上,而微优化将其速度提高了约 25%。
对于以后偶然发现这篇文章的任何人,以下是我所做的优化:
- 将所有 sys_write 调用替换为 write_buf 调用,其中 write_buf 将输出写入缓冲区,并且仅在缓冲区已满时才打印缓冲区。这种 write_buf 函数的实现留给读者作为练习。
- 用幻数乘法代替了除法,这样每次循环迭代节省了几个时钟周期。
- 将 while 循环改为 do...while 循环,每次循环迭代节省一条 jmp 指令(随着时间的推移会增加很多)。
- 优化单个指令(例如使用 neg 而不是 xor、add)并删除冗余指令。
正如 phuclv 在评论中提到的那样,我可以(但没有)进行的另一个潜在改进是除以更大的基数并使用查找表。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。