Optimization 优化Go文件读取程序

Optimization 优化Go文件读取程序,optimization,go,Optimization,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脚本,可以读取百万行日志文件,为所有不同持续时间的每个数据包创建一个列表,

我正在尝试处理一个日志文件,其中的每一行看起来如下所示:

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秒钟内计算出中间值

我正在玩围棋编程语言,我想我应该重写它,希望它能运行得更快

到目前为止,我一直很失望。仅将文件读入数据结构大约需要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.FieldsFunc
unicode.IsSpace
runtime.stringiter2
字符串.Fields

我如何使它运行得更快?

替换

split\u line:=strings.Fields(行)

split\u line:=strings.SplitN(第11行)

在一个1M行随机生成的文件上实现了约4倍的速度提升,该文件模仿了您上面提供的格式:

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

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

一些效率来自于能够避免在分割持续时间后解析和分割输入行,但大多数效率来自于SplitN中更简单的分割逻辑。即使拆分所有字符串也不会比在持续时间后停止要花费更长的时间。使用:

split\u line:=strings.SplitN(行“,-1)

完成时间为1.554971313s

SplitN和field不一样。字段假定标记由1个或多个空格字符限定,其中SplitN将标记视为由分隔符字符串限定的任何内容。如果您的输入在标记之间有多个空格,则split_行将包含每对空格的空标记

排序和计算中值不会增加太多时间。为了方便排序,我将代码改为使用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

对于k:=范围排序键(数据)=>对于,k:=范围排序键(数据)@Ekkehard.Horner:没错。在这种情况下,它似乎工作正常,因为索引和值都从零开始。谢谢