日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
怎么選擇Go文件讀取方案

創(chuàng)建不同大小的文件

首先,我們需要有比較對(duì)象。鑒于電腦磁盤(pán)空間有限,本文就比較 KB、MB、GB 三個(gè)級(jí)別的文件讀取差異。

創(chuàng)新互聯(lián)2013年至今,是專(zhuān)業(yè)互聯(lián)網(wǎng)技術(shù)服務(wù)公司,擁有項(xiàng)目成都網(wǎng)站建設(shè)、成都網(wǎng)站制作網(wǎng)站策劃,項(xiàng)目實(shí)施與項(xiàng)目整合能力。我們以讓每一個(gè)夢(mèng)想脫穎而出為使命,1280元中牟做網(wǎng)站,已為上家服務(wù),為中牟各地企業(yè)和個(gè)人服務(wù),聯(lián)系電話(huà):18982081108

package main

import (
"bufio"
"math/rand"
"os"
"time"
)

const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

var seededRand = rand.New(rand.NewSource(time.Now().UnixNano()))

func StringWithCharset(length int) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[seededRand.Intn(len(charset))]
}
return string(b)
}

func main() {
files := map[string]int{"4KB.txt": 4, "4MB.txt": 4096, "4GB.txt": 4194304, "16GB.txt": 16777216}
for name, number := range files {
file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
panic(err)
}
write := bufio.NewWriter(file)
for i := 0; i < number; i++ {
s := StringWithCharset(1023) + "\n"
write.WriteString(s)
}
file.Close()
}
}

執(zhí)行以上代碼,我們依次得到 4KB、4MB、4GB、16GB 大小的文件,它們是由每行 1KB 大小隨機(jī)字符串的內(nèi)容組成。

$ ls -alh 4kb.txt 4MB.txt 4GB.txt 16GB.txt
-rw-r--r-- 1 slp staff 16G Mar 6 15:57 16GB.txt
-rw-r--r-- 1 slp staff 4.0G Mar 6 15:54 4GB.txt
-rw-r--r-- 1 slp staff 4.0M Mar 6 15:53 4MB.txt
-rw-r--r-- 1 slp staff 4.0K Mar 6 15:16 4kb.txt

接下來(lái),我們使用不同的方式來(lái)讀取這些文件內(nèi)容。

整個(gè)文件加載

Go 提供了可一次性讀取文件內(nèi)容的方法:os.ReadFile 與 ioutil.ReadFile。在 Go 1.16 開(kāi)始,ioutil.ReadFile 就等價(jià)于 os.ReadFile。

func BenchmarkOsReadFile4KB(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := os.ReadFile("./4KB.txt")
if err != nil {
b.Fatal(err)
}
}
}

func BenchmarkOsReadFile4MB(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := os.ReadFile("./4MB.txt")
if err != nil {
b.Fatal(err)
}
}
}

func BenchmarkOsReadFile4GB(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := os.ReadFile("./4GB.txt")
if err != nil {
b.Fatal(err)
}
}
}

func BenchmarkOsReadFile16GB(b *testing.B) {
for i := 0; i < b.N; i++ {
_, err := os.ReadFile("./16GB.txt")
if err != nil {
b.Fatal(err)
}
}
}

一次性加載文件的優(yōu)缺點(diǎn)非常明顯,它能減少 IO 次數(shù),但它會(huì)將文件內(nèi)容都加載至內(nèi)存中,對(duì)于大文件,存在內(nèi)存撐爆的風(fēng)險(xiǎn)。

逐行讀取

在很多情況下,例如日志分析,對(duì)文件的處理都是按行進(jìn)行的。Go 中 bufio.Reader 對(duì)象提供了一個(gè) ReadLine() 方法,但其實(shí)我們更多地是使用 ReadBytes('\n') 或者 ReadString('\n') 代替。

// ReadLine is a low-level line-reading primitive. Most callers should use
// ReadBytes('\n') or ReadString('\n') instead or use a Scanner.

我們以 ReadString('\n') 為例,對(duì) 4 個(gè)文件分別進(jìn)行逐行讀取。

func ReadLines(filename string) {
fi, err := os.Open(filename)
if err != nil{
panic(err)
}
defer fi.Close()
reader := bufio.NewReader(fi)
for {
_, err = reader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
}
}

func BenchmarkReadLines4KB(b *testing.B) {
for i := 0; i < b.N; i++ {
ReadLines("./4KB.txt")
}
}

func BenchmarkReadLines4MB(b *testing.B) {
for i := 0; i < b.N; i++ {
ReadLines("./4MB.txt")
}
}

func BenchmarkReadLines4GB(b *testing.B) {
for i := 0; i < b.N; i++ {
ReadLines("./4GB.txt")
}
}

func BenchmarkReadLines16GB(b *testing.B) {
for i := 0; i < b.N; i++ {
ReadLines("./16GB.txt")
}
}

塊讀取

塊讀取也稱(chēng)為分片讀取,這也很好理解,我們可以將內(nèi)容分成一塊塊的,每次讀取指定大小的塊內(nèi)容。這里,我們將塊大小設(shè)置為 4KB。

func ReadChunk(filename string) {
f, err := os.Open(filename)
if err != nil {
panic(err)
}
defer f.Close()
buf := make([]byte, 4*1024)
r := bufio.NewReader(f)
for {
_, err = r.Read(buf)
if err != nil {
if err == io.EOF {
break
}
panic(err)
}
}
}

func BenchmarkReadChunk4KB(b *testing.B) {
for i := 0; i < b.N; i++ {
ReadChunk("./4KB.txt")
}
}

func BenchmarkReadChunk4MB(b *testing.B) {
for i := 0; i < b.N; i++ {
ReadChunk("./4MB.txt")
}
}

func BenchmarkReadChunk4GB(b *testing.B) {
for i := 0; i < b.N; i++ {
ReadChunk("./4GB.txt")
}
}

func BenchmarkReadChunk16GB(b *testing.B) {
for i := 0; i < b.N; i++ {
ReadChunk("./16GB.txt")
}
}

匯總結(jié)果:

BenchmarkOsReadFile4KB-8           92877             12491 ns/op
BenchmarkOsReadFile4MB-8 1620 744460 ns/op
BenchmarkOsReadFile4GB-8 1 7518057733 ns/op
signal: killed

BenchmarkReadLines4KB-8 90846 13184 ns/op
BenchmarkReadLines4MB-8 493 2338170 ns/op
BenchmarkReadLines4GB-8 1 3072629047 ns/op
BenchmarkReadLines16GB-8 1 12472749187 ns/op

BenchmarkReadChunk4KB-8 99848 12262 ns/op
BenchmarkReadChunk4MB-8 913 1233216 ns/op
BenchmarkReadChunk4GB-8 1 2095515009 ns/op
BenchmarkReadChunk16GB-8 1 8547054349 ns/op

在本文的測(cè)試條件下(每行數(shù)據(jù) 1KB),對(duì)于小對(duì)象 4KB 的讀取,三種方式差距并不大;在 MB 級(jí)別的讀取中,直接加載最快,但塊讀取也慢不了多少;上了 GB 后,塊讀取方式會(huì)最快。

且有一點(diǎn)可以注意到的是,在整個(gè)文件加載的方式中,對(duì)于 16 GB 的文件數(shù)據(jù)(測(cè)試機(jī)器運(yùn)行內(nèi)存為 8GB),會(huì)內(nèi)存耗盡出錯(cuò),沒(méi)法執(zhí)行。

總結(jié)

不管是什么大小的文件,均不推薦整個(gè)文件加載的方式,因?yàn)樗谛∥募r(shí)的速度優(yōu)勢(shì)并沒(méi)有那么大,相較于安全隱患,不值得選擇它。

塊讀取是優(yōu)先選擇,尤其對(duì)于一些沒(méi)有換行符的文件,例如音視頻等。通過(guò)設(shè)定合適的塊讀取大小,能讓速度和內(nèi)存得到很好的平衡。且在讀取過(guò)程中,往往伴隨著處理內(nèi)容的邏輯。每塊內(nèi)容可以賦給一個(gè)工作 goroutine 來(lái)處理,能更好地并發(fā)。


網(wǎng)站標(biāo)題:怎么選擇Go文件讀取方案
分享鏈接:http://www.5511xx.com/article/djchsdo.html