A while ago I wrote a kdb+ tickerplant (it's on GitHub): a feed pushing options quotes every millisecond, a tickerplant that logs them and fans them out, an rdb, a cep that keeps live metrics per option, an hdb, a gateway that asks both databases at once, and a monitor. Plain q, nothing clever. For 2.9.0 the test was simple. Run those .q files on Amber, unchanged.
What the plant asked for
Mostly the parts of q a demo never touches:
- q's wire format, both ways.
hopen,h x,neg[h] x,h[]for a deferred reply, and.z.pg .z.ps .z.po .z.pc .z.wdoing their jobs. A q client can query Amber and the other way round. - A real timer.
\tand.z.ts. The feed is a 1 ms timer. - Logs and
-11!. The rdb and the cep replay today's log when they start, so a crash loses nothing. - The hdb q wrote. Its column files, enums and sym file. My repo had lost its
.dfiles (git and dot files), so Amber reads tables without them too. Funny thing: q can't load that repo anymore, Amber can. - Writing it back as q would. After the rdb's
eod[], q still reads the database. - A console that behaves. A process with a port open reads its console a line at a time, like q, so rlwrap works and what the handlers print doesn't stair-step across the screen.
Plus the small stuff a real script is made of: a select over several indented lines, { [name] ...} with a
blank before the args, .z.D as local time, @[h;msg;f] on a handle.
Bugs only real code finds
Two I'd never have hit with tests I wrote myself. Every tickerplant defines upd, and Amber's update
ran a function called upd too, so inside an rdb any update called the rdb's handler instead. And
.z.D in a query inside a lambda looked like a local named z, which in k makes a lambda take three
arguments, so it came back as a projection. Both are fixed and tested now.
Then the Amber way
Running q unchanged is the compatibility test. Then I wrote the plant again the way you'd write it for Amber. The feed builds everything that doesn't move once and sends ten columns a tick, no table. The cep keeps one vector per metric with a slot per option and updates them in place: no select, no group by, a table only when you ask for one.
| what | time |
|---|---|
| cep, one batch of 20 quotes, the q original | ~175 µs |
| cep, one batch of 20 quotes, Amber-native | ~6.7 µs |
the tp's whole upd (log it, send it twice, stamp it) | ~12 µs |
| Amber to Amber, sync round trip | 51 µs |
| feed to tp to subscriber, p50, either version | 220–250 µs |
One laptop, WSL2, both plants on Amber 2.9.0, the same probe subscribed to each tp.
The last row is the honest one. Between processes, what you wait for is waking them up, not what they compute: at one tick a millisecond every process sleeps between batches, and on this machine the wake-up costs more than all the work. Spinning instead of sleeping is the next thing to try.
Try it
Everything q-side is on the Talking to q page. The short version:
amber tp.q -p 5000 / any q script; a .q file loads the q libraries first
amber rdb.q -p 5002
h:hopen 5002
h"count quotes"