优化Go文件读取程序

我正在尝试处理一个日志文件,其每一行看起来像这样:


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。


我怎样才能使它运行得更快?


九州编程
浏览 259回答 1
1回答

慕哥6287543

更换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 mainimport (    "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.498146pkts: 1, median: 0.511023pkts: 2, median: 0.501408...pkts: 99, median: 0.501517pkts: 100, median: 0.491499Completed in  1.497052072s
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go