| Commit message (Collapse) | Author | Age | Files | Lines |
| ... | |
| |
|
|
| |
fitted to the keys searched, between events, and build trees from the leaves up: BFS core 89 to 55 ms (022)
|
| |
|
|
| |
searched, narrow columns, a JIT with registers and loop guards, read ahead; criteria first
|
| |
|
|
| |
forms by hand beat C only packed, narrow and read ahead (123)
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
a frame's fields one by one, and inline group_of (021)
- Compiled code paid only where blocks repeat; a block run once (a view's
cell, a fold's item, most handlers) ran slower through the code's entry
and its calls of step: the bank's table queries took 16-24% more
instructions. Now only a block with an EACH or a LOOP is compiled, and
a module with none keeps no compiled code.
- run() set its whole frame each time (the compiled code's run among it);
now its fields one by one.
- group_of is inlined into its pass: after the executor's refactor it
was a call per row (per_branch +4-5% instructions; now +1.1-1.3%).
|
| |
|
|
| |
tree's seek, still seven times C's (021)
|
| |
|
|
| |
find a slice against C's 21 (021)
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
| |
at the finger's leaf, and move indices only for a list an index refers to (021)
- The frame keeps the run compiled code read a tree's item from last (the
list as it was: its version); an item it holds is read from its column,
else pure_run_item takes the item's run and steps.
- pure_list_run starts at the leaf the finger keeps when it holds the
position (a slice found by key just before): no second descent; the
run after it is found from the root (PureRun.pathless).
- LIST_APPEND and LIST_REMOVE move index states only for a list one
refers to.
- A search (core only): 89 ms compiled (answers the executor's).
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
arithmetic, has by bits, a DENSE field by key, a FLAT item, and loop iterations (021)
- module-jit.c writes CONST, LOAD, STORE, the local and row loads,
INDEX_FIRST, LIST_COUNT, INDEX_MOVE, NOT, NEG, FLOOR, the number
arithmetic and comparisons (DIVIDE and NON_FINITE faults where the
executor gives them), LIST_HAS by a key set's bits, a joined FIND's
field of a DENSE list, a FLAT list's ITEM_GET, and a walk's and a
count's iteration (work charged as the executor charges it). Anything
else, or a list in another form, still calls step.
- module-list.c says where a list keeps what the code reads
(pure_list_layout); list_of and item_field are shared.
- PURE_JIT_DUMP=file writes the code made, to read with objdump.
- A search (core only): 103 ms compiled, 122 ms executed; the answers
(hops, reached, work) are the executor's.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
call the same steps (021)
- run() keeps a block's operand stack and open loops in a Frame; step()
runs one instruction (branches and loops too, one dispatch), and
enter_each, enter_loop, leave_loop and iterate are their own functions.
The executor is the same machine code, 7% fewer instructions.
- The verifier records each instruction's stack height (from its block's
start), so compiled code knows each slot's place.
- module-jit.c (AArch64 Linux; elsewhere, or PURE_JIT=0, none): a
module's handlers', views' and folds' blocks compiled when it loads.
Branches and loops are branches; every other instruction calls the
executor's own step, so both run the same meaning. A loop that is not
opened lands where its iteration would have ended; an empty loop
iterates in place.
- test-module recompiles a module whose code a test changes after load.
- A search (100,000 nodes, 1,000,000 edges, core only) is 150 ms
compiled, against 122 ms executed: a call of step for every instruction
costs more than the executor's switch; the hot instructions are written
as code next.
|
| |
|
|
| |
trees for the next event (021)
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
key as one (021)
- pure_list_arrays: an event's list (never shared, journaled or undone)
keeps its items in arrays: FLAT in order, or DENSE at their keys when
keyed by small whole numbers. A position asked of a DENSE list makes it
FLAT; moves past a few times its items, arrays past 64 MiB, or a third
change of form make it a tree. Answers are the tree's; the stored bytes
stand for its items in the memory budget whatever it keeps them in.
- pure_list_add need not say where an item went: a list no index state
refers to is not asked.
- A LIST_FIND whose index the next ITEM_GET of its list takes at once is
joined to it at load (PLAN_JOINED): pure_list_lookup reads the field by
key, at the key in a DENSE list.
- test-module arrays_equivalence: beside a tree, every step of a random
run (adds, inserts, removes, changes, finds, reads, runs, clears) gives
the same answers through every form and change of form.
- A search (100,000 nodes, 1,000,000 edges, core only): 187 to 126 ms.
|
| |
|
|
| |
keys, then a JIT, with criteria (021)
|
| |
|
|
| |
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)
|
| |
|
|
| |
two, and a plain view's reads
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
and pushed (020)
- pure-serve --view-workers N (a CPU each; 0: on the connection threads):
a GET of a view with a body (or a table with such a column) goes to the
workers, which show it from the app's newest snapshot on readers of
their own and give the answer back as an owner does (eventfd). The
connection thread reads and writes its other connections meanwhile.
- A snapshot of another module than the request was resolved with, or
none (the app stopped), answers 503 stopped. Past an app's queue=N of
them, 503 busy.
- A push group of such a view hands one evaluation at a time; a newer
snapshot meanwhile is evaluated after it; a group whose subscribers all
left stays until its evaluation is back.
- check.py server_view_workers: 8 askers of a long view while its app
changes module (another view in its place) and then goes: every answer
the old module's, the new one's, 503 or 404; streams closed at once
leave nothing; --view-workers 0 as before.
|
| |
|
|
| |
instruction (020)
|
| |
|
|
| |
instructions and a search no time (020)
|
| | |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
in one descent, and loop without recursion (020)
- An event's list keyed by a number keeps its keys beside its tree: bits
while they are small whole numbers, else an open-addressing table;
has() reads only that, and inserts, removes, key changes, their undoing
and clears keep it.
- Each thread keeps fingers on the last four leaves it read (list id and
version must match): reads by position and searches by key that land in
them do not descend.
- pure_list_add finds a key's place and inserts there in one descent; a
stored keyed list's appends take it too, journaled as inserts.
- Child searches count over the whole guide; item fields know at load
whether they are text; loops run on a stack of loop records, not by
recursion; module-list.c builds at -O3.
- test-module: the key set and fingers answer as a model at every step
(bits to table, removes inside runs, undone changes, clears, shares),
and a stored keyed list's adds replay from its journal.
|
| |
|
|
| |
criteria (020)
|
| |
|
|
| |
show a subscriber costs writes nothing (019)
|
| | |
|
| |
|
|
| |
freshness by push and polling (019)
|
| |
|
|
| |
(019)
|
| |
|
|
| |
carry streams across module changes
|
| |
|
|
| |
must show (019)
|
| | |
|
| |
|
|
| |
research 119)
|
| |
|
|
| |
measure instead (018)
|
| | |
|
| | |
|
| |
|
|
| |
compare across machines (033)
|
| |
|
|
| |
bounded (018 stage 2)
|
| | |
|
| | |
|
| | |
|
| |
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
| |
An app given a tokens file (tokens=FILE, --tokens FILE) takes requests
bearing a token whose SHA-256 the file lists: read for its views, queries
and documents, write for all, input=NAME for one input alone; 401
without one, 403 past its grant. A delivery another node sealed needs
none: its key told who sent it. A connection keeps its last token's
grant; a reading swaps the tokens without stopping the app.
|
| |
|
|
|
|
|
|
|
| |
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.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
Stopping an app and starting it again reads its whole store again: a
million items took 142 ms, as long as restarting the host (030). A
running app whose line changed but not its store now goes quiet (what it
took applied, saved and answered, its batches back, its outlets closed,
its checkpoint finished), has its module replaced in memory
(pure_app_replace carries its states, and the sends in its outbox, by
name), its budgets set and its outlets made anew, and resumes; a module
its states do not go into stops it. An app whose store changed, one a
save of which is uncertain, and one that does not run still start from
their stores. A reading says changed for the first. A snapshot is
published again when the module changed though the states did not.
server_apps_lifecycle reads the changing apps' views (lists among them)
as they change, and moves an app's store; it passed 10 times alone, 5
under load and 6 with an ASan pure-serve.
|
| |
|
|
|
|
|
|
|
|
|
|
| |
restart (030)
An app of a million items (a list grown by doubling) or of a thousand, in
a host of apps, its store in RAM: the longest gap between two answered
writes while it changes module on SIGHUP (stopped and started again from
its store, 71e319b), and while the host is restarted. A thousand items:
1.59 ms against 3.05 (median of 20 and 10). A million: 150.64 ms against
149.73 (10 and 5): both are the store read again (pure_app_start, 142
ms), so changing does not answer sooner than restarting.
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| |
A host of apps reads its --apps file again on SIGHUP, on a thread of its
own (pure-apps, the one that takes the signal): a file with a wrong line
changes nothing; the apps of lines gone stop and their routes go (404
no_app); those of changed lines, and those not running (they could not
start, or a save of theirs is uncertain), start again from their stores,
states carried by name; new ones take places of their own; a line out
says what was done. An app stops by taking nothing more (503 stopped),
applying, saving and answering what it took, waiting for its batches at
other apps, then closing its outlets (what they did not deliver stays in
the outbox) and its store. Its link streams find their inputs again as it
starts. Owners' locks are made with their places; counts are kept over
an app's runs; workers grow with the apps that run, a CPU each at most.
server_apps_lifecycle adds, removes and changes apps, and changes the
receivers (a local outlet's and a link's, their inputs at other numbers,
taken by name) and the senders while 600 sends each and writes go: every
send arrives once and in order, a write answered 200 is applied once and
one answered 503 stopped not at all, the app left alone fails no request;
an app whose store cannot be made starts at the next reading once it
can; kill -9 and the host starts as the file says. It passed 10 times
alone, 5 under load, 6 with an ASan pure-serve.
|
| |
|
|
|
|
|
|
|
|
|
|
|
| |
A stream finds its input once, when it is made; an app that starts again
with another module may have the input at another number, of another
shape, or none. pure_link_receiver_rebind marks the streams to inputs of
a prefix stale: each finds its input again by name, with the fields it
was laid out with, before its next delivery and at its sender's next
packet, keeping what it holds; one none takes is refused as a new one
would be. pure_link_listener_rebind asks it of the listener's thread.
test-link's rebound case stops its app, starts it with the input at
another number, then of another shape taken by name: all 3,000 records
come once, in order (without the rebind, none after the stop).
|