请注意,我已经进行了很长时间的编程,但是对于这篇文章,我已经进行了大约四个小时的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/