| Commit message (Collapse) | Author | Age | Files | Lines |
| |
|
|
| |
holding bytes doing their work: no collections table, DOC, DOC_SIZE or REPLACE (106 opcodes now 103, those from LEN on moved up), keyed messages or a base revision (pure_app_message takes an input and a payload), no PureData document callbacks (a data host opens states and saves them together), no burst by document key; the header 38 bytes; pure-edit-module gone; pure-serve without /objects, ETag, If-Match and If-None-Match, 412 or an app a thread for a module of documents alone (one without states or outputs runs so); a hole closed: an instruction in no handler's, view's or function's block was never verified yet the load's later passes read its arguments (a damaged module read out of bounds once the header moved what the mutation test damaged), now blanked to CONST 0; checks moved: server_documents (DOCS_SOURCE, keyed byte documents with the app's revision, against a Python model, one and three connection threads, a restart) in place of the parity with editor.pure, pipelining and bursts through kill -9 on its inputs and views, the lent budget on a text view (lending a document's read is server_lent_views' already), clocks and tokens on it; message tests without a document host; the HTTP benchmark's document suite runs the earlier document server and the baselines alone; the count harness builds against either message API (runs.sh build: -DPURE_KEYED_MESSAGES before 4b); counts: b33 against b32 with batches within 0.002% but BFS -0.33%, without batches BFS +0.63%, knapsack +0.52%, join +0.38%; the bank's queries (s32 on a v6 module, s33 on v7) field +0.68%, found +0.44%, large +0.42%, the rest within 0.13%; 008 as v7, 027's 4b-2 as made and 4c designed
|
| |
|
|
| |
freshness by push and polling (019)
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
An app given a tokens file (tokens=FILE in --apps, --tokens FILE for the
one app of a host) answers a request only when it bears a token
(Authorization: Bearer) whose SHA-256 the file lists with a grant: read
for its views, queries and documents, write for all, input=NAME for
those inputs alone; 401 unauthorized (WWW-Authenticate: Bearer) without
one, 403 forbidden past its grant. A delivery sealed by a key --sender
names needs none. The digests are compared in constant time; a
connection keeps its last token's digest, so the same token is hashed
once. SHA-256 is the host's own (serve/sha256.c), no OpenSSL loaded. A
reading puts a changed tokens file in place with the app running on; a
file that is not one stops the app. pure-serve --make-token GRANT makes
a token and its line. load takes PURE_LOAD_TOKEN.
|
| |
|
|
|
|
| |
load takes a ninth argument, PREFIX, put before every path: /NAME for
an app of a host of apps (pure-serve --apps, 017). Without it, as
before.
|
| |
|
|
|
|
|
| |
With four warehouses the order lines answered at once (about 140 MB of
JSON) outgrew pure-serve's 64 MiB budget, and the check stopped with
503; a larger budget would have left the answer's memory in the
resident size measured after.
|
| |
|
|
|
|
| |
Its default, each 1,000 pages, is 1 MB of WAL with the 1 KiB pages the
SD card favours: grouped commits there went from 203 to 694 a second,
and in RAM a thread's p99 from 1.2 to 0.2 ms (docs/research/106).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
run costs
The terminals hand their writing transactions to one writer, which runs
those waiting in one transaction, each in a savepoint, and commits them
with one sync; reads stay on the terminals' connections, as pure-serve
shows views from the last save. An undone New-Order is rolled back by
its caller now (it rolled back the whole group before, and counted as
failed after the split). Each run reports its CPU a transaction, the
owner thread's share of a core, its memory, and the bytes a transaction
had written, by the process and to the device. SQLite runs with its
database mapped, a 64 MiB cache and savepoint journals in memory unless
told otherwise.
|
| |
|
|
| |
and an SD card
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
benchmarks/bank/bank.aup keeps accounts, tellers and branches as keyed
lists and a history list; a transfer changes a row of each and appends a
history row in one event, as pgbench's TPC-B-like transaction does in
SQL. run.py makes a scale's rows and drives transfers through the load
generator's new transfer kind; sqlite-bank.c runs the same tables and
SQL on SQLite 3.46.1, a connection a thread, WAL.
With lists as trees, transfers run at 30.5k a second on 1,000 accounts
and 24.5k on 100,000 (RAM store, one connection thread, HTTP and JSON
included), where they ran at 3.2k on 30,000 before. Durable on the SD
card, Pure makes 1,339 a second and SQLite with synchronous=FULL 84 to
153. SQLite in process with no HTTP makes about 40k in RAM: recorded as
it is, with the write amplification of 100,000-row tables on the card
and ad hoc queries as what comes next.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
benchmarks/sample.c runs a command and samples where its threads spend
user time (the task clock every 50 us, one perf_event_open event a CPU,
since the kernel maps no ring for an inherited per-task event), then
names the samples from the ELF symbol tables of the files the command had
mapped: a stripped library from its debug file by build id. On arm64 it
also takes the link register, so time in a library's leaf (memcpy,
strcmp) is shown again by its caller. The command's user and system time
head the report.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The benchmark runs an earlier pure-serve build in turn with the current one
(--baseline). Against the build before the move, linking through libpure.a
costs nothing: a one-command build with the library's object order has the
same size and the same CPU per request. What moved was code placement, so
the runtime links last again, as it did; the two orders now differ by less
than rounds of one build do (total -0.4%, page +1.2%, mixed -0.1%). The
binary is 5,008 B smaller: unused flow.c functions stay in the library, and
the linker's A53 erratum padding is gone.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The app model, the tree store and the HTTP host lived in
prototypes/connections among the earlier document runtime, the store was
known only by its format's name, and Vega compiled six Pure sources by path.
Now, as the README's principle has it:
core/ runtime, flow and the app model (module*.c), with what the core
asks of storage (state.h, store.h)
store/ the tree store (PURETRE3, PUREREC1)
serve/ pure-serve: HTTP host, outbound delivery, JSON in place
cli/ pure-run, pure-edit-module
A top-level make builds libpure.a (app model and store, for hosts to link)
and the hosts; make check runs the store's and the app model's tests. The
earlier runtime still builds from prototypes/connections against core/ and
store/ (check, check-tree and check-core pass). Design 009 records the rule:
parts by role behind libraries; git repositories split by coupling, so the
store stays here while its contract moves with the core, and Vega stays apart.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
pure-serve no longer copies a JSON body's strings into the message's
payload. The ingress is the prefix, a table of each field's start and
size, and the body as it came; the connection thread scans the body
once, puts numbers into the prefix and records where each string is.
Only a string with escapes is unescaped, over itself (it never grows).
A group of fields comes as an object inside the body and, for a fixed
field, as its path in the query (?to.zip=7). A group where a field
belongs, a value where a group does, an array or true/false/null is
json_type; an unknown member, inside a group too, json_field.
The string scan and UTF-8 checks now pass over eight bytes at a time
while none needs a look (pure_utf8_step, so every text check gains).
On the Pi, posting {"name":"k","text":"x..."} to a stateless app went
from 652-655 to 3,737-3,953 requests/s with 256 KiB of text (server
CPU 1.52 to 0.25 ms a request, peak RSS 5.2 to 3.2-3.4 MiB) and from
29.1k to 76.3-76.7k/s with 4 KiB; the load generator gains a json
method and benchmarks/http/echo.aup is the app.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
The owner publishes a snapshot of the saved states after every save,
before it hands back the answers; connection threads take the newest
one each round and show views from it with a reader app of their own.
A read made after a write's answer sees the write, nothing unsaved or
undone is ever shown, and reads no longer wait while the owner applies
events or syncs. Apps with states always get an owner thread, even
with one connection thread. A round's inputs go to the owner first;
its views are shown and then answered together (answering each one as
it was shown made one thread's 20-row page 8% slower).
On the Pi 5, with 8 connections posting durable scores and 56 reading
the total, reads went from 10-16k/s at p99 13-29 ms to 107-149k/s at
p99 0.6-0.7 ms, and posts stayed in range; axum and drogon, which sync
under their lock, read 1.1-1.3k/s at p99 111-156 ms. A snapshot of a
changed 1,024-item list costs 1.2 us (65,536 items: 87 us) against a
3-4 ms save.
|
| |
|
|
|
|
|
|
| |
The app suite's last workload, mixed, runs two load generators at once:
8 connections post durable scores while 56 read the total. It shows
whether a server's reads wait while its writes sync. It is reported as
two rows, posts and reads; answers per CPU second count both.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
A view that shows a text state as it is (pure_module_text_state) now goes
out as the state's stored extents, as a document read does: the answer
holds the store mapping, charged to the budget, until it is sent and its
ticket released; a state with edits not yet stored is copied as before.
Documents and views share one loan path, and the extent array grows as
needed. A 1 MiB note read by 8 connections: 776-817 answers a second
copied, 1,875-1,901 lent, p50 9.2 to 3.5 ms, peak RSS 10.3 to 5.7 MiB.
The load generator learns GET /views/page.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
The scoreboard, 22 lines of aup served by its views and inputs, against
the same service written by hand in axum and drogon on the Pi: durable
posts about 76 times as many, reads level with drogon and ahead of axum
with the least CPU and memory. The note keeps what the profile showed on
the way (the kernel's share of a single thread, integers through printf,
an owner that saved too early) and what comes next. run.py gains
pure-serve-2 and waits for a server that prints its port before it
listens.
|
| |
|
|
|
|
|
|
|
|
| |
run.py --suite app serves the scoreboard of scores.aup (22 lines of aup)
with pure-serve against the same scoreboard written by hand in axum and
drogon: POST /inputs/score?points=N with a name as the body, GET
/views/total and a page of 20 rows as JSON. Durable, the baselines append
each score to a log and fdatasync it before answering (replayed at
start); the load generator learns the three requests.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A drogon 1.9 service with the axum baseline's contract joins the benchmark
(memory, fdatasync and crash-safe modes, three IO threads), built from
Debian's packages unpacked under .deps without installing them. drogon
listens once per event loop, so the service picks a free port itself:
with port 0 every loop got a port of its own and the load reached one.
The load generator has one core and saturates near 150k answers a second,
so the harness now also reports answers per second of server CPU time
(all threads). Over three rotated rounds: small GETs 151.6k/s on three
shards (drogon 145.3k, axum 120.9k) and 128k per CPU-second on one thread
(drogon 65k, axum 62k); 1 MiB GETs 2,794/s (axum 1,372, drogon 708);
crash-safe PUTs 5.1-5.8 times axum and 13-30 times drogon; peak RSS
5.0 MiB against 14.4 and 76.5 MiB. --workloads is now a regular expression.
|
| |
|
|
|
|
|
|
|
|
| |
An SD card slows down under sustained syncs, so the order of runs alone
changed write results several times over. The HTTP benchmark now repeats
rounds with the servers in a rotated order, gives every run a fresh store,
times the device (an in-place 4 KiB write + fdatasync) before each write
workload and reports medians over rounds with the probe range beside them.
--workloads picks workloads by label prefix.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A burst now looks at the store once (pure_store_pin) instead of once per
request; another shard's commit ends before its answer is sent, so a
request made after that answer still reads it. With this alone one thread
serves 125k small keep-alive reads per second, as many as axum on three
cores, with a shorter p99.
--threads N runs a shard per thread: its own SO_REUSEPORT listener, epoll
set, runtime domain, app and store handle. Writer sessions of the tree
store now take turns inside a process with a mutex (the file lock belongs
to the process and does not keep threads apart), and revisions settle
concurrent edits of a key. A module with states runs on one thread. Three
shards serve 151k reads per second; they still commit one by one, so
durable writes and 1 MiB reads are better on one thread for now, which
stays the default. Note 075 records the numbers.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
pure-serve replaces the module mode of server.c, which goes back to what
it was. It keeps the document contract, the runtime core (a work slot per
connection, the payload budget, core-owned ingress, borrowed PUT replies)
and the group commits, and adds what server.c lacked: HTTP/1.1 persistent
connections with in-order pipelining, an edge-triggered epoll owner
thread, up to 1,024 connections allocated as they arrive and reused, one
writev per answer, and documents opened through a store handle.
On the Pi 5 with one thread it serves 106k small keep-alive reads per
second (axum on three cores: 126k) at the same p99, 1,741 1 MiB reads
(axum: 1,370), and 1,126 durable writes from 64 connections (crash-safe
axum: 372), in 7.3 MiB against axum's 17-20 MiB. Note 075 has the table.
|
|
|
A C load generator, an axum service with the same document contract (in
memory, fdatasync, and crash-safe replacement), and a runner that pins the
servers and the load to separate CPUs. On the Pi 5, Pure uses about 40%
less memory, wins with a new connection per request and with eight durable
writers (3.2x the crash-safe axum), but loses 5x on keep-alive reads and
1.5x on 64 durable writers: no persistent connections, one thread, eight
connection slots and two fsyncs per commit. Note 075 records the numbers
and the targets.
|