VictoriaMetrics/lib/bytesutil/bytesutil.go
Aliaksandr Valialkin f4989edd96
lib/bytesutil: split Resize() into ResizeNoCopy() and ResizeWithCopy() functions
Previously bytesutil.Resize() was copying the original byte slice contents to a newly allocated slice.
This wasted CPU cycles and memory bandwidth in some places, where the original slice contents wasn't needed
after slize resizing. Switch such places to bytesutil.ResizeNoCopy().

Rename the original bytesutil.Resize() function to bytesutil.ResizeWithCopy() for the sake of improved readability.

Additionally, allocate new slice with `make()` instead of `append()`. This guarantees that the capacity of the allocated slice
exactly matches the requested size. The `append()` could return a slice with bigger capacity as an optimization for further `append()` calls.
This could result in excess memory usage when the returned byte slice was cached (for instance, in lib/blockcache).

Updates https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2007
2022-01-25 15:24:44 +02:00

50 lines
1.5 KiB
Go

package bytesutil
import (
"reflect"
"unsafe"
)
// ResizeWithCopy resizes b to n bytes and returns the resized buffer (which may be newly allocated).
//
// If newly allocated buffer is returned then b contents is copied to it.
func ResizeWithCopy(b []byte, n int) []byte {
if n <= cap(b) {
return b[:n]
}
// Allocate the exact number of bytes instead of using `b = append(b[:cap(b)], make([]byte, nn)...)`,
// since `append()` may allocate more than the requested bytes for additional capacity.
// Using make() instead of append() should save RAM when the resized slice is cached somewhere.
bNew := make([]byte, n)
copy(bNew, b)
return bNew
}
// ResizeNoCopy resizes b to n bytes and returns the resized buffer (which may be newly allocated).
//
// If newly allocated buffer is returned then b contents isn't copied to it.
func ResizeNoCopy(b []byte, n int) []byte {
if n <= cap(b) {
return b[:n]
}
return make([]byte, n)
}
// ToUnsafeString converts b to string without memory allocations.
//
// The returned string is valid only until b is reachable and unmodified.
func ToUnsafeString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
// ToUnsafeBytes converts s to a byte slice without memory allocations.
//
// The returned byte slice is valid only until s is reachable and unmodified.
func ToUnsafeBytes(s string) (b []byte) {
sh := (*reflect.StringHeader)(unsafe.Pointer(&s))
slh := (*reflect.SliceHeader)(unsafe.Pointer(&b))
slh.Data = sh.Data
slh.Len = sh.Len
slh.Cap = sh.Len
return b
}