| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
| |
and a Mac (121); 019 points at 020's view workers
|
| |
|
|
| |
meanwhile, and queue=N's busy and its places coming back (020)
|
| |
|
|
| |
shared, two askers 1.95 times one (122)
|
| |
|
|
| |
fingers found and undone (121)
|
| |
|
|
| |
what a request does (120)
|
| |
|
|
| |
show a subscriber costs writes nothing (019)
|
| | |
|
| |
|
|
| |
research 119)
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
| |
Requests to an app that checks tokens go 2.92% slower than to an open
one on 64 keep-alive connections, 2.2% of it the head the token makes
longer (an open app sent the same head is as slow); hashing every
request on a new connection costs 0.91%. 031 measures the three in
turns.
|
| |
|
|
|
|
|
|
|
| |
The use after free in local_done and the owner's lock made after another
app's outlet used it were found reading the code, not by the check,
which passed with both before it was made to change senders (115, 017).
The usage now says changed lines change in place unless their store
moved; and 115 names the path no check takes: a module refused while
changing in place.
|
| |
|
|
|
|
|
|
|
|
| |
Changing an app in place keeps a million items' app answering but for
1.12 ms (a host restart: 150 ms; stopping and starting it again from its
store, as first built, 150.4 ms), 171.5 ms when its records change shape
(332 ms). Another app's p99 stays 0.97 times as it was while one changes
every 0.2 s; a host of one app is as it was (TPC-C +0.7%, instructions
at most +0.02%); each module change leaves 19 KB. 030 holds the windows
of both builds, the isolation, the pairs, the counts and the memory.
|
| |
|
|
|
|
| |
The same events make byte-identical stores on the x86_64 WSL2 (4 KiB
pages) as on the Pi and the Mac, and a store made on either of the Pi
and the WSL2 opens on the other and goes on the same.
|
| |
|
|
|
|
|
|
|
| |
The same modules and inputs give the same outputs, failures and store
bytes on the Pi, an x86_64 WSL2 and an M3 Mac; other compilers found an
argument read before it was set, and the core and store build on macOS
again. WSL2's wall clock steps back about 2 s every 32 s. The Pi froze
twice under a measuring tool that held its output: it now streams it,
and experiments run under research/guard.py.
|
| |
|
|
|
|
|
| |
The loop alone slowed four links to half: woken by every record queued,
it chased the owner a record at a time. Pushing once a turn made links
1.6 times as fast, with threads or the loop alike; the loop's own cost
is within the noise. Twenty outputs now add two threads, not 21.
|
| |
|
|
|
|
|
|
|
|
| |
Two apps of one host, src sending one message for each of 1,000 items
in a burst event, 2,000 bursts a run, five rounds with the ways in
turns: the local outlet stores 1,141,642 sends a second at 1.17 us of
the host's CPU each, a loopback pure:// link to the host's own port
578,879 at 2.93 us (1.97 times the sends, 0.40 times the CPU). 017's
second stage asked for at least the link's rate at half its CPU or
less: it goes in. Every run checks dst's sum: each send came once.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
017's first stage met its four criteria on the Pi (RAM stores, five
rounds, the order of the ways turned): app B's 99th percentile while
app A is flooded by 64 connections is 168 us, half of two processes'
336 us; the two apps busy together write 146.1k a second against
146.2k, with the same 1.4 ms tail; an app costs a host 120 KiB of its
own memory (a process: 1,311 KiB PSS) and 100 apps still run on three
threads; a host of one app keeps its speed (TPC-C +1.2% at the median
of six pairs, two of them interfered and kept; the bank's queries
within 2.6%) and its instructions (+0.94% at most).
On the way: apps hopped between workers each turn (fixed in db4ee9c),
connections of one app spread over all connection threads made the
host a third more expensive a request and its tail a kernel tick
(fixed by home threads, d24881d), and the work budget's counting cost
every table row 26 instructions (fixed in be297ea). The earlier runs
are kept apart (db4ee9c's isolation, d24881d's pairs and counts, the
interrupted first try).
|
| |
|
|
|
|
|
|
|
|
| |
The six pairs rerun once the Pi had been quiet for a minute, on the same
binaries. Two pairs were still interfered (the first's before at an
owner share of 0.54, the sixth at 0.73 on both sides); the four clean
ones are -1.7%, -0.4%, +1.8% and +1.1%, CPU per transaction 43.25 to
42.9 us. The bank's queries are as fast or faster after, though the
interference fell more on the before runs; the instruction counts stay
the comparison to trust.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
016's second stage passed its criterion: six interleaved pairs against
ee21aec gave TPC-C (RAM, W=1) -0.6% at the median and the bank's
queries 0.0% to +2.2%, no run interfered. The ranks query was slower
all six times, and chasing it showed that on this Pi timing cannot
separate 1-3%: two servers asked in turns disagree with themselves
(ee21aec to e02bfc9 +1.6%, e02bfc9 to 481642f -1.2%, ee21aec to 481642f
+2.8%), and a query whose instructions grew 0.6% ran 3.5% faster.
Instructions do not move so: pcount.c counts the server's user-mode
instructions with perf_event_open (no perf here), and psample.c samples
where they go. ranks had grown 0.4% in instructions (the rest was where
code and data landed), per_branch 3.6% and tables 20 instructions a row,
which 07047c3 halved. The six pairs of 07047c3 were interfered (another
session's build, load 4-12) and are kept apart, unused.
|
| | |
|
| |
|
|
|
|
|
|
|
| |
Fetching a found item's cells, a leaf's keys and each level's keys ahead
made finding and reading items 20% faster on 100,000 items in a micro
benchmark, but TPC-C's clean pairs moved 1% at most and finding alone got
48% slower, so it is not in. The owner's finding samples were the descent,
not the fields: a write right after a read copies the leaf whole anyway,
and what costs is walking structures larger than the caches at random.
|
| | |
|
| |
|
|
| |
(015, 108)
|
| | |
|
| |
|
|
| |
check (108)
|
| | |
|
| |
|
|
|
|
|
|
|
| |
Most of it was the allocator: leaves past glibc's thread cache, freed by
whichever thread let the last snapshot go. jemalloc was 11-14% faster
at more memory; reusing the nodes in Pure gave a median +9.4% over six
pairs, 4-8% less CPU a transaction in all six, and 1.97x the state bytes
resident where it was 2.18x. TPC-C in RAM is now 4.0x a tuned SQLite at
W=1 and 3.5x at W=4; finding items is the owner's largest part now.
|
| |
|
|
|
|
|
|
| |
transaction
The 90x of 092 was against SQLite's defaults committing each
transaction; tuned for the card and grouped as Pure groups, SQLite is
4-11x behind, the bytes it writes a transaction being the difference.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
warehouses (106)
SQLite tuned first, by the criteria committed before (and, beyond them,
checkpoints each 40 MB of WAL, in its favour). On the SD card Pure stays
4.2-10.8x ahead at equal terminals, SQLite writing 8-18x the bytes a
transaction; in RAM 3.2-3.5x. All tables equal at W=1 and W=4, and
every run consistent. In RAM the one owner is the limit (0.94 of a core
at W=4, no gain from 16 to 64 terminals), a third of its busy time
copying nodes the snapshots share; checkpoints and memory are within
their criteria.
|
| | |
|
| |
|
|
|
|
|
|
|
| |
The board is Vega's examples/shared-board.aup, and Vega's shared_board
check runs its two nodes: changes on either agree, and a partition
healed agrees on the later change. Reading a named field list once a
delivery would save part of 310 ns a record during upgrades alone: not
worth an API. What review 004 asked that is left is 103's third item,
broader performance comparisons under conditions stated first.
|
| |
|
|
|
|
|
|
|
| |
aup spreads an item or a message into a send or a record, and sets an
item whole (c69682e): the board sends { ..at } and applies cards[k] =
Card { ..remote }, and a new field is written in two places, the record
and the output. v6 on both nodes, cut apart and joined again, agrees on
the later change; its module is byte for byte the one with every field
written.
|
| |
|
|
|
|
|
| |
A board record of five fields (38 B) sent with v3's output fields took
310 ns to lay out for v4's input, reading the field list each time.
Only streams of another shape pay it, and only while nodes are upgraded
one after the other: less than a delivery's own work.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
103's next step: grow a real app, adding and reordering fields, cutting
the connection, starting again, and write down what gets in the way.
Two nodes share a board (from Vega's examples/board.aup), each sending
its changes to the other, the later change winning.
Upgrading them one after the other deadlocked: fixed by recasting
waiting sends by name (9aa123e) and taking fields by name (014,
5ce7655), and under it a link that stopped for good when a refused
stream's try was lost (baaf955). Now the older node's changes reach the
newer one during the upgrade, the newer one's new field waits for the
older, and the boards agree a second after. Fields in another order
agree at once; a partition healed agrees on the later change; both
nodes started again go on. Two rough edges eased on the way (the node's
name, message like in aup); sends and applies are still written field
by field. CHECKPOINT's row for links says so.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
013's second step is in (d9e0b68): each transport holds a window of its
output's sends. 104 measures it with the receivers down: once the window
is full a node grows by the outbox alone, 219 and 218 B a send over the
link and HTTP (399 and 534 B before).
The third step is a measurement. A checkpoint holds one list's snapshot
while it writes the image, so what it adds is what the app changed
meanwhile: nothing visible on tmpfs or for a list that fits the card's
fast writes; on the SD card, a 22.4 MB list written for about 8 s while
1,400 items a second were set left +3.0 and +4.4 MiB. The highest
resident memory was twice the state bytes, inside 013's bound (the state
and one snapshot) and its stop criterion (3x).
021's run.py takes --items, --stores (a directory on the device to
measure) and --label, and reports the peak (VmHWM).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
104 measures a node's memory under a receiver that is down and under a
snapshot held for long (021's run.py: resident memory against what was
posted, 30 s a case). A held snapshot grew the node without end (6.4 to
678 MiB, fixed in d1e8f38); a slow receiver grows it by what was sent,
221 B a send, which is state until delivered, so the budget bounds state
and refuses past it.
013 is that budget's design: what it counts (state bytes, deterministic),
what an event over it does, why an answer takes its send's room, how
links and HTTP wait, what it does not count and those bounds, and the
checks. With --memory 4000000 the same load fills it in about 4 s and
the node stays at 9.6-9.8 MiB (45.9 MiB and growing without).
012 records the hole the budget found in the link (the full flag), and
103 that its clock fix fell short (385754c). CHECKPOINT gains the row.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
Three broke what data means (a walk's range missed the indices an insert
moves; an uncertain save of numbers alone let the next save through; an
output whose fields swapped names kept its waiting sends), four were
errors (a stateless timer never ticked, Vega's terminal lost a key cut
across reads, an input named edit forced documents, timers waited on the
wall clock). Each is fixed with a check that fails on the code before
and passes now, in Pure's and Vega's make check. The review's other
advice (a whole app's memory budget, one real app grown, comparisons with
their conditions set first) is what comes next.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
102: the spec (012) and a receiver written from it alone; the four holes
writing it found (REFUSED, the record's size, a peer's run places, the
memo's places beside) and the credit M6 showed fixed at 1,024 records;
M6 with ngtcp2 as a third, two rounds. Under reordering Pure carries the
most (45 Mbit/s; ngtcp2 29-47, quinn 1-2), under random loss the three
are the same, on a clean bottleneck Pure carries 3-5 % less (its framing,
some 112 B a packet to QUIC's 41), and where loss and reordering meet
ngtcp2 carries 1.3-3.6 times as much: undoing a loss it took for
reordering, it restores the window a real loss had cut too (its log: 22
of 34 undos past the window before the last cut; none without
reordering). Shares beside TCP stay within 25 %.
011 and the status table say so; 012 also says that a receiver answers
within 25 ms (a sender's PTO counts on it), that a shape and a record's
bytes are the app's, and why a packet of the past does no harm.
|
| |
|
|
|
|
|
|
|
| |
A path MTU black hole is seen and the link goes back to 1,200 bytes; a
receiver that lost its memo says LOST and M2 without a memo catches up
in 0.4-0.5 s, not 7.7; M5 stands beside ngtcp2 too (Pure 25 % lighter
than both QUIC from 1 KiB, 1.04 of ngtcp2 and 0.90 of quinn at 61 B);
and the stream's name in every packet, 096's guess for 61 B's gap, cost
nothing measurable, so it stays. 098 notes the one line LOST changes.
|
| |
|
|
|
|
|
|
|
| |
(096, 100)
61-byte records: 0.287 us a record against quinn's 0.31 in the same five
rounds (0.93 of it); 096's 1.06-1.12 was against quinn at 0.27, which
moves between 0.27 and 0.32 from day to day. H4 is no longer at the
edge.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
H3 of design 011 stands: with ten thousand devices sending 2,000 records
a second, the node's store, key and memo carried to another address,
nothing stopped for a 100 ms step, the moved node made no X25519, and
every record was applied once. Each device's first record after the move
waits one probe (30-45 ms here), as its first packet finds no way there.
The search found the listener answering from an address other than the
one a packet came to, and a link that looked for its receiver after its
probe went, losing every answer. H1-H4 are all measured now; 61 B's CPU
a delivery is at the edge of the criterion, to be looked at before step
5 (adoption and a specification).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
Forward secrecy, 098's first task left: keys that leak later open only
epoch 0. M2 after the epoch began and after a restart is as before it
(p50 0.4 ms, p99 0.8 ms, the same CPU, no X25519 on restart). It costs a
p50 of 0.7-1.0 ms more while ten thousand devices first meet and for
about 40 s of accepts after, and a receiver that lost its memo now
stalls 0.8 s and catches up in 7.7 s, not 0.2-0.7: it lost the epochs,
and each sender opens one again. The search also found a packet more a
device from each of M6's path probe and the security checks' challenge,
and a load generator whose own pass over its devices was the floor of
the latency it measured. 098's note that epochs narrow KCI to the first
epoch was wrong; corrected.
|
| | |
|
| |
|
|
|
|
|
|
|
|
| |
(097)
M6 of design 011: with a 50 Mbit/s bottleneck, a link carries 21-41
times quinn's goodput when packets reorder, the same under random loss,
and 1.3-3.5 times with both; its p99 at 50 records a second is the same
or shorter; beside TCP CUBIC it takes 0.48-0.55 of the path, inside the
guardrail. Only on a clean path is it 3-5% below quinn.
|
| |
|
|
|
| |
libpure-link.a holds them; pure-serve and the M5 harness link it instead
of naming its source files.
|
| |
|
|
|
|
|
| |
Between two pure-serve nodes, 16 KiB records drain at 33,600 a second
(23,000 before), the links spending 58 us a record (76), under quinn's 60
while the receiver also stores each one. 1,013 bytes: 406,000 a second
(347,000). Saving sends one by one: 5,000 of 16 KiB in 0.91 s (84.8).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
M5 of design 011. At first a link spent eight times quinn's CPU on a small
record: scans of the backlog, a system call and allocations a record, 19
bytes of framing. After the seven changes before this one, the transport
alone (transport.c; a backlog drained, every record checked) spends per
record 0.30 us for 61 bytes (quinn 0.27: 1.06 to 1.12 times over three
campaigns, at the design's 10 % line), 2.91 us for 1,013 bytes (quinn
3.88) and 45.2 us for 16,013 bytes (quinn 60.9): 25 % less for the larger
ones, and 30 % more records a second. What is left for small records is
per packet: the stream's names and the RECORDS head. quinn's numbers carry
no confirmation of storing and no exactly once.
Between two pure-serve nodes the links spend more per record, woken at
the receiving app's pace. Filling the sender showed the outbox is written
whole at every save, as the numbers heard were (094): the next thing to
change.
run.py gains --transport and --drain; the quinn service reads a drain a
chunk at a time and its client writes 64 KiB at a time.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
M2 and M4 of design 011. Ten thousand devices send an order every five
seconds (2,000 a second); the receiver is killed (-9) at about 21 s and
started again at once. With its secrets remembered the node shows no
100 ms step without an order stored, catches up in 0.2 s, spends no more
CPU than before and makes no X25519; 34 orders were sent again, and
every order made was applied once (three rounds; on the SD card the
same, with 0.2 s silent). Without the memo it makes 10,000 X25519, 1.6 s
of CPU. quinn under the same load stops for 1.0 s and catches up after
10.9 s: 10,115 new connections, about 7 s more of the service's CPU and
8 s of the clients'. A first contact takes Pure one round trip and QUIC
two, at 10, 50 and 100 ms of RTT.
run.py --restart and --first-contact make them; the quinn harness gains
a fixed port with a reset key from its key, clients that connect again,
and a drain for M5.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
M1 of design 011. A Pure node allows 1,000 to 1,000,000 senders; each
sends one order and, once told it is stored, rests. At rest a link costs
the node 132 bytes (the sender allowed, 73; its number heard, about 60)
and no transport state; with a million the node runs in 139 MB. A quinn
0.11 service holding the same exchange's clients connected at rest costs
38,106 bytes a connection: 289 times as much, past the design's 1/100.
A link at rest takes its next record in 0.56 ms to stored, one X25519 on
the node included; a new QUIC connection takes 1.06 ms and 2.8 times the
receiver's CPU, without storing.
research/experiments/019-idle-links: links.c, many senders written from
serve/link.h's packet format alone (a second implementation), and the
quinn service and clients (quic/, built offline); run.py makes both
measurements.
|