为了账号安全,请及时绑定邮箱和手机立即绑定

优化Go文件读取程序

优化Go文件读取程序

Go
九州编程 2021-05-07 15:14:38
我正在尝试处理一个日志文件,其每一行看起来像这样:flow_stats: 0.30062869162666672 gid 0 fid 1 pkts 5.0 fldur 0.30001386666666674 avgfldur 0.30001386666666674 actfl 3142 avgpps 16.665896331902879 finfl 1我对这个pkts领域和fldur领域都很感兴趣。我有一个Python脚本,可以读取一百万行的日志文件,为所有不同持续时间的每个数据包创建一个列表,对这些列表进行排序,并在3秒钟内找出中值。我正在玩Go编程语言,以为我会重写它,希望它能更快地运行。到目前为止,我一直很失望。仅将文件读入数据结构大约需要5.5秒。所以我想知道你们中的一些很棒的人是否可以帮助我更快地完成任务。这是我的循环:data := make(map[int][]float32)infile, err := os.Open("tmp/flow.tr")defer infile.Close()if err != nil {  panic(err)}reader := bufio.NewReader(infile)line, err := reader.ReadString('\n')for {  if len(line) == 0 {    break  }  if err != nil && err != io.EOF {    panic(err)  }  split_line := strings.Fields(line)  num_packets, err := strconv.ParseFloat(split_line[7], 32)  duration, err := strconv.ParseFloat(split_line[9], 32)  data[int(num_packets)] = append(data[int(num_packets)], float32(duration))  line, err = reader.ReadString('\n')}请注意,实际上我确实err在循环中检查了s-为了简洁起见,我省略了它。 google-pprof表明大部分时间被消耗在strings.Fields用strings.FieldsFunc,unicode.IsSpace和runtime.stringiter2。我怎样才能使它运行得更快?
查看完整描述

1 回答

?
慕哥6287543

TA贡献1831条经验 获得超10个赞

更换


split_line := strings.Fields(line)



split_line := strings.SplitN(line, " ", 11)


在1M行随机生成的文件中,模仿您上面提供的格式,速度提高了约4倍:


strings.Fields版本:在4.232525975s中完成


strings.SplitN版本:在1.111450755s中完成


效率的提高部分来自于避免在分割持续时间后解析和分割输入线,但大部分来自于SplitN中更简单的分割逻辑。即使拆分所有字符串,也不会比在持续时间之后停止花费很长时间。使用:


split_line := strings.SplitN(line, " ", -1)


1.554971313s完成


SplitN和字段不相同。字段假定标记由1个或多个空格字符限制,其中SplitN将标记视为由分隔符字符串限制的任何字符。如果输入的标记之间有多个空格,则split_line将为每对空格包含空标记。


排序和计算中位数不会增加太多时间。为了方便起见,我将代码更改为使用float64而不是float32。这是完整的程序:


package main


import (

    "bufio"

    "fmt"

    "os"

    "sort"

    "strconv"

    "strings"

    "time"

)


// SortKeys returns a sorted list of key values from a map[int][]float64.

func sortKeys(items map[int][]float64) []int {

    keys := make([]int, len(items))

    i := 0

    for k, _ := range items {

        keys[i] = k

        i++

    }

    sort.Ints(keys)

    return keys

}


// Median calculates the median value of an unsorted slice of float64.

func median(d []float64) (m float64) {

    sort.Float64s(d)

    length := len(d)

    if length%2 == 1 {

        m = d[length/2]

    } else {

        m = (d[length/2] + d[length/2-1]) / 2

    }

    return m

}


func main() {

    data := make(map[int][]float64)

    infile, err := os.Open("sample.log")

    defer infile.Close()

    if err != nil {

        panic(err)

    }

    reader := bufio.NewReaderSize(infile, 256*1024)


    s := time.Now()

    for {

        line, err := reader.ReadString('\n')

        if len(line) == 0 {

            break

        }

        if err != nil {

            panic(err)

        }

        split_line := strings.SplitN(line, " ", 11)

        num_packets, err := strconv.ParseFloat(split_line[7], 32)

        if err != nil {

            panic(err)

        }

        duration, err := strconv.ParseFloat(split_line[9], 32)

        if err != nil {

            panic(err)

        }

        pkts := int(num_packets)

        data[pkts] = append(data[pkts], duration)

    }


    for _, k := range sortKeys(data) {

        fmt.Printf("pkts: %d, median: %f\n", k, median(data[k]))

    }

    fmt.Println("\nCompleted in ", time.Since(s))

}

并输出:


pkts: 0, median: 0.498146

pkts: 1, median: 0.511023

pkts: 2, median: 0.501408

...

pkts: 99, median: 0.501517

pkts: 100, median: 0.491499


Completed in  1.497052072s


查看完整回答
反对 回复 2021-05-17
  • 1 回答
  • 0 关注
  • 258 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信