淺析Go語言中的緩沖區(qū)及其在fmt包中的應用
傳統(tǒng)的IO流程
在傳統(tǒng)的IO流程中,通常涉及以下幾個步驟:
- 打開文件或建立網(wǎng)絡連接:首先,需要打開文件或建立網(wǎng)絡連接,以便進行讀取或?qū)懭氩僮鳌_@通常涉及到操作系統(tǒng)提供的系統(tǒng)調(diào)用,如
open
、socket
等。 - 讀取或?qū)懭霐?shù)據(jù):一旦文件或網(wǎng)絡連接打開,就可以進行數(shù)據(jù)的讀取或?qū)懭氩僮鳌Wx取操作將從文件或網(wǎng)絡連接中獲取數(shù)據(jù),而寫入操作將將數(shù)據(jù)寫入文件或發(fā)送到網(wǎng)絡連接中。這些操作通常涉及到系統(tǒng)調(diào)用,如
read
和write
。 - 緩沖區(qū):為了提高IO性能,通常會使用緩沖區(qū)。緩沖區(qū)是一塊內(nèi)存區(qū)域,用于臨時存儲要讀取或?qū)懭氲臄?shù)據(jù)。數(shù)據(jù)首先被讀取到緩沖區(qū)中,然后從緩沖區(qū)中寫入到文件或網(wǎng)絡連接中,或者從緩沖區(qū)中讀取數(shù)據(jù)。
- 關閉文件或網(wǎng)絡連接:當讀取或?qū)懭氩僮魍瓿珊?,需要關閉文件或網(wǎng)絡連接。這通常涉及到系統(tǒng)調(diào)用,如
close
。
在傳統(tǒng)的IO流程中,每次讀取或?qū)懭氩僮鞫紩婕暗较到y(tǒng)調(diào)用,這會導致較高的開銷。為了提高性能,通常會使用緩沖區(qū)來減少系統(tǒng)調(diào)用的次數(shù)。緩沖區(qū)可以一次讀取或?qū)懭攵鄠€數(shù)據(jù),從而減少了系統(tǒng)調(diào)用的開銷。
緩沖區(qū)
上面我們了解到緩沖區(qū)這個概念,那什么是緩沖區(qū)呢?
內(nèi)存緩沖區(qū)是計算機中的一種臨時存儲區(qū)域,用于暫時存儲數(shù)據(jù)。它通常用于提高數(shù)據(jù)讀寫的效率,減少對底層存儲設備的頻繁訪問。
緩沖區(qū)的主要目的是在數(shù)據(jù)的生產(chǎn)者和消費者之間起到一個中間層的作用。當數(shù)據(jù)被生產(chǎn)者生成時,它首先被寫入緩沖區(qū),而不是直接寫入到目標存儲設備。然后,消費者可以從緩沖區(qū)中讀取數(shù)據(jù)。
緩沖區(qū)的大小是有限的,一旦緩沖區(qū)被填滿,生產(chǎn)者必須等待消費者讀取數(shù)據(jù),以便為新的數(shù)據(jù)騰出空間。同樣,如果緩沖區(qū)為空,消費者必須等待生產(chǎn)者生成新的數(shù)據(jù)。
內(nèi)存緩沖區(qū)可以用于各種場景,比如:
- 文件讀寫:在讀取或?qū)懭氪笪募r,可以使用內(nèi)存緩沖區(qū)來提高讀寫性能。數(shù)據(jù)首先被讀取到緩沖區(qū)中,然后批量地寫入或讀取到磁盤上的文件。
- 網(wǎng)絡通信:在網(wǎng)絡通信中,數(shù)據(jù)通常需要經(jīng)過網(wǎng)絡協(xié)議的封裝和解析。使用內(nèi)存緩沖區(qū)可以將數(shù)據(jù)暫時存儲起來,以便進行協(xié)議處理和網(wǎng)絡傳輸。
- 數(shù)據(jù)庫操作:在數(shù)據(jù)庫操作中,使用內(nèi)存緩沖區(qū)可以提高數(shù)據(jù)的讀寫性能。數(shù)據(jù)首先被寫入緩沖區(qū),然后批量地寫入到數(shù)據(jù)庫中,或者從緩沖區(qū)中讀取數(shù)據(jù)進行查詢。
需要注意的是,內(nèi)存緩沖區(qū)只是一個臨時存儲區(qū)域,數(shù)據(jù)在緩沖區(qū)中并不是持久化的。一旦程序結束或緩沖區(qū)被清空,緩沖區(qū)中的數(shù)據(jù)就會丟失。因此,在使用內(nèi)存緩沖區(qū)時需要確保數(shù)據(jù)的正確性和一致性。
go緩沖區(qū)
在Go語言中,緩沖區(qū)的大小是由創(chuàng)建緩沖區(qū)時指定的參數(shù)決定的。在標準庫中,可以使用bufio
包提供的NewWriterSize
函數(shù)創(chuàng)建一個指定大小的緩沖區(qū)。
默認情況下,bufio.Writer
的緩沖區(qū)大小為4096字節(jié)(4KB),即調(diào)用bufio.NewWriter
創(chuàng)建的緩沖區(qū)大小為4096字節(jié)。這是因為4096字節(jié)是一個常見的磁盤塊大小,對于大多數(shù)應用場景來說,這個大小已經(jīng)足夠了。
如果需要自定義緩沖區(qū)的大小,可以使用bufio.NewWriterSize
函數(shù)來指定緩沖區(qū)的大小。例如,可以通過bufio.NewWriterSize(writer, 8192)
來創(chuàng)建一個大小為8192字節(jié)(8KB)的緩沖區(qū)。
為什么
為什么go編程中要設置緩沖區(qū)呢?其實我們上面都有提到:設置緩沖區(qū)的一個主要目的就是為了減少頻繁的IO操作。
在進行IO操作時,例如讀取或?qū)懭胛募?,每次都直接操作底層的存儲設備(如磁盤或網(wǎng)絡)可能會導致性能下降。這是因為每次IO操作都需要進行系統(tǒng)調(diào)用,這涉及到內(nèi)核和用戶空間之間的上下文切換,以及硬件設備的訪問延遲。
通過使用緩沖區(qū),可以將數(shù)據(jù)暫時存儲在內(nèi)存中,而不是直接與底層存儲設備進行交互。這樣可以將多個小的IO操作合并為一個大的IO操作,從而減少了系統(tǒng)調(diào)用的次數(shù)。這種批量處理的方式通常比頻繁的小IO操作更高效。
此外,緩沖區(qū)還可以提供更好的數(shù)據(jù)傳輸效率。當數(shù)據(jù)被寫入緩沖區(qū)時,實際的IO操作可以被推遲到緩沖區(qū)被填滿或手動刷新緩沖區(qū)時才執(zhí)行。這樣可以減少IO操作的次數(shù),提高數(shù)據(jù)傳輸?shù)男省?/p>
go 緩沖區(qū)(Buffer)是分配在堆還是棧?
在Go語言中,緩沖區(qū)(Buffer)的申請是在堆上進行的。
Go語言中的??臻g是有限的,而且棧上的內(nèi)存分配和釋放是由編譯器自動管理的,無法手動控制。因此,較大的緩沖區(qū)無法放在棧上進行申請。
相反,Go語言中的堆空間是用于動態(tài)分配內(nèi)存的,可以手動控制內(nèi)存的申請和釋放。當我們使用make
關鍵字創(chuàng)建一個切片或映射時,內(nèi)存就會在堆上進行動態(tài)分配。而bufio
包中的緩沖區(qū)也是通過make
函數(shù)在堆上進行申請的。
緩沖區(qū)的申請通常是在創(chuàng)建緩沖區(qū)時進行的,例如使用bufio.NewWriter
或bufio.NewWriterSize
函數(shù)來創(chuàng)建一個緩沖區(qū)對象。這個過程會調(diào)用make
函數(shù)來分配足夠大小的內(nèi)存,并返回一個指向該內(nèi)存的指針。
fmt打印
示例
fmt.Println("Hello, world!"),大家平時用得最多了,這不就是打印輸出到控制臺嘛
當執(zhí)行fmt.Println("Hello, world!")
命令時,會調(diào)用fmt
包內(nèi)的Println
函數(shù)來打印輸出。
首先,Println
函數(shù)會根據(jù)傳入的參數(shù)列表構建一個字符串,并將其傳遞給Fprintln
函數(shù)。Fprintln
函數(shù)是fmt
包內(nèi)部的一個輔助函數(shù),它會將構建的字符串寫入到標準輸出(即控制臺)。
在Fprintln
函數(shù)內(nèi)部,它會調(diào)用newPrinter
函數(shù)來創(chuàng)建一個pp
(printer)對象。pp
對象是printer
結構體的實例,它包含了打印輸出的相關配置和狀態(tài)信息。
接下來,Fprintln
函數(shù)會調(diào)用pp.print
方法來實際執(zhí)行打印輸出的操作。在print
方法中,它會根據(jù)配置的格式化選項,將構建的字符串寫入到pp.buf
緩沖區(qū)中。
如果緩沖區(qū)已滿,或者遇到換行符(\n
),print
方法會調(diào)用pp.write
方法將緩沖區(qū)的內(nèi)容寫入到標準輸出。write
方法會使用os.Stdout
作為目標,將緩沖區(qū)的內(nèi)容寫入到控制臺。
最后,Fprintln
函數(shù)會調(diào)用pp.free
方法來釋放pp
對象占用的內(nèi)存,以及清空緩沖區(qū)。
總結起來,當執(zhí)行fmt.Println("Hello, world!")
命令時,fmt
包內(nèi)部會構建打印輸出的字符串,并將其寫入到標準輸出。這個過程涉及到字符串的構建、緩沖區(qū)的管理和標準輸出的寫入。通過使用printer
結構體和相關方法,fmt
包實現(xiàn)了方便的打印輸出功能。
源碼查看
// ... func main() { fmt.Println("Hello, world!") } // fmt 包 // ... func Println(a ...any) (n int, err error) { return Fprintln(os.Stdout, a...) } // ... func Fprintln(w io.Writer, a ...any) (n int, err error) { p := newPrinter() p.doPrintln(a) n, err = w.Write(p.buf) p.free() return }
當打印內(nèi)容很大怎么辦
當打印的內(nèi)容超出了緩沖區(qū)的大小時,fmt
包會動態(tài)擴展緩沖區(qū)的大小,以容納更大的數(shù)據(jù),并完整地輸出到標準輸出。
在執(zhí)行打印操作時,fmt
包會檢查緩沖區(qū)的剩余空間是否足夠容納當前要打印的內(nèi)容。如果空間不足,fmt
包會自動擴展緩沖區(qū)的大小,通常是將緩沖區(qū)的大小翻倍。
例如,在默認情況下,fmt
包的緩沖區(qū)大小為4KB。如果要打印的內(nèi)容超過了4KB,fmt
包會自動將緩沖區(qū)擴展到8KB,以容納更多的數(shù)據(jù)。如果仍然不夠,會繼續(xù)擴展到16KB,以此類推,直到能夠容納所有的數(shù)據(jù)。
當緩沖區(qū)大小足夠容納要打印的內(nèi)容時,fmt
包會將數(shù)據(jù)寫入緩沖區(qū)中。當緩沖區(qū)滿了或者遇到換行符(\n
)時,fmt
包會將緩沖區(qū)的內(nèi)容寫入到標準輸出,確保完整地輸出所有的數(shù)據(jù)。
也就是說我有一個8k的打印內(nèi)容,而緩沖區(qū)大小為4KB,那么在第一次寫入緩沖區(qū)后,緩沖區(qū)將被填滿。此時,fmt
包會進行一次IO操作,將緩沖區(qū)的內(nèi)容寫入標準輸出。
由于緩沖區(qū)已滿,第二次寫入操作將觸發(fā)另一次IO操作,將剩余的內(nèi)容寫入標準輸出。
因此,在這種情況下,fmt
包會進行兩次IO操作,將完整的8KB內(nèi)容寫入標準輸出。第一次是在緩沖區(qū)填滿后,第二次是在第二次寫入時。
到此這篇關于淺析Go語言中的緩沖區(qū)及其在fmt包中的應用的文章就介紹到這了,更多相關Go緩沖區(qū)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
golang使用sync.singleflight解決熱點緩存穿透問題
在go的sync包中,有一個singleflight包,里面有一個?singleflight.go文件,代碼加注釋,一共200行出頭,通過?singleflight可以很容易實現(xiàn)緩存和去重的效果,避免重復計算,接下來我們就給大家詳細介紹一下sync.singleflight如何解決熱點緩存穿透問題2023-07-07以go為例探究beyla從環(huán)境變量BEYLA_OPEN_PORT發(fā)現(xiàn)進程原理
這篇文章主要為大家介紹了以golang進程為例,研究beyla從環(huán)境變量BEYLA_OPEN_PORT(即通過端口)發(fā)現(xiàn)進程的原理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12詳解Golang net/http包中的RoundTripper接口
RoundTripper 是 net/http 包中的一個接口,定義了處理 HTTP 請求返回和響應的方法,是 http.Client 結構體中執(zhí)行 http 請求的核心部分,本文將詳細的給大家介紹Golang RoundTripper接口,需要的朋友可以參考下2023-09-09