76 Introduction
Version 3 builds on the Version 2 storage design and makes the LSS fully asynchronous. Separating the asynchronous design from Version 2 keeps the initial implementation simple enough to reason about and allows asynchronous I/O to be introduced as a later task. It is better to keep the initial problem simple enough to reason about and obtain good solutions than to introduce so much complexity that progress is slow and mistakes are made.
The aim is to implement the LSS as a pure event-driven state machine, without using coroutines. An event causes the LSS to update its state and may synchronously produce further work or notifications. Operations that require I/O return to the caller and continue later when the RAS reports completion.
For example, the RAS may call back into the LSS with
OnReadComplete(). The LSS can use that event to make a newly loaded
segment available in the segment cache. Making the segment available may in turn allow pending reads
of serial elements to continue: the LSS copies data into their destination buffers with
memcpy() and, when those reads are complete, notifies applications
built on top of the LSS. In this way, a native I/O completion can drive a sequence of state
transitions and application notifications without suspending a coroutine or blocking a thread.
Performance principles
The current LSS is already fast, and profiling has shown that a significant proportion of its time
can be spent in memcpy(). Version 3 changes how the LSS waits for
work, but must not make the existing data path less efficient. In particular, asynchrony must not
introduce additional payload copies merely to simplify ownership or completion handling.
RAS reads target their final segment-cache buffers directly. The RAS fills the buffer supplied in a
RASReadRequest, and completion transfers that buffer back to the
LSS without an intermediate RAS or event buffer. Similarly, an LFU is constructed in the buffer from
which the RAS writes it. The WSN completion contract keeps that original buffer alive until the native
write no longer needs it, so asynchronous writing does not require a staging copy.
Events carry lightweight operation identities or references to existing state, not copies of payload
data. A read-completion event can refer to its RASReadRequest, a
segment-cache event can refer to its segment, and write progress is represented by two counters.
Internal event dispatch must not move segment or serial-element contents between buffers.
When an application asks the LSS to fill an application-owned buffer, copying data from cached
segments into that buffer remains necessary. The implementation should preserve large contiguous
memcpy() operations where the packet layout permits them. Existing
interfaces that safely provide a direct read-only view of resident data should remain zero-copy and
must not be routed through a copied result merely for uniformity with asynchronous operations.
Native I/O may usefully overlap CPU work. For example, the device can load one segment while the LSS copies data from another resident segment. This does not imply that several CPU threads should perform payload copies concurrently. Parallel copies can compete for memory bandwidth and disrupt cache locality, so that form of concurrency should be introduced only when profiling demonstrates a benefit.
For equivalent operations, Version 3 should perform no more payload copies than Version 2 solely because it is asynchronous. Performance tests should compare the two versions and record at least the number of payload copies, bytes copied per application byte, average copy size, heap allocations, event dispatches, cross-thread hand-offs and overall throughput. RAS staging copies and payload copies inside events should both be zero.
The specification does not mandate a particular event queue, object pool, I/O queue depth, batching threshold or thread on which copying occurs. Those are implementation choices to be guided by measurement. The architectural requirement is that asynchronous coordination remains lightweight and does not compromise the efficient buffer usage of the existing LSS.