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ます。100 万行のログ ファイルを読み取り、さまざまな期間のパケット数ごとにリストを作成し、それらのリストを並べ替えて、約 3 秒で中央値を計算できる Python スクリプトを用意しました。

私は 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')
}

ループ内で s を実際にチェックしていることに注意してくださいerr。簡潔にするために省略しました。 は、大部分の時間が、、およびgoogle-pprofで費やされていることを示しstrings.Fieldsます。strings.FieldsFuncunicode.IsSpaceruntime.stringiter2

これをより速く実行するにはどうすればよいですか?

4

1 に答える 1

7

交換する

split_line := strings.Fields(line)

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

上記で提供した形式を模倣した 1M 行のランダムに生成されたファイルで、最大 4 倍の速度向上が得られました。

strings.Fields バージョン: 4.232525975 秒で完了

strings.SplitN バージョン: 1.111450755 秒で完了

効率の一部は、期間が分割された後に入力行の解析と分割を回避できることからもたらされますが、そのほとんどは、SplitN のより単純な分割ロジックからもたらされます。すべての文字列を分割しても、デュレーションの後に停止するよりもそれほど長くはかかりません。使用:

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

1.554971313 秒で完了

SplitN と Fields は同じではありません。フィールドは、トークンが 1 つ以上の空白文字で区切られていることを前提としています。ここで、SplitN はトークンを区切り文字列で区切られたものとして扱います。入力のトークン間に複数のスペースがある場合、split_line にはスペースのペアごとに空のトークンが含まれます。

中央値を並べ替えて計算しても、それほど時間はかかりません。ソート時の便宜上、float32 ではなく float64 を使用するようにコードを変更しました。完全なプログラムは次のとおりです。

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
于 2012-10-06T03:25:17.577 に答える