為什么 Go 中讀寫文件比 Perl 慢很多?這是很多開發者在使用這兩種編程語言時經常遇到的問題。在這篇文章中,php小編草莓將為您解答這個問題。在比較 Go 和 Perl 讀寫文件的速度時,我們需要考慮到兩個關鍵因素:語言特性和底層實現。Go 語言在文件讀寫方面的設計理念與 Perl 不同,這導致了它們在性能上的差異。同時,底層實現也是影響讀寫速度的重要因素。接下來,我們將詳細分析這些因素,幫助您更好地理解為什么 Go 中讀寫文件比 Perl 慢很多。
問題內容
我使用go是為了提高代碼效率,但是當我使用go讀寫文件時,發現它的讀寫效率沒有perl高。是我代碼的問題還是其他原因?
構建輸入文件:
# input file: for i in $(seq 1 600000) do echo server$((random%800+100)),$random,$random,$random >> sample.csv done
登錄后復制
用perl讀寫文件:
time cat sample.csv | perl -ne 'chomp;print"$_"' > out.txt
登錄后復制
real 0m0.249s user 0m0.083s sys 0m0.049s
登錄后復制
使用 go 讀寫文件:
package main import ( "bufio" "fmt" "io" "os" "strings" ) func main() { filepath := "./sample.csv" file, err := os.openfile(filepath, os.o_rdwr, 0666) if err != nil { fmt.println("open file error!", err) return } defer file.close() buf := bufio.newreader(file) for { line, err := buf.readstring('\n') line = strings.trimspace(line) fmt.println(line) if err != nil { if err == io.eof { fmt.println("file read ok!") break } else { fmt.println("read file error!", err) return } } } }
登錄后復制
然后我運行:
time go run read.go > out.txt
登錄后復制
real 0m2.332s user 0m0.326s sys 0m2.038s
登錄后復制
為什么 go 的讀寫速度比 perl 慢近 10 倍?
解決方法
您正在將蘋果與橙子進行比較。
至少有兩個方法錯誤:
您的 perl 咒語測量 cat
如何讀取文件并通過 pipe(2) 發送其內容
,而 perl
從那里讀取數據,對其進行處理并將結果寫入其標準輸出。
你的圍棋咒語
測量 go 工具鏈的完整構建過程(包括編譯、鏈接和寫出可執行映像文件)然后運行
已編譯程序的組成部分,以及
測量對標準輸出的無緩沖寫入(fmt.print*
調用),而在 perl 代碼中寫入標準輸出 – 引用 文檔 – “如果輸出到終端,通常可以進行行緩沖,否則進行塊緩沖。”
讓我們嘗試比較一下蘋果。
首先,這是一個類似的 go 實現:
package main import ( "bufio" "bytes" "fmt" "os" ) func main() { in := bufio.newscanner(os.stdin) out := bufio.newwriter(os.stdout) for in.scan() { s := bytes.trimspace(in.bytes()) if _, err := out.write(s); err != nil { fmt.fprint(os.stderr, "failed to write file:", err) os.exit(1) } } if err := out.flush(); err != nil { fmt.fprint(os.stderr, "failed to write file:", err) os.exit(1) } if err := in.err(); err != nil { fmt.fprint(os.stderr, "reading failed:", err) os.exit(1) } }
登錄后復制
讓我們將其保存為 chomp.go
并進行測量:
構建代碼:
$ go build chomp.go
生成輸入文件:
$ for i in $(seq 1 600000);執行 echo server$((random%800+100)),$random,$random,$random;完成 >sample.csv
運行 perl 代碼:
$ time { perl -ne 'chomp; print "$_";' out1.txt; } real 0m0.226s user 0m0.102s sys 0m0.048s
登錄后復制
再次運行它以確保它已從文件系統緩存中讀取輸入文件:
$ time { perl -ne 'chomp; print "$_";' out1.txt; } real 0m0.123s user 0m0.090s sys 0m0.033s
登錄后復制
注意執行時間是如何減少的。
在緩存的輸入上運行 go 代碼:
$ time { ./chomp out2.txt; } real 0m0.063s user 0m0.032s sys 0m0.032s
登錄后復制
確保結果相同:
$ cmp out1.txt out2.txt
如您所見,在我的帶有 ssd 的 linux/amd64
系統上,結果大致相同。
嗯,我還應該指出,為了獲得合理的結果,您需要運行每個命令,例如 1000 次,并對每個批次中的結果進行平均,然后比較這些數字,但我認為這足以證明什么您的方法存在的問題是。
還有一件事需要考慮:這兩個程序的運行時間絕大多數由文件系統 i/o 主導,因此,如果您認為 go 會更快,那么您的期望是沒有根據的:這兩個程序大部分時間sleep 在內核的系統調用 read(2)
和 write(2)
。在某些涉及 cpu 運算的情況下,go 程序可能比 perl 程序更快(特別是如果它是為利用多核系統而編寫的),但您的示例根本不是這種情況。
哦,只是為了明確未說明的事實:雖然 go 語言規范沒有說明 aot,而 go run
是一種針對一次性一次性演出的 hack,不嚴肅的工作,也不執行任何嚴重復雜程度的代碼。簡而言之,go-that-you-are-using 并不是一種解釋性語言,盡管 go run
的可用性可能使它看起來如此。事實上,它執行正常 go build
會執行的操作,然后運行生成的可執行文件,然后將其丟棄。
您可能會想說 perl 也處理“源代碼”,但 perl 解釋器針對處理腳本和 go 的構建工具鏈進行了高度優化——同時與大多數其他編譯語言相比速度快得驚人——未針對此進行優化。
可能更明顯的區別是,perl 解釋器實際上解釋您的(非常簡單的)腳本,而 chomp
和 print
是所謂的“內置函數”,很容易提供給由解釋器執行腳本。與構建 go 程序相比,編譯器解析源代碼文件并將其轉換為機器代碼,鏈接器實際上讀取 go 標準庫的編譯包的文件 – 所有這些都是 import
ed, – 從它們,組合所有這些機器代碼并寫出一個可執行圖像文件(這很像 perl
二進制文件本身!);當然,這是一個非常消耗資源的過程,與實際的程序執行無關。