go readfile詳細buffer

6564 ワード

ファイルが存在しないのか、それとも他の原因なのかを判断する必要があります.
注意:ファイルが存在しない場合に返されるerrはnilです.
ここで返されるdataは[]byteです.
func readAll(r io.Reader, capacity int64) (b []byte, err error) {
	var buf bytes.Buffer
	// If the buffer overflows, we will get bytes.ErrTooLarge.
	// Return that as an error. Any other panic remains.
	defer func() {
		e := recover()
		if e == nil {
			return
		}
		if panicErr, ok := e.(error); ok && panicErr == bytes.ErrTooLarge {
			err = panicErr
		} else {
			panic(e)
		}
	}()
	if int64(int(capacity)) == capacity {
		buf.Grow(int(capacity))
	}
	_, err = buf.ReadFrom(r)
	return buf.Bytes(), err
}

// MinRead is the minimum slice size passed to a Read call by
// Buffer.ReadFrom. As long as the Buffer has at least MinRead bytes beyond
// what is required to hold the contents of r, ReadFrom will not grow the
// underlying buffer.
const MinRead = 512

// ReadFrom reads data from r until EOF and appends it to the buffer, growing
// the buffer as needed. The return value n is the number of bytes read. Any
// error except io.EOF encountered during the read is also returned. If the
// buffer becomes too large, ReadFrom will panic with ErrTooLarge.
func (b *Buffer) ReadFrom(r io.Reader) (n int64, err error) {
	b.lastRead = opInvalid
	for {
		i := b.grow(MinRead)
		b.buf = b.buf[:i]
		m, e := r.Read(b.buf[i:cap(b.buf)])
		if m < 0 {
			panic(errNegativeRead)
		}

		b.buf = b.buf[:i+m]
		n += int64(m)
		if e == io.EOF {
			return n, nil // e is EOF, so return nil explicitly
		}
		if e != nil {
			return n, e
		}
	}
}

まずbufは成長を試み,まず[i,cap(buf)]は最後まで完全なスライスを一度に読み取ることを保証できる.
、そしてmは、今回読み出したバイト数であるべきである.iはshouldreadfromなので、iはlen(buffer)のはずです.
そしてこのreadはfileの実装であり、実はシステム呼び出しである.
/ ReadFile reads the file named by filename and returns the contents.
// A successful call returns err == nil, not err == EOF. Because ReadFile
// reads the whole file, it does not treat an EOF from Read as an error
// to be reported.
func ReadFile(filename string) ([]byte, error) {
	f, err := os.Open(filename)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	// It's a good but not certain bet that FileInfo will tell us exactly how much to
	// read, so let's try it but be prepared for the answer to be wrong.
	var n int64 = bytes.MinRead

	if fi, err := f.Stat(); err == nil {
		// As initial capacity for readAll, use Size + a little extra in case Size
		// is zero, and to avoid another allocation after Read has filled the
		// buffer. The readAll call will read into its allocated internal buffer
		// cheaply. If the size was wrong, we'll either waste some space off the end
		// or reallocate as needed, but in the overwhelmingly common case we'll get
		// it just right.
		if size := fi.Size() + bytes.MinRead; size > n {
			n = size
		}
	}
	return readAll(f, n)
}

その中で大きなファイルに対してfileのsizeをbufferのcapと見なし、絶えずbufferのgrowによる干渉を省くことができます.
// Bytes returns a slice of length b.Len() holding the unread portion of the buffer.
// The slice is valid for use only until the next buffer modification (that is,
// only until the next call to a method like Read, Write, Reset, or Truncate).
// The slice aliases the buffer content at least until the next buffer modification,
// so immediate changes to the slice will affect the result of future reads.
func (b *Buffer) Bytes() []byte { return b.buf[b.off:] }

readALLはbufferを返します.bytes、これはもう一つのブレークポイントで読み続ける機能があります.
残念ながら、ここでファイルを読み取るのは役に立たない.毎回新しいbufferを開いてファイルを読み取るのを見ることができるからだ.
// Read reads up to len(b) bytes from the File.
// It returns the number of bytes read and any error encountered.
// At end of file, Read returns 0, io.EOF.
func (f *File) Read(b []byte) (n int, err error) {
	if err := f.checkValid("read"); err != nil {
		return 0, err
	}
	n, e := f.read(b)
	return n, f.wrapErr("read", e)
}

fileのread実装は、filapathが空であるかどうかを確認することです.
なぜgrowは毎回512バイト単位でなければならないのか
ディスクの物理構造から見るアクセス情報の最小単位はセクタであり、1つのセクタは512バイトである.
// grow grows the buffer to guarantee space for n more bytes.
// It returns the index where bytes should be written.
// If the buffer can't grow it will panic with ErrTooLarge.
func (b *Buffer) grow(n int) int {
	m := b.Len()
	// If buffer is empty, reset to recover space.
	if m == 0 && b.off != 0 {
		b.Reset()
	}
	// Try to grow by means of a reslice.
	if i, ok := b.tryGrowByReslice(n); ok {
		return i
	}
	if b.buf == nil && n <= smallBufferSize {
		b.buf = make([]byte, n, smallBufferSize)
		return 0
	}
	c := cap(b.buf)
	if n <= c/2-m {
		// We can slide things down instead of allocating a new
		// slice. We only need m+n <= c to slide, but
		// we instead let capacity get twice as large so we
		// don't spend all our time copying.
		copy(b.buf, b.buf[b.off:])
	} else if c > maxInt-c-n {
		panic(ErrTooLarge)
	} else {
		// Not enough space anywhere, we need to allocate.
		buf := makeSlice(2*c + n)
		copy(buf, b.buf[b.off:])
		b.buf = buf
	}
	// Restore b.off and len(b.buf).
	b.off = 0
	b.buf = b.buf[:m+n]
	return m
}

まずtryGrowbysliceを試して、入ってきたのは512です
// tryGrowByReslice is a inlineable version of grow for the fast-case where the
// internal buffer only needs to be resliced.
// It returns the index where bytes should be written and whether it succeeded.
func (b *Buffer) tryGrowByReslice(n int) (int, bool) {
	if l := len(b.buf); n <= cap(b.buf)-l {
		b.buf = b.buf[:l+n]
		return l, true
	}
	return 0, false
}

case 1:このbufferの残りの空間にnの大きさがあるとすれば、もちろん割り当てる必要はありません.
しかし、ここではoffのことは考えていないようなので、readのたびに少ないと、このoffデータは保存できます.さもなくばoffは意味がなくて、growのたびにすべて削除しました
Case 2:bufferが空で64バイト未満に割り当てられているとすると、64バイトサイズの空間割り当てが直接申請される.
ページのサイズに関係するはずです
Case 3:c/2の場合=l-m
l-mはすでに読んだ部分です.つまり、すでに読んだ部分が大きさを満たしていれば、そのまま上書きして、前に読んだものをクリアします.
実はn(=c-mでいいのに、ここはどうして2/c未満なの?
それはやはりすべての時間をcopyに費やしたくないからです.要求はやはり一定の空間が必要で、彼が要求した空間よりもっと大きいということです.
Case 4:cが大きすぎるのを防ぐため
case 5:十分なスペースがないので、2倍のcapと元の要求のnを割り当ててから、copyで読み取らなかったものを削除します.
このunreadpartionはb.len()です
copy(b.buf, b.buf[b.off:])

実はこの倍増形式の取得bufferは基本的に各分野で通用していることが分かった.
彼のところはcopyの条件を厳しくしても理解できます.copyの場合、ちょうど彼が申請したいのと同じように、メモリが足りないためにコピーされる場合が大きく増加します.
だから
don’t spend all our time copying
ファイルにとって、offが更新されないといえば、
1つのクラスタから3つのクラスタになり、7つのクラスタになります.絶え間ないcopy、、、、、とても時間がかかります.
これで、これまでのreadfileがgrowのメモリ割り当てを避けたことがどれほどよかったかがわかります.