日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

浅谈尾递归的优化

發布時間:2025/3/15 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 浅谈尾递归的优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

在 淺談尾調用和尾遞歸 這篇博文中,我談了什么是尾遞歸以及編譯器如何優化尾遞歸。這篇文章,咱來個具體的例子,通過匯編代碼來看看優化和不優化的區別。

求階乘的尾遞歸寫法

// file_name : factorial.c #include <stdio.h>int factorial_tail(int n, int product_from_n) {if (n == 1)return product_from_n;elsereturn factorial_tail(n - 1, n * product_from_n); }int main(void) {int result = factorial_tail(5,1);printf("%d\n",result); }

實驗思路是將上面的源碼編譯為普通版本和優化版本,通過對比2個版本的匯編代碼來了解編譯器如何優化尾遞歸。

編譯為2個版本

gcc -S factorial.c -o factorial.s #普通版本 gcc -S factorial.c -O2 -o factorial_O2.s #優化版本

注:我的gcc版本是 4.8.4

對比文件

普通版本(未優化)

為了說明問題,我刪除了一些偽指令,并加了些注釋:

factorial_tail:pushq %rbpmovq %rsp, %rbpsubq $16, %rspmovl %edi, -4(%rbp) // 傳遞參數 nmovl %esi, -8(%rbp) // 傳遞參數 product_from_ncmpl $1, -4(%rbp) //把參數n和1比較jne .L2 //不相等就跳轉到.L2movl -8(%rbp), %eax //相等的話 eax = product_from_njmp .L3 //跳轉到.L3 .L2:movl -4(%rbp), %eax // eax = nimull -8(%rbp), %eax // eax = product_from_n*nmovl -4(%rbp), %edx // edx = nsubl $1, %edx // edx--;movl %eax, %esi //esi = eax = (product_from_n*n)movl %edx, %edi //edi = edx = (n-1)call factorial_tail //遞歸調用 .L3:leave //leave和ret用于返回main函數retmain:pushq %rbpmovq %rsp, %rbpsubq $16, %rspmovl $1, %esimovl $5, %edicall factorial_tail

可以看到,編譯器就是把C代碼翻譯成匯編了,沒有做什么優化。遞歸依然是遞歸。

優化版本

我整理后的代碼如下:

factorial_tail:cmpl $1, %edi //把edi(edi=參數n)和1比較movl %esi, %eax // eax = esi =product_from_nje .L2 //if(edi==1),則跳到.L2 .L3:imull %edi, %eax // eax = (edi * eax)= (n * product_from_n)subl $1, %edi // edi--; (即n--;)cmpl $1, %edi // edi和1相比較,即n和1相比較jne .L3 // if(edi != 1) 則跳轉到.L3 .L2:rep ret //返回主調函數,返回值在eax中

由此可見,編譯器把遞歸轉化成循環了。從第5行到第9行是典型的循環結構。

再多說一句,最后一行的rep指令是什么意思呢?我搜到的結果如下。

Some AMD processors have a one cycle pipeline stall when a “ret” instruction is the target of a conditional jump or is immediately preceded by a conditional jump. To avoid this, gcc generates “rep; ret” in those cases intead. Since the “rep” prefix means nothing with the “ret” instruction, this effectively becomes a two byte “ret” instruction, and that is sufficient to avoid the pipeline bubble.

總結

以上是生活随笔為你收集整理的浅谈尾递归的优化的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。