Upgrade to Pro — share decks privately, control downloads, hide ads and more …

善用 Go 語言效能測試工具來提升執行效率

Bo-Yi Wu
October 24, 2020

善用 Go 語言效能測試工具來提升執行效率

在 AI 訓練模型前,都需要經過大量的資料處理,而資料處理的速度在整個流程內扮演很重要的角色,寫出高效能的 Parser 能降低整體處理時間,那如何評估程式效能如何,以及如何快速找到效能瓶頸?本議程會帶大家了解 Go 語言內建的效能測試工具,透過 Benchmark 來找出程式效能瓶頸的地方,快速改善及優化,讓整個系統流程更順暢。也會順道分享 Go 在字串處理優化的一些小技巧。聽過此議程相信您對 Go 語言會有更深入的了解,如果你想寫出有效率的程式碼,本議程一定不能錯過。

Bo-Yi Wu

October 24, 2020
Tweet

More Decks by Bo-Yi Wu

Other Decks in Technology

Transcript

  1. 善⽤ Go 語⾔效能測試
    ⼯具來提升執⾏效率
    Bo-Yi Wu
    2020/10/24

    View Slide

  2. About me
    • Software Engineer in Mediatek
    • Member of Drone CI/CD Platform
    • Member of Gitea Platform
    • Member of Gin Golang Framework
    • Maintain Some GitHub Actions Plugins.

    View Slide

  3. 使⽤ AI 來偵測異常
    減少晶片設計發⽣的問題

    View Slide

  4. View Slide

  5. Data 資料量
    • 1 ~ 2 萬個檔案
    • 每個檔案 1000 萬⾏以上
    • 總共 8 億 ~ 1x 億個資料點 (單⼀製程)

    View Slide

  6. 效能結果 (原先使⽤ 400 台機器)
    1BSTFS ,# .# .
    (# .)8

    PME





    VOJUTFDPOE
    ఏঋYYഒ
    DPSF(
    লԼ)8㑌࣍୆(3BN7.

    View Slide

  7. Python to Golang
    ఏঋ଎౓۰౸ළᰍ

    View Slide

  8. 效能結果 (原先使⽤ 400 台機器)
    1BSTFS ,# .# .
    (# .)8

    PME





    VOJUTFDPOE
    ఏঋYYഒ
    DPSF(
    লԼ)8㑌࣍୆(3BN7.

    View Slide

  9. Write Parser in Golang
    ᅷยိݯIUUQT[IVBOMBO[IJIVDPNQ

    View Slide

  10. %BUB㚎༰֨ࣜ

    View Slide

  11. ⾃⾏研發 Parser 原因
    • 提⾼部⾨產能 (太多單位有分析需求)
    • 減少 Computing Farm 使⽤ (省錢)
    • 整合 AI Model 花費時間過久 (inference time
    不到⼀秒啊)
    • ⽬前在市⾯上找不到任何⼀套開源 Parser?

    View Slide

  12. 效能結果 (原先使⽤ 400 台機器)
    1BSTFS ,# .# .
    (# .)8

    PME





    VOJUTFDPOE
    ఏঋYYഒ
    DPSF(
    লԼ)8㑌࣍୆(3BN7.

    View Slide

  13. 第⼀個版本
    var group = regexp.MustCompile(`^(\s*)(\S+)\s*\((.*?)\)\s*{\s*$`)
    var groupDone = regexp.MustCompile(`^\s*}\s*$`)
    /// Simple attribute compile.
    /// key : value;
    var simpleAttribute = regexp.MustCompile(`^(\s*)(\S+)\s*:\s*(.+)\s*;.*$`)
    var specialSimpleAttribute = regexp.MustCompile(`^(\s*)(\S+)\s*:\s*(.+)\s*$`)
    /// Complex attribute compile.
    /// key (valueList);
    var complexAttribute = regexp.MustCompile(`^(\s*)(\S+)\s*(\(.+\))\s*;.*$`)
    var specialComplexAttribute = regexp.MustCompile(`^(\s*)(\S+)\s*(\(.+\))\s*$`)
    Ꮘೳළᰍ

    View Slide

  14. 效能評估⽅式

    View Slide

  15. 記錄程式執⾏時間
    $ time your_program
    SFBMNT
    VTFSNT
    TZTNT

    View Slide

  16. 埋入程式碼
    func main() {
    start := time.Now()
    fmt.Println(foobar(1000, 10))
    elapsed := time.Since(start)
    log.Printf("foobar took %s", elapsed)
    }

    View Slide

  17. Go 內建性能⼯具

    View Slide

  18. Go benchmark
    $ go test -v -bench=foobar -run=none

    View Slide

  19. var fixtures = []struct {
    path string
    }{
    {"cells.lib"},
    {"cells_timing.lib"},
    {"sample2_Late.lib"},
    {"small.lib"},
    }
    func withFixtures(b *testing.B, fn func(b *testing.B, input []byte)) {
    for _, fix ::= range fixtures {
    data, err ::= ioutil.ReadFile(filepath.Join(".../testdata", fix.path))
    if err !!= nil {
    b.Fatal(err)
    }
    b.Run(fix.path, func(b *testing.B) {
    b.ReportAllocs()
    b.SetBytes(int64(len(data)))
    b.ResetTimer()
    fn(b, data)
    })
    }
    }
    3VO1BSBMMFMฏߦ႔ཧ
    3FQPSU"MMPDTهԱᱪ࢖༻ྔ
    4FU#ZUFTᄸ࣍॥؀࢖༻ଟগهԱᱪ
    3FTFU5JNFSॏஔ5JNFSܭࢉ

    View Slide

  20. func BenchmarkWhitespaceArrayInlined(b *testing.B) {
    withFixtures(b, benchmarkWhitespaceArrayInlined)
    }
    func BenchmarkWhitespaceArrayLoop(b *testing.B) {
    withFixtures(b, benchmarkWhitespaceArray)
    }
    func BenchmarkWhitespaceSwitch(b *testing.B) {
    withFixtures(b, benchmarkWhitespaceSwitch)
    }
    func BenchmarkWhitespaceIf(b *testing.B) {
    withFixtures(b, benchmarkWhitespaceIf)
    }
    func BenchmarkWhitespaceIfInlined(b *testing.B) {
    withFixtures(b, benchmarkWhitespaceIfInlined)
    }
    ଟ䈕Ҋෆಉํ๏Ꮘೳଌࢼ

    View Slide

  21. View Slide

  22. RunParallel (平⾏處理)
    根據系統 CPU 數量加速處理
    HPUFTUDQV [email protected]

    View Slide

  23. n ::= uint64(0)
    numProcs ::= b.parallelism * runtime.GOMAXPROCS(0)
    var wg sync.WaitGroup
    wg.Add(numProcs)
    for p ::= 0; p < numProcs; p+++ {
    go func() {
    defer wg.Done()
    pb ::= &PB{
    globalN: &n,
    grain: grain,
    bN: uint64(b.N),
    }
    body(pb)
    }()
    }
    wg.Wait()
    CQBSBMMFMJTN༬ઃҝ
    ༬ઃҝ$16ݸᏐ
    IUUQTQLHHPEFWUFTUJOH#3VO1BSBMMFM

    View Slide

  24. View Slide

  25. ུաෆඞཁతܭࢉ
    C4UPQ5JNFSC4UBSU5JNFS
    init()
    b.ResetTimer()
    for i::=0; ifoobar01()
    b.StopTimer()
    foobar02()
    b.StartTimer()
    }

    View Slide

  26. 測試多久+測試次數
    CFODIUJNFIT CFODIUJNFY

    View Slide

  27. 產⽣多組樣本數據
    DPVOU

    View Slide

  28. ᒬຊᏐᎦ
    Ҡআ༗ྀٙతࢿ㘤

    View Slide

  29. Benchmark 比較
    golang.org/x/perf/cmd/benchstat

    View Slide

  30. 比較兩種不同版本差異
    HPUFTUWDPVOUCFODIYYYYYSVOOPOFMFYFScUFFPMEUYU
    HPUFTUWDPVOUCFODIYYYYYSVOOPOFMFYFScUFFOFXUYU

    View Slide

  31. ᄸҰଌࢼ܈ᱪ
    benchstat old.txt

    View Slide

  32. func (m *Metrics) computeStats() {
    /// Discard outliers.
    values ::= stats.Sample{Xs: m.Values}
    q1, q3 ::= values.Percentile(0.25), values.Percentile(0.75)
    lo, hi ::= q1-1.5*(q3-q1), q3+1.5*(q3-q1)
    for _, value ::= range m.Values {
    if lo <<= value &&& value <<= hi {
    m.RValues = append(m.RValues, value)
    }
    }
    /// Compute statistics of remaining data.
    m.Min, m.Max = stats.Bounds(m.RValues)
    m.Mean = stats.Mean(m.RValues)
    }
    ഒ࢛෼Ґڑํࣜ
    ࣗಈҠআ༗ྀٙతᏐᎦ
    golang.org/x/perf/cmd/benchstat

    View Slide

  33. Ꮘೳલޙൺֱ
    benchstat old.txt new.txt

    View Slide

  34. 如何找出效能瓶頸
    ࢖༻QQSPG෼ੳ$16҃هԱᱪ࢖༻ྔ
    DQVQSPpMFDQVPVUNFNQSPpMFNFNPVU

    View Slide

  35. HPUFTUCFODIYYYYYDPVOUSVO?DQVQSPpMFDQVPVUMFYFS
    HPUPPMQQSPGDQVPVU

    View Slide

  36. ؃ᄸҰവࣔଌࢼᏈೳ݁Ռ MJTUGVOD

    View Slide

  37. ݺڣ࣍Ꮠ౷ܭ QFFLGVOD

    View Slide

  38. QQSPG㗞ੜ֤छෆಉใࠂ

    View Slide

  39. ⽤ Web 直接看比較快
    HPUPPMQQSPGIUUQDQVPVU

    View Slide

  40. View Slide

  41. 第⼀版 Parser 效能
    3FHFY1BSTFSWT-FYFS1BSTFS

    View Slide

  42. ఏঋେ໿ഒᏈೳ

    View Slide

  43. ఏঋେ໿ഒᏈೳ

    View Slide

  44. 效能結果
    1BSTFS ,# .# .
    (# .)8

    PME





    VOJUTFDPOE
    ఏঋ઀ۙഒ
    DPSF(

    View Slide

  45. /FYU5PLFOՖඅ࣌ؒ࠷ٱ

    View Slide

  46. View Slide

  47. OFX5PLFOՖඅ኷ٱ

    View Slide

  48. func (l *Lexer) NextToken() {
    LOOP:
    switch l.char {
    case '{':
    l.token.Type = token.LeftBrace
    l.token.Literal = []byte{l.char}
    l.token.Line = l.line
    l.token.Start = l.position
    l.token.End = l.position + 1
    case ' ', '\t', '\r', '\n':
    if l.char === '\n' {
    l.line+++
    }
    l.readChar()
    goto LOOP

    View Slide

  49. 4LJQ8IJUFTQBDF
    func (l *Lexer) NextToken() {
    l.skipWhitespace()
    switch l.char {
    case '{':
    l.token.Type = token.LeftBrace
    l.token.Literal = []byte{l.char}
    l.token.Line = l.line
    l.token.Start = l.position
    l.token.End = l.position + 1

    View Slide

  50. 4LJQ8IJUFTQBDFमਖ਼݁Ռ

    View Slide

  51. values ( \
    "0, 0.00014625, 0.0003375, 0.00043875, 0.000545625", \
    "0, 0.065, 0.15, 0.195, 0.2425, 0.295, 0.35, 0.42" \
    );
    values ( "0.1, 0.2, 0.3", \
    "0.11, 0.21, 0.31", \
    "0.12, 0.22, 0.32" );
    index_2 ("0.02239")
    index_2 (TVDD, 0.633)
    statetable ( " D CP SE SI " , "Q" )
    ႔ཧଟछࣈ۲ ૬Ճ

    View Slide

  52. func (p *Parser) parseMultipleValue() ast.Value {
    val ::= ast.Literal{Type: ast.LiteralType}
    values ::= []string{}
    for !p.currentTokenTypeIs(token.EOF) {
    if p.currentTokenTypeIs(token.Escape) {
    p.nextToken()
    } else if p.currentTokenTypeIs(token.String) {
    values = append(values, p.parseString())
    p.nextToken()
    } else if p.currentTokenTypeIs(token.Comma) {
    values = append(values, p.parseString())
    p.nextToken()
    } else if p.currentTokenTypeIs(token.RightParentheses) {
    break
    }
    }
    val.Value = strings.Join(values, "")
    return val
    }

    View Slide

  53. func (p *Parser) parseMultipleValue() ast.Value {
    val ::= ast.Literal{Type: ast.LiteralType}
    buf.Reset()
    for !p.currentTokenTypeIs(token.EOF) {
    if p.currentTokenTypeIs(token.Escape) {
    p.nextToken()
    } else if p.currentTokenTypeIs(token.String) {
    buf.WriteString(p.parseString())
    p.nextToken()
    } else if p.currentTokenTypeIs(token.Comma) {
    buf.WriteString(p.parseString())
    p.nextToken()
    } else if p.currentTokenTypeIs(token.RightParentheses) {
    break
    }
    }
    val.Value = buf.String()
    return val
    }
    ༻CZUFT#V⒎FSऔ୅

    View Slide

  54. ࣈ۲႔ཧमਖ਼݁Ռ

    View Slide

  55. values ( \
    "0, 0.00014625, 0.0003375, 0.00043875, 0.000545625", \
    "0, 0.065, 0.15, 0.195, 0.2425, 0.295, 0.35, 0.42" \
    );
    values ( "0.1, 0.2, 0.3", \
    "0.11, 0.21, 0.31", \
    "0.12, 0.22, 0.32" );
    index_2 ("0.02239")
    index_2 (TVDD, 0.633)
    statetable ( " D CP SE SI " , "Q" )
    ႔ཧଟछࣈ۲ ૬Ճ

    View Slide

  56. case ast.PropertyParentheses:
    var val ast.Value
    if !p.peekTokenTypeIs(token.RightParentheses) {
    val = p.parseMultipleValue()
    prop.Value = val
    } else {
    /// single value
    /// index_2 ("0.02239")
    val = p.parseValue()
    prop.Value = val
    }
    ࣈ۲႔ཧᬓाௐ੔

    View Slide

  57. ࣈ۲႔ཧᬓाௐ੔

    View Slide

  58. case ',':
    l.token.Type = token.Comma
    l.token.Literal = []byte{l.char}
    l.token.Line = l.line
    l.token.Start = l.position
    l.token.End = l.position + 1
    case '"':
    l.token.Type = token.String
    l.token.Literal = l.readString()
    l.token.Line = l.line
    l.token.Start = l.position
    l.token.End = l.position + 1
    ݮগهԱᱪ࢖༻ྔ

    View Slide

  59. case ',':
    l.token.Type = token.Comma
    l.token.Start = l.position
    l.token.End = l.position + 1
    l.token.Literal = l.Data[l.token.Start:l.token.End]
    l.token.Line = l.line
    case '"':
    l.token.Type = token.String
    l.token.Literal = l.readString()
    l.token.Line = l.line
    l.token.Start = l.position
    l.token.End = l.position + 1
    ݮগهԱᱪ࢖༻ྔ

    View Slide

  60. ݮগهԱᱪ࢖༻ྔ

    View Slide

  61. View Slide

  62. ᩇऔძҾᥒؒతࣈ۲

    View Slide

  63. func (l *Lexer) readString() []byte {
    l.position+++
    data ::= l.Data[l.position:]
    _, length ::= findStringLen(data)
    l.position += length
    l.readPosition = l.position + 1
    return data[:length]
    }
    ༏ԽSFBETUJOH

    View Slide

  64. func findStringLen(data []byte) (isValid bool, length int) {
    for {
    idx ::= bytes.IndexByte(data, '"')
    if idx === -1 {
    return false, len(data)
    }
    if idx === 0 ||| (idx > 0 &&& data[idx-1] !!= '\\') {
    return true, length + idx
    }
    length += idx + 1
    data = data[idx+1:]
    }
    }
    ፙ౸ୈҰݸ݁ଋූᥒ

    View Slide

  65. ༏Խ݁Ռ

    View Slide

  66. ༏ԽSFBE*EFOUJpFS

    View Slide

  67. /// check previous token
    /// example:
    /// voltage_map ( TVDD, 0.633);
    /// voltage_map (TVDD, 0.633);
    if len(l.token.Literal) > 0 &&& l.token.Literal[0] === '(' {
    l.token.Type = token.String
    l.token.Literal = l.readMapString()
    l.token.Line = l.line
    l.token.Start = l.position
    l.token.End = l.position + 1
    return
    }
    ࢿྉྔ኷େ
    ࢖༻CZUFT*OEFY#ZUF

    View Slide

  68. ༏Խ݁Ռ

    View Slide

  69. मਖ਼4LJQ8IJUF4QBDF'VOD
    if whitespace[l.Data[l.position]] {
    if l.Data[l.position] === '\n' {
    l.line+++
    }
    l.readChar()
    goto LOOP
    }
    var whitespace = [256]bool{
    ' ': true,
    '\t': true,
    '\n': true,
    '\r': true,
    }

    View Slide

  70. Ꮘೳଌࢼใࠂ

    View Slide

  71. 效能結果 (原先使⽤ 400 台機器)
    1BSTFS ,# .# .
    (# .)8

    PME





    VOJUTFDPOE
    ఏঋYYഒ
    DPSF(
    লԼ)8㑌࣍୆(3BN7.

    View Slide

  72. Ꮘೳใࠂ -FYFS

    View Slide

  73. Ꮘೳใࠂ 1BSTFS

    View Slide