2012-10-06 39 views
2

我想处理一个日志文件,各行其看起来是这样的:优化围棋文件读取程序

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秒 - 我省略了,对于简洁。 google-pprof表示大部分时间花费在strings.Fields之前strings.FieldsFunc,unicode.IsSpaceruntime.stringiter2

如何让此运行更快?

回答

7

更换

split_line := strings.Fields(line)

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

墓内〜上的1M线4倍速度的提高随机生成的文件,该文件模仿你上面提供的格式:

字符串。 Fields版本:完成于4.232525975s

strings.SplitN版本:在1.111450755s

一些效率来自能够避免解析和分裂的持续时间之后的输入线完成了拆分,但大部分来自于简单的拆分逻辑SplitN。即使分割所有的字符串也不会比持续时间之后停止更长的时间。使用:

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

在1.554971313s

SplitN和领域的已完成是不一样的。字段假定令牌由一个或多个空格字符限定,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 
+1

为K:=范围SORTKEYS(数据)=>为_,K:=范围SORTKEYS(数据) –

+0

尼斯的答案!谢谢。 :-) –

+0

@ Ekkehard.Horner:对,你是。它似乎在这种情况下正常工作,因为索引和值都从零开始。谢谢。 – markc