qq.*_*ang 2 perl benchmarking go
我使用Go是为了提高代码效率,但是当我使用Go读写文件时,发现它的读写效率没有Perl高。是我代码的问题还是其他原因?
\n构建输入文件:
\n# Input File:\nfor i in $(seq 1 600000) do echo SERVER$((RANDOM%800+100)),$RANDOM,$RANDOM,$RANDOM >> sample.csv done\nRun Code Online (Sandbox Code Playgroud)\n使用 Perl\xef\xbc\x9a 读写文件
\ntime cat sample.csv | perl -ne 'chomp;print"$_"' > out.txt\nRun Code Online (Sandbox Code Playgroud)\nreal 0m0.249s\nuser 0m0.083s\nsys 0m0.049s\nRun Code Online (Sandbox Code Playgroud)\n使用 Go 读写文件:
\npackage main\n\nimport (\n "bufio"\n "fmt"\n "io"\n "os"\n "strings"\n)\n\nfunc main() {\n\n filepath := "./sample.csv"\n file, err := os.OpenFile(filepath, os.O_RDWR, 0666)\n if err != nil {\n fmt.Println("Open file error!", err)\n return\n }\n defer file.Close()\n buf := bufio.NewReader(file)\n for {\n line, err := buf.ReadString('\\n')\n line = strings.TrimSpace(line)\n fmt.Println(line)\n if err != nil {\n if err == io.EOF {\n fmt.Println("File read ok!")\n break\n } else {\n fmt.Println("Read file error!", err)\n return\n }\n }\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n然后我运行:
\ntime go run read.go > out.txt\nRun Code Online (Sandbox Code Playgroud)\nreal 0m2.332s\nuser 0m0.326s\nsys 0m2.038s\nRun Code Online (Sandbox Code Playgroud)\n为什么 Go 的读写速度比 Perl 慢近 10 倍?
\nkos*_*tix 15
您正在将苹果与橙子进行比较。
\n至少有两个方法论错误:
\n您的 Perl 咒语测量如何cat读取文件并将其内容发送到pipe(2),并perl从那里读取数据,处理它并将结果写入其标准输出。
你的围棋咒语
\nfmt.Print*调用)的无缓冲写入,而在 Perl 代码中写入标准输出 - 引用文档- “如果输出到终端,则通常可以进行行缓冲,否则进行块缓冲。”让我们试着比较一下苹果。
\n首先,这是一个类似的 Go 实现:
\npackage main\n\nimport (\n "bufio"\n "bytes"\n "fmt"\n "os"\n)\n\nfunc main() {\n in := bufio.NewScanner(os.Stdin)\n out := bufio.NewWriter(os.Stdout)\n\n for in.Scan() {\n s := bytes.TrimSpace(in.Bytes())\n\n if _, err := out.Write(s); err != nil {\n fmt.Fprint(os.Stderr, "failed to write file:", err)\n os.Exit(1)\n }\n }\n\n if err := out.Flush(); err != nil {\n fmt.Fprint(os.Stderr, "failed to write file:", err)\n os.Exit(1)\n }\n\n if err := in.Err(); err != nil {\n fmt.Fprint(os.Stderr, "reading failed:", err)\n os.Exit(1)\n }\n}\nRun Code Online (Sandbox Code Playgroud)\n让我们将其另存为chomp.go并测量:
构建代码:
\n$ go build chomp.go
生成输入文件:
\n$ for i in $(seq 1 600000); do echo SERVER$((RANDOM%800+100)),$RANDOM,$RANDOM,$RANDOM; done >sample.csv
运行 Perl 代码:
\n$ time { perl -ne \'chomp; print "$_";\' <sample.csv >out1.txt; }\n\nreal 0m0.226s\nuser 0m0.102s\nsys 0m0.048s\nRun Code Online (Sandbox Code Playgroud)\n再次运行它以确保它已从文件系统缓存中读取输入文件:
\n$ time { perl -ne \'chomp; print "$_";\' <sample.csv >out1.txt; }\n\nreal 0m0.123s\nuser 0m0.090s\nsys 0m0.033s\nRun Code Online (Sandbox Code Playgroud)\n请注意执行时间是如何减少的。
\n在缓存的输入上运行 Go 代码:
\n$ time { ./chomp <sample.csv >out2.txt; }\n\nreal 0m0.063s\nuser 0m0.032s\nsys 0m0.032s\nRun Code Online (Sandbox Code Playgroud)\n确保结果相同:
\n$ cmp out1.txt out2.txt
正如您所看到的,在我的linux/amd64配备 SSD 的系统上,结果大致相同。
好吧,我还应该指出,为了获得合理的结果,您需要运行每个命令,例如 1000 次,并对每个批次中的结果进行平均,然后比较这些数字,但我认为这足以证明你的方法有什么问题。
\n还有一件事需要考虑:这两个程序的运行时间绝大多数由文件系统 I/O 主导,所以如果你认为 Go 会更快,那么你的期望是没有根据的:这两个程序大部分时间都在内核中休眠的系统调用read(2)和write(2). 在某些涉及 CPU 运算的情况下(特别是如果它是为了利用多核系统而编写的),Go 程序可能比 Perl 程序更快,但您的示例根本不是这种情况。
哦,只是为了明确未说明的事实:虽然 Go 语言规范没有说明Go 实现的运行时系统必须如何完成,但这两个现有的最先进的 Go 实现(其中之一你\表面上使用)依赖AOT,并且go run是一种一次性的一次性工作的黑客,既不适合严肃的工作,也不适合执行任何严重复杂程度的代码。简而言之,Go-that-you-are-using 并不是一种解释性语言,尽管它的可用性go run可能使它看起来如此。事实上,它执行正常操作go build,然后运行生成的可执行文件,然后将其丢弃。
\xc2\xb9\xc2\xa0 你可能会想说 Perl 也处理“源代码”,但 Perl 解释器在处理脚本方面进行了高度优化,而 Go 的构建工具链\xe2\x80\x93与大多数其他编译语言相比,速度非常快\xe2\x80\x93 并未对此进行优化。
\n可能更明显的区别是,Perl 解释器实际上解释您的(非常简单的)脚本,并且chomp是print所谓的“内置函数”\xe2\x80\x93 函数,很容易由口译员。与构建Go程序相比,编译器解析源代码文件并将其转换为机器代码,链接器实际上读取Go标准库的编译包\xe2\x80\x94的文件,所有这些都是edimport的,\xe2\x80\x93 从中获取代码位,组合所有这些机器代码位并写出可执行映像文件(这与二进制文件perl本身非常相似!);当然,这是一个非常消耗资源的过程,与实际的程序执行无关。