mirror of
https://github.com/ClusterCockpit/cc-metric-store.git
synced 2025-07-06 13:33:49 +02:00
Pre-allocate batchwise via mmap
This commit is contained in:
parent
9830f3c5d4
commit
ac5f113ccb
@ -1,6 +1,8 @@
|
|||||||
package memstore
|
package memstore
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
@ -16,20 +18,26 @@ const bufferSizeInBytes int = bufferSizeInFloats * 8
|
|||||||
// The allocator rarely used, so a single big lock should be fine!
|
// The allocator rarely used, so a single big lock should be fine!
|
||||||
var allocatorLock sync.Mutex
|
var allocatorLock sync.Mutex
|
||||||
var allocatorPool [][]byte
|
var allocatorPool [][]byte
|
||||||
|
var allocatorBatch []byte
|
||||||
|
|
||||||
func RequestBytes(size int) []byte {
|
func RequestBytes(size int) []byte {
|
||||||
requested := size
|
requested := size
|
||||||
size = (size + bufferSizeInBytes - 1) / bufferSizeInBytes * bufferSizeInBytes
|
size = (size + bufferSizeInBytes - 1) / bufferSizeInBytes * bufferSizeInBytes
|
||||||
if size == bufferSizeInBytes {
|
|
||||||
allocatorLock.Lock()
|
// Check allocation caches:
|
||||||
if len(allocatorPool) > 0 {
|
allocatorLock.Lock()
|
||||||
bytes := allocatorPool[len(allocatorPool)-1]
|
if len(allocatorPool) > 0 && size == bufferSizeInBytes {
|
||||||
allocatorPool = allocatorPool[:len(allocatorPool)-1]
|
bytes := allocatorPool[len(allocatorPool)-1]
|
||||||
allocatorLock.Unlock()
|
allocatorPool = allocatorPool[:len(allocatorPool)-1]
|
||||||
return bytes
|
|
||||||
}
|
|
||||||
allocatorLock.Unlock()
|
allocatorLock.Unlock()
|
||||||
|
return bytes[:requested]
|
||||||
|
} else if cap(allocatorBatch) > size {
|
||||||
|
bytes := allocatorBatch[:0:size]
|
||||||
|
allocatorBatch = allocatorBatch[size:]
|
||||||
|
allocatorLock.Unlock()
|
||||||
|
return bytes[:requested]
|
||||||
}
|
}
|
||||||
|
allocatorLock.Unlock()
|
||||||
|
|
||||||
pagesize := os.Getpagesize()
|
pagesize := os.Getpagesize()
|
||||||
if size < pagesize || size%pagesize != 0 {
|
if size < pagesize || size%pagesize != 0 {
|
||||||
@ -48,6 +56,35 @@ func RequestBytes(size int) []byte {
|
|||||||
return bytes[:requested]
|
return bytes[:requested]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FillAllocatorCache(estimate int) error {
|
||||||
|
size := (estimate + bufferSizeInBytes - 1) / bufferSizeInBytes * bufferSizeInBytes
|
||||||
|
pagesize := os.Getpagesize()
|
||||||
|
if size < pagesize || size%pagesize != 0 {
|
||||||
|
return errors.New("estimate to small of buffer size not page size compatible")
|
||||||
|
}
|
||||||
|
|
||||||
|
allocatorLock.Lock()
|
||||||
|
defer allocatorLock.Unlock()
|
||||||
|
|
||||||
|
if len(allocatorBatch) > 0 {
|
||||||
|
n := len(allocatorBatch) / bufferSizeInBytes
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
chunk := allocatorBatch[i*bufferSizeInBytes : i*bufferSizeInBytes+bufferSizeInBytes]
|
||||||
|
allocatorPool = append(allocatorPool, chunk[0:0:bufferSizeInBytes])
|
||||||
|
}
|
||||||
|
allocatorBatch = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes, err := unix.Mmap(-1, 0, size, unix.PROT_READ|unix.PROT_WRITE, unix.MAP_ANONYMOUS|unix.MAP_SHARED)
|
||||||
|
if err != nil {
|
||||||
|
panic("unix.Mmap failed: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("batch-allocated %d bytes", cap(bytes))
|
||||||
|
allocatorBatch = bytes
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func ReleaseBytes(bytes []byte) {
|
func ReleaseBytes(bytes []byte) {
|
||||||
if cap(bytes)%bufferSizeInBytes != 0 {
|
if cap(bytes)%bufferSizeInBytes != 0 {
|
||||||
panic("bytes released that do not match the buffer size constraints")
|
panic("bytes released that do not match the buffer size constraints")
|
||||||
|
@ -42,6 +42,7 @@ func (c *Checkpoint) Serialize(w io.Writer) error {
|
|||||||
buf = encodeBytes(buf, c.Reserved)
|
buf = encodeBytes(buf, c.Reserved)
|
||||||
buf = encodeUint64(buf, c.From)
|
buf = encodeUint64(buf, c.From)
|
||||||
buf = encodeUint64(buf, c.To)
|
buf = encodeUint64(buf, c.To)
|
||||||
|
buf = encodeUint64(buf, 0) // Estimate zero for non-streaming checkpoints
|
||||||
|
|
||||||
// The rest:
|
// The rest:
|
||||||
var err error
|
var err error
|
||||||
@ -129,6 +130,9 @@ func (c *Checkpoint) Deserialize(r *bufio.Reader) error {
|
|||||||
if c.To, err = decodeUint64(buf, r); err != nil {
|
if c.To, err = decodeUint64(buf, r); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if _, err = decodeUint64(buf, r); err != nil { // Estimate ignored here
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
c.Root = &CheckpointLevel{}
|
c.Root = &CheckpointLevel{}
|
||||||
return c.Root.deserialize(buf, r)
|
return c.Root.deserialize(buf, r)
|
||||||
|
@ -110,6 +110,35 @@ func (l *Level) free(t int64) (delme bool, n int) {
|
|||||||
return len(l.metrics) == 0 && len(l.sublevels) == 0, n
|
return len(l.metrics) == 0 && len(l.sublevels) == 0, n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return an estimate of how many values are stored for all metrics and sublevels between from and to.
|
||||||
|
func (l *Level) countValues(from int64, to int64) int {
|
||||||
|
vals := 0
|
||||||
|
l.lock.RLock()
|
||||||
|
defer l.lock.RUnlock()
|
||||||
|
|
||||||
|
for _, c := range l.metrics {
|
||||||
|
for c != nil {
|
||||||
|
if to < c.start || c.end() < from {
|
||||||
|
continue
|
||||||
|
} else if c.start < from {
|
||||||
|
vals += int((from - c.start) / c.frequency)
|
||||||
|
} else if from < c.start {
|
||||||
|
vals += int((c.start - from) / c.frequency)
|
||||||
|
} else {
|
||||||
|
vals += len(c.data)
|
||||||
|
}
|
||||||
|
|
||||||
|
c = c.prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sublevel := range l.sublevels {
|
||||||
|
vals += sublevel.countValues(from, to)
|
||||||
|
}
|
||||||
|
|
||||||
|
return vals
|
||||||
|
}
|
||||||
|
|
||||||
type MemoryStore struct {
|
type MemoryStore struct {
|
||||||
root Level // root of the tree structure
|
root Level // root of the tree structure
|
||||||
// TODO...
|
// TODO...
|
||||||
|
@ -17,6 +17,7 @@ func (ms *MemoryStore) SaveCheckpoint(from, to int64, w io.Writer) error {
|
|||||||
buf = encodeBytes(buf, nil)
|
buf = encodeBytes(buf, nil)
|
||||||
buf = encodeUint64(buf, uint64(from))
|
buf = encodeUint64(buf, uint64(from))
|
||||||
buf = encodeUint64(buf, uint64(to))
|
buf = encodeUint64(buf, uint64(to))
|
||||||
|
buf = encodeUint64(buf, uint64(ms.root.countValues(from, to)))
|
||||||
|
|
||||||
metricsbuf := make([]types.Float, 0, (to-from)/ms.MinFrequency()+1)
|
metricsbuf := make([]types.Float, 0, (to-from)/ms.MinFrequency()+1)
|
||||||
var err error
|
var err error
|
||||||
@ -122,6 +123,13 @@ func (ms *MemoryStore) LoadCheckpoint(r io.Reader) error {
|
|||||||
if _, err := decodeUint64(buf, r); err != nil { // To
|
if _, err := decodeUint64(buf, r); err != nil { // To
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if valueEstimate, err := decodeUint64(buf, r); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
if err := FillAllocatorCache(int(valueEstimate) * 8); err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := ms.root.loadCheckpoint(ms, r, buf); err != nil {
|
if err := ms.root.loadCheckpoint(ms, r, buf); err != nil {
|
||||||
return err
|
return err
|
||||||
|
Loading…
x
Reference in New Issue
Block a user