<feed xmlns='http://www.w3.org/2005/Atom'>
<title>pure/serve/link.c, branch master</title>
<subtitle>Verified execution runtime unifying state, persistence, and replication.
</subtitle>
<id>http://git.yeowoo.org/pure/atom?h=master</id>
<link rel='self' href='http://git.yeowoo.org/pure/atom?h=master'/>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/'/>
<updated>2026-09-29T06:06:27Z</updated>
<entry>
<title>Put the links and the crypto they use in link/, a part of their own</title>
<updated>2026-09-29T06:06:27Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-29T06:06:27Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=a052b95c8135cdc5f9b91f8efc991cac80fbbe8b'/>
<id>urn:sha1:a052b95c8135cdc5f9b91f8efc991cac80fbbe8b</id>
<content type='text'>
libpure-link.a holds them; pure-serve and the M5 harness link it instead
of naming its source files.
</content>
</entry>
<entry>
<title>Send a record whole when it fits a packet, however many pieces it has</title>
<updated>2026-09-29T04:11:01Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-29T04:11:01Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=41cbb5631fd12dc223738de65246af1a69761bea'/>
<id>urn:sha1:41cbb5631fd12dc223738de65246af1a69761bea</id>
<content type='text'>
A record over a piece (1,000 bytes) went in pieces even when it fit the
packet whole: a record of 1,013 bytes went as two RECORD frames, and the
receiver gathered it the slow way (an allocation, pieces, then onto the
line). A record none of whose pieces went yet now goes whole in a RECORDS
frame whenever it fits what is left of the packet; its pieces are marked
sent together. A record too large for a packet, or one resent after
some of its pieces were lost, still goes in pieces, and a receiver takes
a record whole or in pieces alike.

The transport alone takes 2.85 us a record of 1,013 bytes, not 3.18
(quinn: 3.90). transport.c now checks every record delivered: in order,
once each, its bytes as sent. None was wrong in a network namespace with
1 ms of delay, 3 % loss and 10 % reordering, with records of 61, 1,013
and 16,013 bytes.
</content>
</entry>
<entry>
<title>Keep a link's queued records in one byte ring, taken back as they are stored</title>
<updated>2026-09-28T19:22:32Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-28T19:22:32Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=a6d0dcdb6a378115ac03d40964d323e82475332a'/>
<id>urn:sha1:a6d0dcdb6a378115ac03d40964d323e82475332a</id>
<content type='text'>
A queued record had an allocation of its own for its bytes and its
pieces' states, freed when the receiver stored it. Records come and go in
order, so their bytes now go one after another into a ring, never cut by
its end, and go back as the ring's head moves past the ones stored; a
ring too small grows to twice its size, the records moved into it in
order. A record of one piece keeps its piece's state in itself.

The transport alone drains 200,000 records of 61 bytes at 5.3 to 6.5
million a second; per record the link takes 0.137 us, the listener 0.14:
0.28 in all, against quinn's 0.26 a message over one stream.
</content>
</entry>
<entry>
<title>Keep a stream's records in order on a line, handed over as they are</title>
<updated>2026-09-28T19:20:28Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-28T19:20:28Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=e08987247a9abf5809064243732246b9a311f7bb'/>
<id>urn:sha1:e08987247a9abf5809064243732246b9a311f7bb</id>
<content type='text'>
A receiver copied each record into an allocation of its own, copied them
all again into a delivery, and freed each once stored: about 110 ns a
record of 61 bytes. Once the last number heard is known, whole records
that come in order now go onto the stream's line as a delivery carries
them (u32 size, bytes). The line is handed over as it is, and let go by
moving its head; its front is given back when there is no room at its
end. Records that come early or in pieces, or before the last heard is
known, still wait in held, and move onto the line once it reaches them.

The transport alone: the listener took 0.20 us a record of 61 bytes, now
0.15; with the link, 0.31 us a record in all, against quinn's 0.26.
</content>
</entry>
<entry>
<title>Take a burst whole with UDP GRO; key the listener's AES only when the run changes</title>
<updated>2026-09-28T19:15:06Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-28T19:15:06Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=ac6b67121c81e7adb305fd67b788e31b66c44093'/>
<id>urn:sha1:ac6b67121c81e7adb305fd67b788e31b66c44093</id>
<content type='text'>
A listener's socket now asks for GRO: the kernel hands over the datagrams
of one sender that came one after another as one buffer, each the
segment's size but the last, and on the loopback a burst sent with GSO
comes whole instead of cut into datagrams. The listener takes up to eight
such buffers at once. It gives its AES-GCM context a key only when the
packet's run has another than the one it holds, for opening and for
sealing answers.

The transport alone drained 200,000 records of 61 bytes at 4.2 million a
second (0.454 us a record, both sides); now at 4.9 million (0.394 us).
</content>
</entry>
<entry>
<title>Send small records in a RECORDS frame: 1 or 2 bytes of framing each, not 19</title>
<updated>2026-09-28T19:12:38Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-28T19:12:38Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=fd95d3087733f025d5d796e6696c7698aa5d65d2'/>
<id>urn:sha1:fd95d3087733f025d5d796e6696c7698aa5d65d2</id>
<content type='text'>
A RECORD frame carries a number, a size, an offset and a length before
each record's bytes, 19 bytes: a packet held 13.7 records of 61 bytes. A
RECORDS frame (0x06) carries the first record's number and a count, then
whole records whose numbers follow on, each its size as a varint and its
bytes: 17.2 a packet. A piece of a record larger than a piece still goes
in a RECORD frame; a receiver takes both.

The transport alone (research/experiments/019-idle-links/transport.c)
drains 200,000 records of 61 bytes in 11,600 packets instead of 14,600;
per record the link takes 0.217 us and the listener 0.23, 0.447 us in all,
against quinn's 0.43 a message over one stream.
</content>
</entry>
<entry>
<title>Wake a link's thread only when it sleeps, once</title>
<updated>2026-09-28T19:10:49Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-28T19:10:49Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=ee7f97001b0e193a766a0f889ae16f9d5af40fea'/>
<id>urn:sha1:ee7f97001b0e193a766a0f889ae16f9d5af40fea</id>
<content type='text'>
Every queued record wrote the link's eventfd, a system call each, and woke
the thread to send a packet of a few records. A record queued now wakes
the thread only when it sleeps and nobody woke it yet; one queued while it
sends marks the link, and the thread looks again before it sleeps. With
records queued one at a time as fast as a thread can, 200,000 small ones
went at 400k a second, and now at 730k; queuing one costs 0.43 us, not
1.0.

research/experiments/019-idle-links/transport.c measures the transport
alone: a link and a listener in one process, no app, no store. A backlog
of 200,000 records of 61 bytes drains at 3 to 4 million a second, 0.24 us
a record on each side.
</content>
</entry>
<entry>
<title>Drain a link's backlog without scanning it: 370k to 990k records a second</title>
<updated>2026-09-28T19:07:44Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-28T19:07:44Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=91754a57ad7b7a92f9935509367590db951f903b'/>
<id>urn:sha1:91754a57ad7b7a92f9935509367590db951f903b</id>
<content type='text'>
A backlog made the link slow in its square: the sender looked at every
queued record's pieces twice a turn to know whether any waited to be
stored, and the receiver moved all its held records down each time it let
the first one go.

- The sender counts the pieces received and not stored, and keeps where
  the unsent ones begin; every change of a piece's state goes through one
  place that keeps both.
- The receiver keeps its held records from a head index. One in order goes
  last, and one out of order finds its place by halving. The record's bytes
  and its pieces' marks are one allocation.
- Packets go in bursts with UDP GSO, 54 at a time: 0.45 us a packet on this
  Pi, against 2.4 us for a send each. A full packet is padded to a whole
  datagram (PADDING frames, as QUIC does). Datagrams are taken 64 at a time
  (recvmmsg).

Twenty thousand saved sends of 61 bytes drained at 370k a second; now at
990k. Per record, the sender's link thread took 1.1 us and now takes 0.5;
the receiver's took 2.4 and now takes 0.5.
</content>
</entry>
<entry>
<title>Name the link threads, so their CPU is seen apart (pure-link, pure-listen)</title>
<updated>2026-09-28T18:57:27Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-28T18:57:27Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=354a73d82a3f16d7fdc215c2c63d8fe9afd5ef00'/>
<id>urn:sha1:354a73d82a3f16d7fdc215c2c63d8fe9afd5ef00</id>
<content type='text'>
</content>
</entry>
<entry>
<title>Remember each sender's secret beside the node's key: no X25519 at restart</title>
<updated>2026-09-28T18:42:27Z</updated>
<author>
<name>eunhhu</name>
<email>necpition205@gmail.com</email>
</author>
<published>2026-09-28T18:42:27Z</published>
<link rel='alternate' type='text/html' href='http://git.yeowoo.org/pure/commit/?id=3308de49adfdb2a55c646e6b675da998f2c36900'/>
<id>urn:sha1:3308de49adfdb2a55c646e6b675da998f2c36900</id>
<content type='text'>
A listener lets a quiet sender's secret go with its active state, so a
node started again made one X25519 for every sender that came back (169
us on this Pi, most of a wake's CPU): asymmetric work in proportion to the
links, which design 011 counts as a reason to design again. The secret is
now remembered in a file beside the key (KEY.links, 0600, begun again for
another node's key): a place of 64 bytes a sender in the order allowed,
mapped, so a sender at rest takes no memory for it. A place is the first
28 bytes of the sender's key, a check (FNV-1a of the key and the secret)
and the secret, written secret first and key last; a place whose check
fails is made again. The UDP link checks see a receiver started again
make no X25519.
</content>
</entry>
</feed>
