mirror of
				https://source.quilibrium.com/quilibrium/ceremonyclient.git
				synced 2025-11-04 15:27:27 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			202 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			202 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
// Copyright 2023 The LevelDB-Go and Pebble Authors. All rights reserved. Use
 | 
						|
// of this source code is governed by a BSD-style license that can be found in
 | 
						|
// the LICENSE file.
 | 
						|
 | 
						|
package objstorageprovider
 | 
						|
 | 
						|
const (
 | 
						|
	// Constants for dynamic readahead of data blocks. Note that the size values
 | 
						|
	// make sense as some multiple of the default block size; and they should
 | 
						|
	// both be larger than the default block size.
 | 
						|
	minFileReadsForReadahead = 2
 | 
						|
	// TODO(bilal): Have the initial size value be a factor of the block size,
 | 
						|
	// as opposed to a hardcoded value.
 | 
						|
	initialReadaheadSize = 64 << 10 /* 64KB */
 | 
						|
)
 | 
						|
 | 
						|
// readaheadState contains state variables related to readahead. Updated on
 | 
						|
// file reads.
 | 
						|
type readaheadState struct {
 | 
						|
	// Number of sequential reads.
 | 
						|
	numReads         int64
 | 
						|
	maxReadaheadSize int64
 | 
						|
	// Size issued to the next call to Prefetch. Starts at or above
 | 
						|
	// initialReadaheadSize and grows exponentially until maxReadaheadSize.
 | 
						|
	size int64
 | 
						|
	// prevSize is the size used in the last Prefetch call.
 | 
						|
	prevSize int64
 | 
						|
	// The byte offset up to which the OS has been asked to read ahead / cached.
 | 
						|
	// When reading ahead, reads up to this limit should not incur an IO
 | 
						|
	// operation. Reads after this limit can benefit from a new call to
 | 
						|
	// Prefetch.
 | 
						|
	limit int64
 | 
						|
}
 | 
						|
 | 
						|
func makeReadaheadState(maxReadaheadSize int64) readaheadState {
 | 
						|
	return readaheadState{
 | 
						|
		size:             initialReadaheadSize,
 | 
						|
		maxReadaheadSize: maxReadaheadSize,
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (rs *readaheadState) recordCacheHit(offset, blockLength int64) {
 | 
						|
	currentReadEnd := offset + blockLength
 | 
						|
	if rs.numReads >= minFileReadsForReadahead {
 | 
						|
		if currentReadEnd >= rs.limit && offset <= rs.limit+rs.maxReadaheadSize {
 | 
						|
			// This is a read that would have resulted in a readahead, had it
 | 
						|
			// not been a cache hit.
 | 
						|
			rs.limit = currentReadEnd
 | 
						|
			return
 | 
						|
		}
 | 
						|
		if currentReadEnd < rs.limit-rs.prevSize || offset > rs.limit+rs.maxReadaheadSize {
 | 
						|
			// We read too far away from rs.limit to benefit from readahead in
 | 
						|
			// any scenario. Reset all variables.
 | 
						|
			rs.numReads = 1
 | 
						|
			rs.limit = currentReadEnd
 | 
						|
			rs.size = initialReadaheadSize
 | 
						|
			rs.prevSize = 0
 | 
						|
			return
 | 
						|
		}
 | 
						|
		// Reads in the range [rs.limit - rs.prevSize, rs.limit] end up
 | 
						|
		// here. This is a read that is potentially benefitting from a past
 | 
						|
		// readahead.
 | 
						|
		return
 | 
						|
	}
 | 
						|
	if currentReadEnd >= rs.limit && offset <= rs.limit+rs.maxReadaheadSize {
 | 
						|
		// Blocks are being read sequentially and would benefit from readahead
 | 
						|
		// down the line.
 | 
						|
		rs.numReads++
 | 
						|
		return
 | 
						|
	}
 | 
						|
	// We read too far ahead of the last read, or before it. This indicates
 | 
						|
	// a random read, where readahead is not desirable. Reset all variables.
 | 
						|
	rs.numReads = 1
 | 
						|
	rs.limit = currentReadEnd
 | 
						|
	rs.size = initialReadaheadSize
 | 
						|
	rs.prevSize = 0
 | 
						|
}
 | 
						|
 | 
						|
// maybeReadahead updates state and determines whether to issue a readahead /
 | 
						|
// prefetch call for a block read at offset for blockLength bytes.
 | 
						|
// Returns a size value (greater than 0) that should be prefetched if readahead
 | 
						|
// would be beneficial.
 | 
						|
func (rs *readaheadState) maybeReadahead(offset, blockLength int64) int64 {
 | 
						|
	currentReadEnd := offset + blockLength
 | 
						|
	if rs.numReads >= minFileReadsForReadahead {
 | 
						|
		// The minimum threshold of sequential reads to justify reading ahead
 | 
						|
		// has been reached.
 | 
						|
		// There are two intervals: the interval being read:
 | 
						|
		// [offset, currentReadEnd]
 | 
						|
		// as well as the interval where a read would benefit from read ahead:
 | 
						|
		// [rs.limit, rs.limit + rs.size]
 | 
						|
		// We increase the latter interval to
 | 
						|
		// [rs.limit, rs.limit + rs.maxReadaheadSize] to account for cases where
 | 
						|
		// readahead may not be beneficial with a small readahead size, but over
 | 
						|
		// time the readahead size would increase exponentially to make it
 | 
						|
		// beneficial.
 | 
						|
		if currentReadEnd >= rs.limit && offset <= rs.limit+rs.maxReadaheadSize {
 | 
						|
			// We are doing a read in the interval ahead of
 | 
						|
			// the last readahead range. In the diagrams below, ++++ is the last
 | 
						|
			// readahead range, ==== is the range represented by
 | 
						|
			// [rs.limit, rs.limit + rs.maxReadaheadSize], and ---- is the range
 | 
						|
			// being read.
 | 
						|
			//
 | 
						|
			//               rs.limit           rs.limit + rs.maxReadaheadSize
 | 
						|
			//         ++++++++++|===========================|
 | 
						|
			//
 | 
						|
			//              |-------------|
 | 
						|
			//            offset       currentReadEnd
 | 
						|
			//
 | 
						|
			// This case is also possible, as are all cases with an overlap
 | 
						|
			// between [rs.limit, rs.limit + rs.maxReadaheadSize] and [offset,
 | 
						|
			// currentReadEnd]:
 | 
						|
			//
 | 
						|
			//               rs.limit           rs.limit + rs.maxReadaheadSize
 | 
						|
			//         ++++++++++|===========================|
 | 
						|
			//
 | 
						|
			//                                            |-------------|
 | 
						|
			//                                         offset       currentReadEnd
 | 
						|
			//
 | 
						|
			//
 | 
						|
			rs.numReads++
 | 
						|
			rs.limit = offset + rs.size
 | 
						|
			rs.prevSize = rs.size
 | 
						|
			// Increase rs.size for the next read.
 | 
						|
			rs.size *= 2
 | 
						|
			if rs.size > rs.maxReadaheadSize {
 | 
						|
				rs.size = rs.maxReadaheadSize
 | 
						|
			}
 | 
						|
			return rs.prevSize
 | 
						|
		}
 | 
						|
		if currentReadEnd < rs.limit-rs.prevSize || offset > rs.limit+rs.maxReadaheadSize {
 | 
						|
			// The above conditional has rs.limit > rs.prevSize to confirm that
 | 
						|
			// rs.limit - rs.prevSize would not underflow.
 | 
						|
			// We read too far away from rs.limit to benefit from readahead in
 | 
						|
			// any scenario. Reset all variables.
 | 
						|
			// The case where we read too far ahead:
 | 
						|
			//
 | 
						|
			// (rs.limit - rs.prevSize)    (rs.limit)   (rs.limit + rs.maxReadaheadSize)
 | 
						|
			//                    |+++++++++++++|=============|
 | 
						|
			//
 | 
						|
			//                                                  |-------------|
 | 
						|
			//                                             offset       currentReadEnd
 | 
						|
			//
 | 
						|
			// Or too far behind:
 | 
						|
			//
 | 
						|
			// (rs.limit - rs.prevSize)    (rs.limit)   (rs.limit + rs.maxReadaheadSize)
 | 
						|
			//                    |+++++++++++++|=============|
 | 
						|
			//
 | 
						|
			//    |-------------|
 | 
						|
			// offset       currentReadEnd
 | 
						|
			//
 | 
						|
			rs.numReads = 1
 | 
						|
			rs.limit = currentReadEnd
 | 
						|
			rs.size = initialReadaheadSize
 | 
						|
			rs.prevSize = 0
 | 
						|
 | 
						|
			return 0
 | 
						|
		}
 | 
						|
		// Reads in the range [rs.limit - rs.prevSize, rs.limit] end up
 | 
						|
		// here. This is a read that is potentially benefitting from a past
 | 
						|
		// readahead, but there's no reason to issue a readahead call at the
 | 
						|
		// moment.
 | 
						|
		//
 | 
						|
		// (rs.limit - rs.prevSize)            (rs.limit + rs.maxReadaheadSize)
 | 
						|
		//                    |+++++++++++++|===============|
 | 
						|
		//                             (rs.limit)
 | 
						|
		//
 | 
						|
		//                        |-------|
 | 
						|
		//                     offset    currentReadEnd
 | 
						|
		//
 | 
						|
		rs.numReads++
 | 
						|
		return 0
 | 
						|
	}
 | 
						|
	if currentReadEnd >= rs.limit && offset <= rs.limit+rs.maxReadaheadSize {
 | 
						|
		// Blocks are being read sequentially and would benefit from readahead
 | 
						|
		// down the line.
 | 
						|
		//
 | 
						|
		//                       (rs.limit)   (rs.limit + rs.maxReadaheadSize)
 | 
						|
		//                         |=============|
 | 
						|
		//
 | 
						|
		//                    |-------|
 | 
						|
		//                offset    currentReadEnd
 | 
						|
		//
 | 
						|
		rs.numReads++
 | 
						|
		return 0
 | 
						|
	}
 | 
						|
	// We read too far ahead of the last read, or before it. This indicates
 | 
						|
	// a random read, where readahead is not desirable. Reset all variables.
 | 
						|
	//
 | 
						|
	// (rs.limit - rs.maxReadaheadSize)  (rs.limit)   (rs.limit + rs.maxReadaheadSize)
 | 
						|
	//                     |+++++++++++++|=============|
 | 
						|
	//
 | 
						|
	//                                                    |-------|
 | 
						|
	//                                                offset    currentReadEnd
 | 
						|
	//
 | 
						|
	rs.numReads = 1
 | 
						|
	rs.limit = currentReadEnd
 | 
						|
	rs.size = initialReadaheadSize
 | 
						|
	rs.prevSize = 0
 | 
						|
	return 0
 | 
						|
}
 |