or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

buffer-management.mdgraph-processing.mdindex.mdinput-output-streams.mdserialization-utilities.md
tile.json

buffer-management.mddocs/

Buffer Management

Efficient buffer management is crucial for high-performance serialization. Protostuff-core provides sophisticated buffer management through the LinkedBuffer system, which minimizes memory allocations and enables buffer reuse.

LinkedBuffer

The core buffer management class that provides efficient memory allocation and reuse for serialization operations.

public final class LinkedBuffer {
    public static LinkedBuffer allocate(int minimumCapacity);
    public static int findBufferSize(int minCapacity);
    public WriteSession clear();
    public int size();
    public byte[] toByteArray();
    public void writeTo(OutputStream out) throws IOException;
    public void writeTo(WriteSession session, OutputStream out) throws IOException;
}

Buffer Allocation

public static LinkedBuffer allocate(int minimumCapacity)

Creates a new LinkedBuffer with the specified minimum capacity. The actual allocated size may be larger to optimize memory usage patterns.

Parameters:

  • minimumCapacity - Minimum buffer size in bytes

Usage Example:

// Allocate buffer with default size
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);

// Allocate larger buffer for big messages
LinkedBuffer largeBuffer = LinkedBuffer.allocate(64 * 1024); // 64KB

Buffer Size Optimization

public static int findBufferSize(int minCapacity)

Calculates the optimal buffer size for a given minimum capacity, taking into account memory alignment and allocation patterns.

Usage:

int optimalSize = LinkedBuffer.findBufferSize(1000); // Returns optimized size >= 1000
LinkedBuffer buffer = LinkedBuffer.allocate(optimalSize);

WriteSession

Manages the writing state and enables buffer reuse across multiple serialization operations.

public final class WriteSession {
    public WriteSession clear();
    public int size();
    public byte[] toByteArray();
    public void writeTo(OutputStream out) throws IOException;
}

Buffer Reuse Pattern

// Create reusable buffer
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);

// Serialize multiple messages efficiently
for (Message message : messages) {
    WriteSession session = buffer.clear(); // Reset for reuse
    ProtostuffIOUtil.writeTo(outputStream, message, schema, buffer);
    // Buffer is automatically reset for next iteration
}

Memory Management Strategies

Default Buffer Sizes

public static final int DEFAULT_BUFFER_SIZE = 512;

The default buffer size provides a good balance between memory usage and performance for typical messages.

Size Calculation

// Buffer sizing strategies
int smallBufferSize = 256;      // For small messages < 1KB
int defaultBufferSize = 512;    // For typical messages 1-10KB  
int largeBufferSize = 4096;     // For large messages > 10KB
int xlargeBufferSize = 64 * 1024; // For very large messages or batches

Performance Characteristics

Small Buffers (256-512 bytes):

  • Lower memory overhead
  • More frequent buffer expansions
  • Good for high-frequency, small messages

Large Buffers (4KB-64KB):

  • Higher memory overhead
  • Fewer buffer expansions
  • Better for large messages or batch operations

Advanced Buffer Operations

Converting to Byte Arrays

public byte[] toByteArray()

Converts the buffer contents to a byte array. This operation copies the data, so use stream-based operations when possible for better performance.

LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
ProtostuffOutput output = new ProtostuffOutput(buffer);

// Write data to buffer
output.writeString(1, "Hello", false);
output.writeInt32(2, 42, false);

// Convert to byte array
byte[] data = buffer.toByteArray();

Streaming to OutputStream

public void writeTo(OutputStream out) throws IOException
public void writeTo(WriteSession session, OutputStream out) throws IOException

Writes buffer contents directly to an OutputStream without intermediate byte array allocation.

// Efficient streaming without byte array allocation
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
ProtostuffOutput output = new ProtostuffOutput(buffer);

// Serialize data
output.writeString(1, "Hello", false);

// Stream directly to output
buffer.writeTo(fileOutputStream);

Buffer Lifecycle Management

Single-Use Pattern

// Simple single-use pattern
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
byte[] data = ProtostuffIOUtil.toByteArray(message, schema, buffer);
// Buffer can be garbage collected

Reuse Pattern

// Efficient reuse pattern for multiple serializations
LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);

for (Message message : messageQueue) {
    WriteSession session = buffer.clear(); // Prepare for reuse
    byte[] data = ProtostuffIOUtil.toByteArray(message, schema, buffer);
    sendData(data);
}

Thread Safety

LinkedBuffer instances are not thread-safe. Use separate buffer instances per thread or implement external synchronization.

// Thread-local buffer pattern
ThreadLocal<LinkedBuffer> bufferPool = ThreadLocal.withInitial(() -> 
    LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE)
);

// Usage in multi-threaded environment
public byte[] serialize(Message message) {
    LinkedBuffer buffer = bufferPool.get();
    WriteSession session = buffer.clear();
    return ProtostuffIOUtil.toByteArray(message, schema, buffer);
}

Integration with Output Classes

All output classes are designed to work with LinkedBuffer:

// ProtobufOutput with LinkedBuffer
LinkedBuffer buffer = LinkedBuffer.allocate(4096);
ProtobufOutput output = new ProtobufOutput(buffer);

// ProtostuffOutput with LinkedBuffer  
ProtostuffOutput protostuffOutput = new ProtostuffOutput(buffer);

// Low-copy variants
LowCopyProtobufOutput lowCopyOutput = new LowCopyProtobufOutput(buffer);

Buffer Chaining

LinkedBuffer supports chaining multiple buffer segments for very large messages:

public ProtobufOutput(LinkedBuffer head, LinkedBuffer tail)
// Chain buffers for large messages
LinkedBuffer head = LinkedBuffer.allocate(4096);
LinkedBuffer tail = LinkedBuffer.allocate(4096);
ProtobufOutput output = new ProtobufOutput(head, tail);

Performance Optimization

Buffer Sizing Guidelines

Message Size Based:

// Small messages (< 1KB)
LinkedBuffer smallBuffer = LinkedBuffer.allocate(512);

// Medium messages (1-10KB)  
LinkedBuffer mediumBuffer = LinkedBuffer.allocate(2048);

// Large messages (> 10KB)
LinkedBuffer largeBuffer = LinkedBuffer.allocate(8192);

Throughput Based:

// High throughput, small messages
LinkedBuffer buffer = LinkedBuffer.allocate(1024);

// Low throughput, large messages
LinkedBuffer buffer = LinkedBuffer.allocate(64 * 1024);

Memory Pool Pattern

// Simple buffer pool implementation
public class BufferPool {
    private final Queue<LinkedBuffer> buffers = new ConcurrentLinkedQueue<>();
    private final int bufferSize;
    
    public BufferPool(int bufferSize) {
        this.bufferSize = bufferSize;
    }
    
    public LinkedBuffer acquire() {
        LinkedBuffer buffer = buffers.poll();
        return buffer != null ? buffer : LinkedBuffer.allocate(bufferSize);
    }
    
    public void release(LinkedBuffer buffer) {
        buffer.clear();
        buffers.offer(buffer);
    }
}

Error Handling

Buffer operations can throw:

  • OutOfMemoryError - When requested buffer size exceeds available memory
  • IOException - During stream write operations
  • IllegalArgumentException - For invalid buffer sizes

Best Practices

  1. Size Appropriately: Choose buffer sizes based on typical message sizes
  2. Reuse Buffers: Use the clear() method to reuse buffers across operations
  3. Stream When Possible: Use writeTo() instead of toByteArray() for better performance
  4. Profile Memory Usage: Monitor buffer allocation patterns in production
  5. Consider Pooling: Implement buffer pools for high-throughput scenarios
  6. Thread Local: Use ThreadLocal buffers in multi-threaded applications

Compatibility

  • LinkedBuffer is compatible with all serialization formats (Protostuff and Protocol Buffer)
  • Buffer format is internal implementation detail and may change between versions
  • Public API remains stable across protostuff versions