歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> SHELL編程 >> 一個 Shell 程序的性能優化

一個 Shell 程序的性能優化

日期:2017/2/27 9:24:04   编辑:SHELL編程
編寫 Linux Shell 腳本程序不要僅限於完成基本的程序功能,認真的分析 Shell 腳本並找出優化的方法對個人能力的提高以及對腳本程序的質量改善都有重要的意義,希望讀者能從本文中獲得許多實用的 Shell 程序方法。

本文 Shell 程序運行環境:

程序運行環境 Redhat Linux As3 GNU bash, version 2.05b.0(1)-release (i386-redhat-linux-gnu) 代碼清單:shellcode.txt

問題描述:有一個普通的通話話單文件(包括"計費號碼","主叫號碼","被叫號碼","開始時間","結束時間","時長","費用"等其它字段),要求根據另外一個號段配置文件(由"號段下限"和"號段上限"兩個字段組成)將此話單文件進行分揀過慮。

分揀規則:如果通話話單文件中的"計費號碼"位於號段文件的某個號段內,則將此條記錄計入結果文件 1,否則計入結果文件 2。

通話話單文件樣例:

901332000390133200039918128025200608141634202006081416345030200001005 992664520899266452089918188065200608141634152006081416354590300001005 9934877207993487720799369720032006081416362020060814163930190500001005 ...... ......

號段配置文件樣例:

9013305000,9013327999 9013767000,9013768999 9923670000,9923679999 9928998000,9928999999 9932310000,9932319999 9932333400,9932333599 9936034000,9936036999 9936084000,9936084999 9998537000,9998537999 9998620000,9998629999 9998690000,9998699999

例如:

對於通話話單文件的第一條記錄中的"計費號碼"為 9013320000,此號碼正好屬於號段配置文件的第一個號段 9013305000,9013327999中,即:條件 9013305000<= 9013320000 <=9013327999 成立,所以應該將通話話單文件的第一條記錄計入結果文件 1 中;對於通話話單文件中的第二條記錄的"計費號碼"為 9926645208 它不屬於號段文件中的任何一個段,所以應該將通話話單的第二條記錄計入結果文件 2 中。

對於這樣一個簡單的問題首先想到的解決方法為:

解決方法1:

寫一個雙重循環,外層循環為逐條讀取"通話話單文件"並獲取每條記錄的第一個字段的值"計費號碼",內層循環:根據外層循環獲得的"計費號碼"在"號段文件"中循環比較,判斷此號碼是否屬於相應號段。

程序代碼如下(省略了文件存在性判斷等語句):

while read f do org="$(eXPr substr ${f} 1 10)" #取得"計費號碼"存入變量org中 while read numseg do nglow="$(expr substr ${numseg} 1 10 )" #將號段下限存入變量nglow ngtop="$(expr substr ${numseg} 12 10 )" #將號段上限存入變量ngtop if [ "$org" \> "$nglow" -a "$org" \< $ngtop ] #判斷"計費號碼"是否在此號段內 then echo "${f}" >> ./resultfile1.cdr #如果在此號段內,將此記錄計入結果文件1中 else echo "${f}" >> ./resultfile2.cdr #如果不在此號段內,將此記錄計入結果文件2中 fi done < ./numseg.txt done < ./rttest.txt

解決方法1 對於號段文件和通話話單的記錄數都比較少的情況下基本可以完成工作,但是當兩個文件的記錄數較多(例如號段文件>50條,話單文件> 10000條)的時候,這種方法就會花費幾個小時甚至幾天的時間才能得出處理結果。此腳本程序執行慢的原因是對第二個循環內的比較運算只用了最簡單的順序比較方法,所以當號段文件的記錄增多的時候,腳本的執行速度會急劇下降。

解決方法2:

將內層循環的逐個比較的方法改為二分查找法進行判斷,程序代碼如下:

#!/bin/bash #Author Xsh date:08-15-2006 #程序中使用了二分查找法進行號碼的范圍判斷 #為突出重點,省略了文件存在性判斷和異常捕獲以及幫助提示等程序語句 #程序的工作目錄為當前目錄 echo "Time:$(date)==>Strat to processing........." #顯示程序開始運行時間 while read numseg do tmplow="${tmplow} $(expr substr ${numseg} 1 10 & >/dev/null ) " tmptop="${tmptop} $(expr substr ${numseg} 12 10 & >/dev/null ) " done < ./numseg.txt #讀取號段文件,下限號段存入變量tmplow,上限號段存入變量tmptop arr_lownug=(${tmplow}) #將下限號段存入數組arr_lownug arr_topnug=(${tmptop}) #將上限號段存入數組arr_topnug #定義函數checknum(),輸入參數為需要檢查的"計費號碼",輸出參數為0或者1 #若checknum()輸出為0 表示"計費號碼" 不在號段文件的所有號段范圍內 #若checknum()輸出為1 表示"計費號碼" 在號段文件的所有號段范圍內 # checknum()函數中用二分搜索法進行號碼的判斷 checknum(){ thisnum=$1 ckresult=0 lowflag=0 topflag=$(expr ${#arr_lownug[*]} - 1 ) #標注1 MaxIndex=$(expr ${#arr_topnug[*]} - 1 ) #標注2 midflag=0 midflag=$(expr ${topflag} / 2 ) #標注3 if [ "${thisnum}" \< "${arr_lownug[0]}" -o "${thisnum}" \> "${arr_topnug[${MaxIndex}]}" ] then return 0 else while [ "$lowflag" != "$midflag" ] do if[ "$thisnum" \> "${arr_lownug[${midflag}]}" -o "$thisnum" == \ "${arr_lownug[${midflag}]}" ] then lowflag=${midflag} midflag=$(expr `expr ${topflag} + ${lowflag}` / 2 ) #標注4 elif["$thisnum"\<"${arr_lownug[${midflag}]}" -o "$thisnum" == \ "${arr_lownug[${midflag}]}" ] then topflag=${midflag} midflag=$(expr `expr ${topflag} + ${lowflag}` / 2 ) #標注5 else echo "Error!" fi done if [ "$thisnum" \< "${arr_topnug[${lowflag}]}" -o "$thisnum" == \ "${arr_topnug[${lowflag}]}" ] then return 1 else return 0 fi fi }#函數定義完畢 while read f do org="$(expr substr ${f} 1 10)" #標注6 checknum ${org} returnval=$? if [ "$returnval" == "1" ] then echo "${f}" >> ./Match_result.cdr #將匹配的記錄存入結果文件1 else echo "${f}" >> ./NoMatch_result.cdr #將不匹配的記錄存入結果文件2 fi done < ./rttest.txt echo "Time:$(date) ==> Proccess is end! " exit 0;

將以上程序投入運行,號段文件有 71行記錄,需要分揀的通話話單文件17 萬條左右通過觀察發現此方法的執行效率確實比解決方法1 提高了很多,但是仍需要數小時的時間才能執行完畢。我另外寫了一個同樣功能的C語言程序,執行同樣的測試數據得出結果僅需要10~15秒的時間!shell 程序確實要比同樣功能的C程序慢一些,但是目前的情況是用C程序處理只需要幾秒鐘時間, 而Shell程序確需要數小時!在用此Shell程序處理的時候我用Top命令看了一些系統資源的消耗發現CPU、內存以及IO的占用都極小,說明系統資源很充足,不是系統的問題造成了程序處理速度慢。問題出在哪了呢?

我用命名ps -ef 觀察系統進程的運行情況,發現每過幾秒種就會有一個expr進程產生,這個進程運行很短的時間就消失了(不仔細觀察可能都看不到有expr這個進程產生)。這時候我覺得我好像發現程序運行慢的原因了:程序的幾個循環裡面都有用expr進行計算的語句,expr屬於Shell外部命令,所以每次運算都要產生一個 expr進程,而程序的運行種最消耗時間的除了IO操作外就數產生新進程操作了,於是我決定把用expr進行計算的地方都盡量修改為用shell的內部命令進行計算。程序變成了下面的樣子(標注1~標注6為修改過的地方):

程序代碼如下:

――――――――――――――――――――――――――――――――――――――― #!/bin/bash #Author Xsh date:08-15-2006 echo "Time:$(date)==>Strat to processing........." #顯示程序開始運行時間 while read numseg do tmplow="${tmplow} $(expr substr ${numseg} 1 10 & >/dev/null ) " tmptop="${tmptop} $(expr substr ${numseg} 12 10 & >/dev/null ) " done < ./numseg.txt #循環讀取號段文件下限號段存入變量tmplow,上限號段存入變量tmptop arr_lownug=(${tmplow}) #將下限號段存入數組arr_lownug arr_topnug=(${tmptop}) #將上限號段存入數組arr_topnug #定義函數checknum(),輸入參數為需要檢查的"計費號碼",輸出參數為0或者1 #函數checknum()輸出為0 表示"計費號碼" 不在號段文件的所有號段范圍內 #函數checknum()輸出為1 表示"計費號碼" 在號段文件的所有號段范圍內 #函數checknum()中用二分搜索法進行號碼的判斷 checknum(){ #函數定義開始 thisnum=$1 ckresult=0 lowflag=0 topflag=$((${#arr_lownug[*]} - 1 )) #標注1 MaxIndex=$((${#arr_topnug[*]} - 1 )) #標注2 midflag=0 midflag=$((${topflag} / 2 )) #標注3 if [ "${thisnum}" \< "${arr_lownug[0]}" -o "${thisnum}" \> "${arr_topnug[${MaxIndex}]}" ] then return 0 else while [ "$lowflag" != "$midflag" ] do if ["$thisnum"\> "${arr_lownug[${midflag}]}" -o "$thisnum" == \ "${arr_lownug[${midflag}]}"] then lowflag=${midflag} midflag=$(( $((${topflag} + ${lowflag})) / 2 )) #標注4 elif["$thisnum"\<"${arr_lownug[${midflag}]}" -o "$thisnum" == \ "${arr_lownug[${midflag}]}"] then topflag=${midflag} midflag=$(($((${topflag} + ${lowflag})) / 2 )) #標注5 else echo "Error!" fi done if ["$thisnum" \< "${arr_topnug[${lowflag}]}" -o "$thisnum" == \ "${arr_topnug[${lowflag}]}"] then return 1 else return 0 fi fi }#函數定義結束 while read f do org="${f:0:10}" #標注6 checknum ${org} returnval=$? if [ "$returnval" == "1" ] then echo "${f}" >> ./Match_result.cdr #將匹配的記錄存入結果文件1 else echo "${f}" >> ./NoMatch_result.cdr #將不匹配的記錄存入結果文件2 fi done < ./rttest.txt echo "Time:$(date) ==> Proccess is end! " exit 0;

將修改過的程序進行運行,很快就得出了結果,總的運行時間沒有超過8分鐘。此時這個程序的運行效率已經基本可以讓人接受了。同樣的一個問題由於改進了程序算法和充分利用了LinuxShell的內置運算符,將程序的運行時間大大的縮短。當然此程序還有可以改進的地方,對此文感興趣的讀者可以對此程序做進一步優化。




Copyright © Linux教程網 All Rights Reserved