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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

循环变量的优化

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

2019獨角獸企業重金招聘Python工程師標準>>>

現在來介紹編譯器如何識別循環變量的問題。
比如前面各種關于循環的優化中,我們如果要計算依賴向量,起碼編譯器要能夠識別循環變量,
而識別循環變量也是編譯器的一個優化過程。
而通常編譯器所認為的循環變量可能同我們所看到的有所不同。
通常的循環變量是狹義的,也就是說,如果這個變量在一個循環的任意兩次相鄰的迭代中變換值是常數,
那么這個變量就是循環變量。最常見的循環變量是如下代碼中:
  • for(i=0;i<n;i++){
  • ? ?....
  • }
  • 復制代碼 如果循環體中沒有對變量i的修改,那么i就必然是一個循環變量。
    但是如果循環體中對i做了修改,那么雖然看上去i還像是一個循環變量,但是對于編譯器來說,i已經不是循環變量了,如:
  • for(i=0;i<n;i++){
  • ??if(..){
  • ? ?? ?i++;
  • ??}
  • ??...
  • }
  • 復制代碼 像上面的循環體,有時候i增加1,有時候i增加2,那么i就不是循環變量了。
    而還有一些情況,循環變量人工不容易看出來,但是編譯器確可以判斷出來,如:
  • i=0;
  • while(...){
  • ? ?j=i+1;
  • ? ?...
  • ? ?k=j+2;
  • ? ?...
  • ? ?i=k;
  • ? ?...
  • }
  • 復制代碼 像上面的代碼,如果沒有其他對j,k,i的修改,那么這里i,j,k實際上都是循環變量,
    其中每次迭代這三個變量都增加了3.
    而對于編譯器來說,通常還可以識別一些更加復雜的循環變量,如:
  • i=0;
  • while(...){
  • ? ?j=i+1;
  • ? ?...
  • ? ?k=j+2;
  • ? ?h=a*k+j;
  • ? ?...
  • ? ?i=k;
  • ? ?u=h-i+b;
  • ? ?...
  • }
  • 復制代碼 像上面代碼中,編譯器首先可以識別出i,j,k是循環變量,然后編譯器發現h,u是循環變量的線性組合,
    所以編譯器可以識別出它們也是循環變量。(其中a,b可以是變量,只要在循環體內部沒有被修改)
    比如h每個循環改變的量為3*(a+1),u每個循環改變的量為3*a,編譯器可以通過將上面代碼改變為:
  • i=0;
  • h=3*a+1;
  • u=3*a+1+b;
  • step1=3*(a+1);
  • step2=3*a;
  • while(...){
  • ? ?j=i+1;
  • ? ?...
  • ? ?k=j+2;
  • ? ?///h=a*k+j;///刪除原先這里對h的定義
  • ? ?...
  • ? ?i=k;
  • ? ?///u=h-i+b;///刪除這里對u的定義
  • ? ?...
  • ? ?h+=step1;
  • ? ?u+=step2;
  • }
  • 復制代碼 在經過這個變換以后,在循環體內部, 所有關于循環變量的計算均可以不包含乘法運算,從而比原先代碼應該可以快一些。
    同樣,如果在編譯器優化比較后面的部分,通常,對于數組的訪問都已經被展開,
    如代碼
  • ??for(i=0;i<n;i++){
  • ? ?? ? a[i] =....;
  • ??}
  • 復制代碼 可能被展開成:
  • for(i=0;i<n;i++){
  • ? ???p=a+i*S; ///這里S是常數,代表數組a中每個元素在內存中占用的空間大小
  • ? ???*p=...;
  • }
  • 復制代碼 那么對于編譯器來說,指針p也是一個循環變量,所以代碼可以被轉化為
  • p=a;
  • for(i=0;i<n;i++){
  • ? ???*p=...;
  • ? ???p=p+S;
  • }
  • 復制代碼 變化以后同樣計算地址中的乘法運算被消除了。
    我看到郭給出鏈接中一篇英文文章中介紹到對于數組,最好讓每個元素數據的大小是2的冪,這樣,計算每個元素的地址時候,
    乘法就可以被移位替換掉,從而提高了速度。但是,如果那樣的數組通常都是被通過循環變量訪問的,我們可以看出來,完全沒有
    必要做那樣的優化(實際上那樣可能會消耗更多的內存空間,從而性能更加差).
    此外,有一些比較優秀的程序員,他們知道計算機計算移位比乘法運算快,所以對于下面的代碼
  • for(i=0;i<n;i++){
  • ? ?a[2*i]=...;
  • ? ?...
  • }
  • 復制代碼 他們可能寫成了
  • for(i=0;i<n;i++){
  • ? ?a[i<<1]=...;
  • ? ?...
  • }
  • 復制代碼 其實,對于編譯器來說,反而前面的代碼更加容易優化,因為編譯器可以非常容易識別出2*i是一個循環變量,從而我們可以計算依賴向量,
    做一些前面提到過的如么模變換,仿射變換之類的優化。反而對于后面的代碼,由于通常編譯器是不會將移位運算轉化為乘法運算的,所以
    通常的編譯器反而無法知道后面的i<<1也是一個循環變量,從而阻止了進一步優化的可能。

    此外,部分編譯器還會對一些循環變量之間的相乘做優化(比如Open64),比如代碼:
  • i=0;
  • while(...){
  • ? ?j=i+1;
  • ? ?...
  • ? ?k=j+2;
  • ? ?h=a*k+j;
  • ? ?...
  • ? ?i=k;
  • ? ?u=h-i+b;
  • ? ?...
  • ? ?sum+=h*u;
  • }
  • 復制代碼 在編譯器分析出h和u都是循環變量以后,編譯器就可以對h*u做進一步優化
    我們知道 h=h0+i*step1,u=u0+i*step2;
    所以h*u=h0*u0+(h0*step2+u0*step1)*i+i*i*step1*step2
    分別對于i和i+1計算上面的表達式并相減,我們可以得到對于第i次迭代,h*u的變換值是
    ? ?h0*step2+u0*step1+step1*step2+i*2*step1*step2;
    所以我們知道,上面代碼于是可以優化成:
  • i=0;
  • h=3*a+1;
  • u=3*a+1+b;
  • step1=3*(a+1);
  • step2=3*a;
  • hu=h*u;
  • ddhu=2*step1*step2;
  • dhu=h0*step2+u0*step1+step1*step2+ddhu*i;
  • while(...){
  • ? ?j=i+1;
  • ? ?...
  • ? ?k=j+2;
  • ? ?///h=a*k+j;///刪除原先這里對h的定義
  • ? ?...
  • ? ?i=k;
  • ? ?///u=h-i+b;///刪除這里對u的定義
  • ? ?...
  • ? ?h+=step1;
  • ? ?u+=step2;
  • ? ?sum+=hu;
  • ? ?hu+=dhu;
  • ? ?dhu+=ddhu;? ?
  • }
  • 復制代碼 從而計算循環體內計算h*u的乘法運算由兩次加法運算代替,從而提高了速度。
    同樣道理,對于三個循環變量的相乘,從理論上,我們同樣可以轉化為若干次加法運算。
    不過據我所知,并沒有編譯器真正這樣去做,畢竟實際中,這樣代碼的例子會非常少見。
    當然,如果換成用郭的HugeCalc代碼中大整數做循環變量的代碼,那么遇到上面的代碼編譯器
    的優化同樣無能為力了,那么就需要手工做類似的優化了。

    轉載于:https://my.oschina.net/u/218425/blog/51947

    總結

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

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