為什么 Go 中讀寫文件比 Perl 慢很多?這是很多開發(fā)者在使用這兩種編程語言時經常遇到的問題。在這篇文章中,php小編草莓將為您解答這個問題。在比較 Go 和 Perl 讀寫文件的速度時,我們需要考慮到兩個關鍵因素:語言特性和底層實現(xiàn)。Go 語言在文件讀寫方面的設計理念與 Perl 不同,這導致了它們在性能上的差異。同時,底層實現(xiàn)也是影響讀寫速度的重要因素。接下來,我們將詳細分析這些因素,幫助您更好地理解為什么 Go 中讀寫文件比 Perl 慢很多。
問題內容
我使用go是為了提高代碼效率,但是當我使用go讀寫文件時,發(fā)現(xiàn)它的讀寫效率沒有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) 發(fā)送其內容,而 perl 從那里讀取數(shù)據(jù),對其進行處理并將結果寫入其標準輸出。
你的圍棋咒語
測量 go 工具鏈的完整構建過程(包括編譯、鏈接和寫出可執(zhí)行映像文件)然后運行
已編譯程序的組成部分,以及
測量對標準輸出的無緩沖寫入(fmt.print* 調用),而在 perl 代碼中寫入標準輸出 – 引用 文檔 – “如果輸出到終端,通常可以進行行緩沖,否則進行塊緩沖。”
讓我們嘗試比較一下蘋果。
首先,這是一個類似的 go 實現(xiàn):
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);執(zhí)行 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
登錄后復制
再次運行它以確保它已從文件系統(tǒng)緩存中讀取輸入文件:
$ time { perl -ne 'chomp; print "$_";' out1.txt; }
real 0m0.123s
user 0m0.090s
sys 0m0.033s
登錄后復制
注意執(zhí)行時間是如何減少的。
在緩存的輸入上運行 go 代碼:
$ time { ./chomp out2.txt; }
real 0m0.063s
user 0m0.032s
sys 0m0.032s
登錄后復制
確保結果相同:
$ cmp out1.txt out2.txt
如您所見,在我的帶有 ssd 的 linux/amd64 系統(tǒng)上,結果大致相同。
嗯,我還應該指出,為了獲得合理的結果,您需要運行每個命令,例如 1000 次,并對每個批次中的結果進行平均,然后比較這些數(shù)字,但我認為這足以證明什么您的方法存在的問題是。
還有一件事需要考慮:這兩個程序的運行時間絕大多數(shù)由文件系統(tǒng) i/o 主導,因此,如果您認為 go 會更快,那么您的期望是沒有根據(jù)的:這兩個程序大部分時間sleep 在內核的系統(tǒng)調用 read(2) 和 write(2)。在某些涉及 cpu 運算的情況下,go 程序可能比 perl 程序更快(特別是如果它是為利用多核系統(tǒng)而編寫的),但您的示例根本不是這種情況。
哦,只是為了明確未說明的事實:雖然 go 語言規(guī)范沒有說明 aot,而 go run 是一種針對一次性一次性演出的 hack,不嚴肅的工作,也不執(zhí)行任何嚴重復雜程度的代碼。簡而言之,go-that-you-are-using 并不是一種解釋性語言,盡管 go run 的可用性可能使它看起來如此。事實上,它執(zhí)行正常 go build 會執(zhí)行的操作,然后運行生成的可執(zhí)行文件,然后將其丟棄。
您可能會想說 perl 也處理“源代碼”,但 perl 解釋器針對處理腳本和 go 的構建工具鏈進行了高度優(yōu)化——同時與大多數(shù)其他編譯語言相比速度快得驚人——未針對此進行優(yōu)化。
可能更明顯的區(qū)別是,perl 解釋器實際上解釋您的(非常簡單的)腳本,而 chomp 和 print 是所謂的“內置函數(shù)”,很容易提供給由解釋器執(zhí)行腳本。與構建 go 程序相比,編譯器解析源代碼文件并將其轉換為機器代碼,鏈接器實際上讀取 go 標準庫的編譯包的文件 – 所有這些都是 imported, – 從它們,組合所有這些機器代碼并寫出一個可執(zhí)行圖像文件(這很像 perl 二進制文件本身!);當然,這是一個非常消耗資源的過程,與實際的程序執(zhí)行無關。






