[英]How do I speed this up?
以下代碼列出了名稱和“數字”,並為每個人提供了15到90歲之間的隨機年齡。
#!/bin/sh
file=$1
n=$2
# if number is zero exit
if [ "$n" -eq "0" ]
then
exit 0
fi
echo "Generating list of $n people."
for i in `seq 1 $n`;
do
let "NUM=($RANDOM%75)+15"
echo "name$i $NUM (###)###-####" >> $file
done
echo "List generated."
有了它,我正在嘗試列出1M名稱。 它很慢,我預料到了; 它太慢了,我失去了耐心,嘗試了10K的名字。 那也很慢,但它在幾秒鍾內就完成了。
我生成名稱的原因是對它們進行排序。 令我驚訝的是,當我對10K名稱列表進行排序時,它是即時的。
怎么會這樣?
是否有什么東西讓這個變得不那么慢? 排序和生成都是訪問文件,那么排序如何更快? 列表生成器中的隨機數數學是什么減慢了它?
這是我的排序腳本。
#!/bin/sh
#first argument is list to be sorted, second is output file
tr -s '' < $1 | sort -n -k2 > $2
使用shell生成這樣的隨機數並不是它的設計目的。 你可能會更好地編寫一些東西,用另一種語言的統一分布生成隨機數,比如Fortran,Perl或C.
在你的代碼中,一件非常慢的事情是從1..1e7生成一系列數字並將它們全部分配給變量。 這可能非常浪費,但如果你想確定,你應該描述一下。 正如混亂所指出的那樣,附加到文件也可能非常昂貴!
在Python中,您可以執行以下操作:
#!/usr/bin/python
import random
count = 1
print ' '.join( ['name', 'age'] )
while count <= 1000000:
age = random.randrange(15,90)
count = count + 1
name = 'name' + str(count)
print ' '.join( [ name, str(age) ] )
在筆記本電腦上運行需要大約10秒鍾。 將seq從1分配給1000000需要大約10秒,當您添加隨機數生成時,您的腳本在同一台機器上花費超過三分鍾。 我和你一樣感到沮喪,並且使用腳本來嘗試讓它更快。 這是我正在使用的縮短版代碼:
for x in `seq 1 10000`; do
let "NUM=($RANDOM%75)+15"
echo $NUM >> test.txt
done
運行這個大概需要5.3s:
$ time ./test.sh
real 0m5.318s
user 0m1.305s
sys 0m0.675s
刪除文件追加並簡單地將STDOUT重定向到單個文件提供以下腳本:
for x in `seq 1 10000`; do
let "NUM=($RANDOM%75)+15"
echo $NUM
done
運行這個大約需要半秒鍾:
$ time ./test.sh > test.txt
real 0m0.516s
user 0m0.449s
sys 0m0.067s
程序的緩慢至少部分是由於附加到該文件。 奇怪的是,當我嘗試用for循環交換seq調用時,我沒有注意到任何加速。
for i in `seq 1 $n`
哎呀! 這會for
循環生成1,000,000個參數。 那個seq
電話需要很長很長時間。 嘗試
for ((i = 1; i <= n; i++))
順便提一下,請注意缺少美元符號。 特別是, var++
語法要求您從變量名中省略美元符號。 您也可以在其他地方使用或省略它們:它可以是i <= n
或$i <= $n
,任何一個。 我的方式,你應該完全在let
, declare
和for ((x; y; z))
語句中省略美元符號。 見算術評價科sh
手冊頁完整的解釋。
不是新的答案,只是新的代碼。
這就是恕我直言,它是一個很好的高效代碼之間的良好中間路徑(就像你在Bash中一樣高效,它很慢,它是一個shell ...)
for ((i=1;i<=n;i++));
do
echo "name$i $((NUM=(RANDOM%75)+15)) (###)###-####"
done > "$file"
替代方案,不使用經典的計數器循環
i=1
while ((i<=n)); do
echo "name$((i++)) $((NUM=(RANDOM%75)+15)) (###)###-####"
done > "$file"
兩者速度大致相同。
修復程序與所有其他修復程序相同:
我想'>> $ file'可能是你問題的根源。 在我的系統上,你的腳本需要10秒才能生成10000.如果我刪除$ file參數,而只是使用stdout並將整個事件捕獲到一個文件,它需要一秒鍾。
$ time ./gen1.sh n1.txt 10000生成10000人的列表。 列表生成。
真正的0m7.552s用戶0m1.355s sys 0m1.886s
$ time ./gen2.sh 10000> n2.txt
實際0m0.806s用戶0m0.576s sys 0m0.140s
不知道這是不是整個故事,但重新打開文件附加到它的每個名字都無濟於事。 在任何可以保持打開文件句柄寫入的上下文中完成所有操作應該會有很大幫助。
試試這個主循環:
seq 1 $n | while read i
do
let "NUM=($RANDOM%75)+15"
echo "name$i $NUM (###)###-####"
done > $file
這將使seq
和循環並行工作,而不是在開始循環之前等待seq完成。 這在多核/ CPU上會更快,但在單核上稍慢。
我同意這里的其他人:它必須是bash嗎?
編輯:添加混亂的建議以保持文件打開,不打開以附加每個名稱。
(我有一種感覺你可能不喜歡這個答案,但你在技術上沒有指明答案必須留在bash!:P)
在原型語言中快速開發一些東西是很常見的,然后可能根據需要切換到另一種語言(通常是C語言)。 這是一個非常類似的Python程序供您比較:
#!/usr/bin/python
import sys
import random
def main(args=None):
args = args or []
if len(args) == 1:
# default first parameter
args = ["-"] + args
if len(args) != 2:
sys.stderr.write("error: invalid parameters\n")
return 1
n = int(args[1])
output = sys.stdout if args[0] == "-" else open(args[0], "a")
for i in xrange(1, n + 1):
num = random.randint(0, 74)
output.write("name%s %s (###)###-####\n" % (i, num))
sys.stderr.write("List generated.\n") # see note below
if __name__ == "__main__":
sys.exit(main(sys.argv[1:]))
注意:僅使用stdout進行“實際輸出”而不是狀態通知允許此程序與其他程序並行運行,將數據直接從一個stdout傳送到另一個stdin。 (可以使用* nix中的特殊文件,但如果可以使用stdout則更容易。)示例:
$./rand_names.py 1000000 | sort -n -k2 > output_file
它應該足夠快:
$time ./rand_names.py 1000000 > /dev/null List generated. real 0m16.393s user 0m15.108s sys 0m0.171s
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.