卓越飞翔博客卓越飞翔博客

卓越飞翔 - 您值得收藏的技术分享站
技术文章64334本站已运行4115

为什么 Go 中读写文件比 Perl 慢很多?

为什么 go 中读写文件比 perl 慢很多?

为什么 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 倍?

解决方法

您正在将苹果与橙子进行比较。

至少有两个方法错误:

  1. 您的 perl 咒语测量 cat 如何读取文件并通过 pipe(2) 发送其内容,而 perl 从那里读取数据,对其进行处理并将结果写入其标准输出。

  2. 你的围棋咒语

    • 测量 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 并进行测量:

  1. 构建代码:

    $ go build chomp.go

  2. 生成输入文件:

    $ for i in $(seq 1 600000);执行 echo server$((random%800+100)),$random,$random,$random;完成 >sample.csv

  3. 运行 perl 代码:

    $ time { perl -ne 'chomp; print "$_";' <sample.csv >out1.txt; }
    
    real    0m0.226s
    user    0m0.102s
    sys 0m0.048s
  4. 再次运行它以确保它已从文件系统缓存中读取输入文件:

    $ time { perl -ne 'chomp; print "$_";' <sample.csv >out1.txt; }
    
    real   0m0.123s
    user   0m0.090s
    sys    0m0.033s

    注意执行时间是如何减少的。

  5. 在缓存的输入上运行 go 代码:

    $ time { ./chomp <sample.csv >out2.txt; }
    
    real   0m0.063s
    user   0m0.032s
    sys    0m0.032s
  6. 确保结果相同:

    $ cmp out1.txt out2.txt

  7. 如您所见,在我的带有 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 解释器实际上解释您的(非常简单的)脚本,而 chompprint 是所谓的“内置函数”,很容易提供给由解释器执行脚本。与构建 go 程序相比,编译器解析源代码文件并将其转换为机器代码,链接器实际上读取 go 标准库的编译包的文件 - 所有这些都是 imported, - 从它们,组合所有这些机器代码并写出一个可执行图像文件(这很像 perl 二进制文件本身!);当然,这是一个非常消耗资源的过程,与实际的程序执行无关。

卓越飞翔博客
上一篇: Docker 没有这样的文件或目录
下一篇: 使用 Golang 的 Cloud Functions Gen2 - 实例生命周期和 BigQuery 插入安全,无需等待作业完成
留言与评论(共有 0 条评论)
   
验证码:
隐藏边栏