Add memory optimization improvements for reduced GC pressure (v0.36.16)
Some checks failed
Go / build-and-release (push) Has been cancelled
Some checks failed
Go / build-and-release (push) Has been cancelled
- Add buffer pool (pkg/database/bufpool) with SmallPool (64B) and MediumPool (1KB) for reusing bytes.Buffer instances on hot paths - Fix escape analysis in index types (uint40, letter, word) by using fixed-size arrays instead of make() calls that escape to heap - Add handler concurrency limiter (ORLY_MAX_HANDLERS_PER_CONN, default 100) to prevent unbounded goroutine growth under WebSocket load - Add pre-allocation hints to Uint40s.Union/Intersection/Difference methods - Update compact_event.go, save-event.go, serial_cache.go, and get-indexes-for-event.go to use pooled buffers Files modified: - app/config/config.go: Add MaxHandlersPerConnection config - app/handle-websocket.go: Initialize handler semaphore - app/listener.go: Add semaphore acquire/release in messageProcessor - pkg/database/bufpool/pool.go: New buffer pool package - pkg/database/compact_event.go: Use buffer pool, fix escape analysis - pkg/database/get-indexes-for-event.go: Reuse single buffer for all indexes - pkg/database/indexes/types/letter.go: Fixed array in UnmarshalRead - pkg/database/indexes/types/uint40.go: Fixed arrays, pre-allocation hints - pkg/database/indexes/types/word.go: Fixed array in UnmarshalRead - pkg/database/save-event.go: Use buffer pool for key encoding - pkg/database/serial_cache.go: Use buffer pool for lookups 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -35,8 +35,8 @@ func (p *Letter) MarshalWrite(w io.Writer) (err error) {
|
||||
}
|
||||
|
||||
func (p *Letter) UnmarshalRead(r io.Reader) (err error) {
|
||||
val := make([]byte, 1)
|
||||
if _, err = r.Read(val); chk.E(err) {
|
||||
var val [1]byte // Fixed array avoids heap escape
|
||||
if _, err = r.Read(val[:]); chk.E(err) {
|
||||
return
|
||||
}
|
||||
p.val = val[0]
|
||||
|
||||
@@ -46,23 +46,23 @@ func (c *Uint40) MarshalWrite(w io.Writer) (err error) {
|
||||
if c.value > MaxUint40 {
|
||||
return errors.New("value exceeds 40-bit range")
|
||||
}
|
||||
// Buffer for the 5 bytes
|
||||
buf := make([]byte, 5)
|
||||
// Fixed array avoids heap escape
|
||||
var buf [5]byte
|
||||
// Write the upper 5 bytes (ignoring the most significant 3 bytes of uint64)
|
||||
buf[0] = byte((c.value >> 32) & 0xFF) // Most significant byte
|
||||
buf[1] = byte((c.value >> 24) & 0xFF)
|
||||
buf[2] = byte((c.value >> 16) & 0xFF)
|
||||
buf[3] = byte((c.value >> 8) & 0xFF)
|
||||
buf[4] = byte(c.value & 0xFF) // Least significant byte
|
||||
_, err = w.Write(buf)
|
||||
_, err = w.Write(buf[:])
|
||||
return err
|
||||
}
|
||||
|
||||
// UnmarshalRead reads 5 bytes from the provided reader and decodes it into a 40-bit unsigned integer.
|
||||
func (c *Uint40) UnmarshalRead(r io.Reader) (err error) {
|
||||
// Buffer for the 5 bytes
|
||||
buf := make([]byte, 5)
|
||||
_, err = r.Read(buf)
|
||||
// Fixed array avoids heap escape
|
||||
var buf [5]byte
|
||||
_, err = r.Read(buf[:])
|
||||
if chk.E(err) {
|
||||
return err
|
||||
}
|
||||
@@ -81,8 +81,9 @@ type Uint40s []*Uint40
|
||||
// Union computes the union of the current Uint40s slice with another Uint40s slice. The result
|
||||
// contains all unique elements from both slices.
|
||||
func (s Uint40s) Union(other Uint40s) Uint40s {
|
||||
valueMap := make(map[uint64]bool)
|
||||
var result Uint40s
|
||||
totalCap := len(s) + len(other)
|
||||
valueMap := make(map[uint64]bool, totalCap)
|
||||
result := make(Uint40s, 0, totalCap) // Pre-allocate for worst case
|
||||
|
||||
// Add elements from the current Uint40s slice to the result
|
||||
for _, item := range s {
|
||||
@@ -108,8 +109,13 @@ func (s Uint40s) Union(other Uint40s) Uint40s {
|
||||
// Intersection computes the intersection of the current Uint40s slice with another Uint40s
|
||||
// slice. The result contains only the elements that exist in both slices.
|
||||
func (s Uint40s) Intersection(other Uint40s) Uint40s {
|
||||
valueMap := make(map[uint64]bool)
|
||||
var result Uint40s
|
||||
// Result can be at most the size of the smaller slice
|
||||
smallerLen := len(s)
|
||||
if len(other) < smallerLen {
|
||||
smallerLen = len(other)
|
||||
}
|
||||
valueMap := make(map[uint64]bool, len(other))
|
||||
result := make(Uint40s, 0, smallerLen) // Pre-allocate for worst case
|
||||
|
||||
// Add all elements from the other Uint40s slice to the map
|
||||
for _, item := range other {
|
||||
@@ -131,8 +137,8 @@ func (s Uint40s) Intersection(other Uint40s) Uint40s {
|
||||
// The result contains only the elements that are in the current slice but not in the other
|
||||
// slice.
|
||||
func (s Uint40s) Difference(other Uint40s) Uint40s {
|
||||
valueMap := make(map[uint64]bool)
|
||||
var result Uint40s
|
||||
valueMap := make(map[uint64]bool, len(other))
|
||||
result := make(Uint40s, 0, len(s)) // Pre-allocate for worst case (no overlap)
|
||||
|
||||
// Mark all elements in the other Uint40s slice
|
||||
for _, item := range other {
|
||||
|
||||
@@ -37,12 +37,12 @@ func (w *Word) MarshalWrite(wr io.Writer) (err error) {
|
||||
// UnmarshalRead reads the word from the reader, stopping at the zero-byte marker
|
||||
func (w *Word) UnmarshalRead(r io.Reader) error {
|
||||
buf := new(bytes.Buffer)
|
||||
tmp := make([]byte, 1)
|
||||
var tmp [1]byte // Fixed array avoids heap escape
|
||||
foundEndMarker := false
|
||||
|
||||
// Read bytes until the zero byte is encountered
|
||||
for {
|
||||
n, err := r.Read(tmp)
|
||||
n, err := r.Read(tmp[:])
|
||||
if n > 0 {
|
||||
if tmp[0] == 0x00 { // Stop on encountering the zero-byte marker
|
||||
foundEndMarker = true
|
||||
|
||||
Reference in New Issue
Block a user