自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

算法競(jìng)賽題目中 Golang IO 方法及其火焰圖對(duì)比

開發(fā) 前端
為什么 bufio.NewScanner + strconv.Atoi 比 bufio.NewReader 快?(一)bufio.NewScanner 直接使用 scanner.Split(bufio.ScanWords) 中的邏輯掃描輸入流;(二)strconv.Atoi 無需做任何反射,其只實(shí)現(xiàn) string 到 int 的轉(zhuǎn)換。任務(wù)越細(xì)致,越好做優(yōu)化。

這里不討論過于 hacky 的方法。對(duì) IO 速度極致要求(甚至在數(shù)學(xué)上進(jìn)行優(yōu)化)可參考:


本文討論 5\n1 2 3 4 5E 這類格式的 Stdin 。具體對(duì)比的三種讀入和兩種輸出如下。

package main

import (
 "bufio"
 "fmt"
 "os"
 "runtime/pprof"
 "strconv"
)

func main() {
 f, _ := os.Create("3-2")
 defer f.Close()
 pprof.StartCPUProfile(f)
 defer pprof.StopCPUProfile()

 // n, a := read_stdin_1()
 // n, a := read_stdin_2()
 n, a := read_stdin_3()
 quick_sort(a, 0, n-1)
 // write_stdout_1(n, a)
 write_stdout_2(n, a)
}

// 使用 fmt.Scanf 讀入
func read_stdin_1() (int, []int) {
 var n int
 fmt.Scanf("%d", &n)
 a := make([]int, n)
 for i := 0; i < n; i++ {
  fmt.Scanf("%d", &a[i])
 }
 return n, a
}

// 使用 bufio.NewReader 讀入
func read_stdin_2() (int, []int) {
 in := bufio.NewReader(os.Stdin)

 var n int
 fmt.Fscan(in, &n)
 a := make([]int, n)
 for i := 0; i < n; i++ {
  fmt.Fscan(in, &a[i])
 }
 return n, a
}

// 使用 bufio.NewScanner 讀入
func read_stdin_3() (int, []int) {
 scanner := bufio.NewScanner(os.Stdin)
 scanner.Split(bufio.ScanWords)

 var n int
 scanner.Scan()
 n, _ = strconv.Atoi(scanner.Text())
 a := make([]int, n)
 for i := 0; i < n; i++ {
  scanner.Scan()
  a[i], _ = strconv.Atoi(scanner.Text())
 }
 return n, a
}

// 使用 fmt.Printf 輸出
func write_stdout_1(n int, a []int) {
 for i := 0; i < n; i++ {
  fmt.Printf("%d ", a[i])
 }
}

// 使用 bufio.NewWriter ,手動(dòng) Flush 輸出
func write_stdout_2(n int, a []int) {
 out := bufio.NewWriter(os.Stdout)
 defer out.Flush()

 for i := 0; i < n; i++ {
  if i > 0 {
   out.WriteByte(' ')
  }
  out.WriteString(strconv.Itoa(a[i]))
 }
}

測(cè)試結(jié)果如下:

read

write

time

1

1

5265ms

2

1

1435ms

3

1

976ms

1

2

5156ms

2

2

452ms

3

2

145ms

測(cè)試平臺(tái):

先說結(jié)論,再看火焰圖

1. 為什么 bufio.NewReader(os.Stdin) + fmt.Fscan 比 fmt.Scanf 快?

(一)每次 fmt.Scanf 都是一次系統(tǒng)調(diào)用 Syscall ,而 bufio.NewReader(os.Stdin) 是在內(nèi)存中緩存了 bufio 的 buf

(二)doScanf 每次會(huì)去解析 %d ,而 fmt.Fscan(in, &a[i]) 是根據(jù)類型的反射去直接找對(duì)對(duì)應(yīng)的 Scan + Parse 函數(shù),如下

// 這里的 arg 就是我們傳進(jìn)來的 &n 和 &a[i]
func (s *ss) scanOne(verb rune, arg any) {
 s.buf = s.buf[:0]
 var err error
 // If the parameter has its own Scan method, use that.
 if v, ok := arg.(Scanner); ok {
  err = v.Scan(s, verb)
  if err != nil {
   if err == io.EOF {
    err = io.ErrUnexpectedEOF
   }
   s.error(err)
  }
  return
 }

 switch v := arg.(type) {
        ...
 case *int:
  *v = int(s.scanInt(verb, intBits))
        ...

2. 為什么 bufio.NewScanner + strconv.Atoi 比 bufio.NewReader 快?

(一)bufio.NewScanner 直接使用 scanner.Split(bufio.ScanWords) 中的邏輯掃描輸入流

(二)strconv.Atoi 無需做任何反射,其只實(shí)現(xiàn) string 到 int 的轉(zhuǎn)換。任務(wù)越細(xì)致,越好做優(yōu)化

3. 為什么 bufio.NewWriter + strconv.Itoa 比 fmt.Printf 快?

(一)顯而易見,有 buf 減少了系統(tǒng)調(diào)用 Syscall 的次數(shù)

(二)手動(dòng)控制 Flush ,可以減少系統(tǒng)調(diào)用 Syscall 的次數(shù)

(三)strconv.Itoa 同理無需反射或解析 %d

來看火焰圖 Flame Graph

火焰圖測(cè)試數(shù)據(jù):

  1. n = 1e6
  2. 整數(shù)取值范圍:[1, 1e9]

read 1read 1

read 2read 2

read 3read 3

如上,方法二方法三 Syscall 占比小非常多。

write 1write 1

write 2write 2

責(zé)任編輯:武曉燕 來源: Piper蛋窩
相關(guān)推薦

2020-03-13 07:40:36

Plato數(shù)據(jù)分析

2010-06-18 13:15:07

UML狀態(tài)機(jī)圖

2023-05-30 09:07:06

CPU性能火焰圖

2024-06-21 13:11:30

2023-08-03 08:48:07

Golang接口

2019-10-14 15:34:10

Web 開發(fā)框架

2023-12-31 19:41:04

PHP性能終端

2022-02-03 23:43:51

人工智能程序員AlphaCode

2017-06-23 09:41:04

服務(wù)器負(fù)載火焰圖

2023-08-02 09:07:27

Golangio 包

2021-02-02 08:11:50

火焰圖組件技術(shù)

2012-03-31 13:55:15

Java

2022-05-17 12:23:25

排序算法面試

2012-02-22 14:12:08

算法

2010-06-18 09:05:04

UML交互圖

2023-08-28 17:16:51

Golangio 包

2022-01-05 08:56:20

Go火焰圖編程

2010-07-12 09:30:34

UML模型圖

2024-07-31 08:28:38

2025-02-14 08:56:09

GoroutineContextChannel
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)