aboutsummaryrefslogtreecommitdiffstats
path: root/benchmarks/http/README.md
Commit message (Collapse)AuthorAgeFilesLines
* Module format v7, 027's 4b-2: keyed documents gone, a keyed list of records ↵eunhhu43 hours1-7/+6
| | | | 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
* Add a sampling profiler for machines without perfeunhhu2026-09-271-0/+11
| | | | | | | | | | | | 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.
* Lay Pure out as a core with the parts selected around iteunhhu2026-09-271-1/+1
| | | | | | | | | | | | | | | | | | | | | 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.
* Read JSON bodies in place, groups included (082)eunhhu2026-09-271-0/+3
| | | | | | | | | | | | | | | | | | | | | 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.
* Show views from the last save's snapshot on connection threads (080)eunhhu2026-09-261-1/+1
| | | | | | | | | | | | | | | | | | | | 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.
* Measure reads made while durable writes go oneunhhu2026-09-261-0/+4
| | | | | | | | 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.
* Record the app service against axum and drogon (077)eunhhu2026-09-261-0/+9
| | | | | | | | | | | | 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.
* Compare the HTTP host with drogon and by CPU timeeunhhu2026-09-261-1/+3
| | | | | | | | | | | | | | | | | 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.
* Compare durable writes on a device in the same stateeunhhu2026-09-261-0/+10
| | | | | | | | | | 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.
* Serve modules over persistent connectionseunhhu2026-09-261-1/+1
| | | | | | | | | | | | | | | | 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.
* Benchmark the HTTP hosts against axumeunhhu2026-09-261-0/+12
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.