performance - 使GO(GOLANG)逐行解析文本更快

标签 performance go

请注意,我已经进行了很长时间的编程,但是对于这篇文章,我已经进行了大约四个小时的GO编程。

现在,这已经成为一个学术问题,并且全都与速度有关。

当前的任务是解析一个非常大的文本文件。可以说,对于测试500Mb来说,它并不是很大,但足以显示出速度上的差异。可以说实际文件是1Gb,只是为了添加一些参数,我确实有足够的内存来在需要时将整个文件加载到内存中。

开头的每一行输出(在这种情况下,输出到控制台)到特定IP地址。

我很感兴趣是否可以通过编写非常具体的代码来比grep和awk(etc)更快地实现。

我决定先在GO中尝试,然后在RUST中尝试(尚未尝试)。

在对GO进行一番摸索之后,我对它执行非常具体的操作的速度慢得多感到惊讶。 Grep快将近50%。

注意:我一直在使用一些基本的内置GO计时,这些计时对于我来说是准确的(足够)。

我还尝试了一些我没有写的特定于读取字节的源代码。我只有80%的人了解语法,但是它们甚至比我讲的代码要慢。

如果您发现任何无关的GO错误,请随时指出并按我所说的进行解释,我对GO进行了大约四个小时的编程。

我喜欢GO尝试做的一些事情,所以即使以至少等于grep和awk等的速度运行它也很棒。

编辑这个测试实际上是在Ubuntu上运行的egrep,因为看起来这段代码实际上比在OSX上的grep和egrep更快。

到目前为止,这是我已经实现的禁食:

package main

import (
    "bufio"
    "flag"
    "fmt"
    "os"
    "time"
)

var (
    filePath  = flag.String("filepath", "", "The path and filename of the file to parse.")
    ipAddress = flag.String("ipaddress", "", "The ip address to search for.")
)

func main() {

    flag.Parse()

    flag.VisitAll(func(f *flag.Flag) {

        if f.Value.String() == "" {
            fmt.Println()
            fmt.Println("Ip address finder.")
            fmt.Println()
            fmt.Println(`Usage: ipfinder -filepath "..." -ipaddress "..."`)
            fmt.Println()
            os.Exit(1)
        }
    })

    timerStart := time.Now()

    file, err := os.Open(*filePath)

    if err != nil {
        fmt.Printf("Failed to open file: %s", err)
        os.Exit(1)
    }

    defer file.Close()

    fileScanner := bufio.NewScanner(file)

    // Stop re-calculating length.
    // The compiler may handle this??
    var ipAddressStringLength int = len(*ipAddress)
    var fileScannerText string

    for fileScanner.Scan() {

        fileScannerText = fileScanner.Text()

        // I took this from the strings.HasPrefix source code and
        // tried to exclude bits (that may crash some input) but 
        // it didn't make much difference.
        if fileScannerText[:ipAddressStringLength] == *ipAddress {
            fmt.Println(fileScannerText)
        }
    }

    // Not sure about this??
    if err := fileScanner.Err(); err != nil {
        fmt.Println(err)
    }

    elapsed := time.Since(timerStart)

    fmt.Println()
    fmt.Printf("Time Took %s", elapsed)
}

最佳答案

我只是使用@CeriseLimón的建议,调整了代码并获得了惊人的加速(在OSX上为30%,在Debian上为〜75%)。现在,在两个平台上,Go版本(在我的测试数据上)都比egrep快:

package main

import (
    "bufio"
    "bytes"
    "flag"
    "fmt"
    "os"
    "time"
)

var (
    filePath  = flag.String("filepath", "", "The path and filename of the file to parse.")
    ipAddress = flag.String("ipaddress", "", "The ip address to search for.")
)

func main() {
    flag.Parse()
    flag.VisitAll(func(f *flag.Flag) {
        if f.Value.String() == "" {
            fmt.Println()
            fmt.Println("Ip address finder.")
            fmt.Println()
            fmt.Println(`Usage: ipfinder -filepath "..." -ipaddress "..."`)
            fmt.Println()
            os.Exit(1)
        }
    })

    timerStart := time.Now()
    file, err := os.Open(*filePath)

    if err != nil {
        fmt.Printf("Failed to open file: %s", err)
        os.Exit(1)
    }
    defer file.Close()

    fileScanner := bufio.NewScanner(file)
    var scannerBytes []byte
    ipBytes := []byte(*ipAddress)
    w := bufio.NewWriterSize(os.Stdout, 1000000)

    for fileScanner.Scan() {
        scannerBytes = fileScanner.Bytes()

        if bytes.HasPrefix(scannerBytes, ipBytes) {
            fmt.Fprintln(w, string(scannerBytes))
        }
    }
    w.Flush()

    if err := fileScanner.Err(); err != nil {
        fmt.Println(err)
    }

    elapsed := time.Since(timerStart)
    fmt.Println()
    fmt.Printf("Time Took %s", elapsed)
}

您可以尝试使用Stdout的缓冲区大小来获得最佳性能。

关于performance - 使GO(GOLANG)逐行解析文本更快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61332140/

相关文章:

regex - Go 中的 "Unknown escape sequence"错误

android - 从相机加载缩略图的最快方法

php - Symfony 2 性能优化

go - 如何在函数调用链中使用 context.Context 取消

go - FileInfo.IsDir() 未检测到目录

go - 如何使用SingleFlight分享下载的大文件?

postgresql - 事务查看开始后对数据库行所做的更改

python - 基于另一个数据框的值在多列上子集 Pandas 数据框

javascript - *ngFor 使用异步管道进行本地分配并减少订阅数量

c++ - 等效于嵌套 for 循环的迭代器显示 50% 的性能故障 - 为什么?