ThinkThen

Overhead

A live call through a binding took a median of 138 ms on 2026-10-01. ThinkThen's own work took about 2 ms of it. Each binding adds a median of 3.0 ms or less. The slowest single function, pandas annotate, adds 6.7 ms.

The answer

Part of a callMedian (ms)10th to 90th percentile (ms)
A whole live call through a binding, 412 calls138117 to 173
The HTTP request inside a live call, 242 calls138116 to 179
A live call less its HTTP request, as recorded1.10.4 to 2.4
A live call less its HTTP request, with the rounding taken out1.60.9 to 2.9
The Rust core against a local test server, one request, 1,800 calls1.71.0 to 3.6

ThinkThen records each HTTP request's time rounded up to a whole millisecond. The rounding adds half a millisecond to a request on average. So the recorded gap reads about half a millisecond low, and the next row adds it back.

The HTTP request holds Jev's work and the network together. Jev sent no server time with any reply in this run. So this page cannot split Jev's time from the network's time.

How it was measured

The spend

A counting run against the test server came first. It counted 566 requests, 2,357 questions and 551,337 request bytes. The paid run sent the same requests. It held a reservation of 2,000,000 tokens under a cap of one dollar.

The replies reported 206,154 input tokens and 47,054 output tokens. Some bindings report no token counts, and some skip the count on a few calls. Each of those bindings sent the same request bytes as a binding that counts every call. Filling in its share from that binding gives about 286,012 input tokens and 57,331 output tokens.

TypeSafe publishes a Jev price of $0.042 a million input tokens, as this record notes. TypeSafe publishes no output price. So the full cost of this run is not known exactly. The input tokens alone cost about 1.2 cents, or 0.9 cents for the reported input tokens. The backend's bill is the real cost.

Each binding

A binding's added time is its median over the core across nine functions. The recognize function sends two requests one after the other. The medians leave it out. The core's own one-request calls spread from 1.0 to 3.6 ms. So a gap of a millisecond between two bindings is inside the noise, and the table lists bindings by name.

BindingAdded, median (ms)Most added, one function (ms)Live, median (ms)Live, 10th to 90th (ms)
Ada1.13.6 in recognize156135 to 244
C (debug library)1.43.6 in recognize129114 to 282
C (release library)-0.10.1 in recognizenonenone
COBOL1.12.9 in recognize135120 to 148
C++1.23.4 in recognize145126 to 190
C#1.43.8 in recognize137123 to 164
Dart1.33.1 in recognize124110 to 153
DuckDB1.85.2 in relate138120 to 171
Go1.42.8 in recognize130117 to 150
Java0.92.6 in recognize148119 to 185
Kotlin1.33.8 in recognize134113 to 190
Objective-C1.13.8 in recognize135116 to 172
pandas3.06.7 in annotate138124 to 178
PHP0.72.3 in recognize167146 to 220
Polars1.01.6 in recognize130110 to 157
PostgreSQL0.21.8 in filter127102 to 136
Python0.20.4 in recognize138112 to 178
R0.93.0 in recognize139119 to 184
Ruby0.10.4 in recognize144117 to 181
Rust (the core)0.0none120108 to 178
Scala1.74.2 in recognize129115 to 159
SQLite-0.11.3 in filter132114 to 162
Swift0.82.6 in recognize148131 to 171
TypeScript0.20.4 in relate138115 to 152
Zig0.82.4 in recognize142120 to 203

The debug C library accounts for most of the time the C-interface bindings add. The release C library adds nothing past the noise. pandas and Polars lack filter, rank, find and relate. SQLite and PostgreSQL send one request for each row in filter. Their filter calls send two requests here, and the live medians leave them out.

Each function

FunctionCore, median (ms)Core, 10th to 90th (ms)Added, median binding (ms)Live, median (ms)Live, 10th to 90th (ms)
decide1.80.8 to 3.90.6140116 to 171
choose1.70.8 to 3.01.0141115 to 170
tag1.71.2 to 4.41.1140117 to 183
score1.61.0 to 4.51.0134119 to 156
filter1.81.1 to 3.51.2141116 to 178
rank1.91.6 to 4.80.9137115 to 187
find1.61.3 to 2.81.1137118 to 170
annotate1.91.0 to 4.01.3139117 to 172
recognize3.42.1 to 4.92.8280250 to 317
relate1.60.9 to 2.31.1134117 to 178

calls.csv holds every binding and function. Each row gives the local median and its 10th and 90th percentiles, the median of each set, the added time, both paid calls and their HTTP time.

The command

Each run of the command starts a process, reads its settings and writes its usage totals to disk.

Part of a runDebug, median (ms)Debug, 10th to 90th (ms)Release, median (ms)Release, 10th to 90th (ms)
A whole decide run against the test server2216 to 331813 to 21
A thinkthen --version run7.36.4 to 123.83.4 to 6.0
Time inside fsync and fdatasync during a decide run5.83.3 to 8.36.93.6 to 11

Each row is 40 runs. The load average was 32.54 when they started and 32.26 when they ended. strace measured the time inside fsync and fdatasync. Across the nine one-request functions, the debug command adds a median of 17 ms over the core, and the release command adds 15 ms.

Live, a command run took a median of 219 ms. A call through a library took 138 ms. Each command run opens a new connection to Jev, and a library keeps its connection open between calls. The new connection is the likely cause of the difference. This run did not time the connection itself.

Large inputs

Two runs of the command sent many records at once. One ranked 306 song titles. The other decided over 1,000 order messages. Each ran once live and 10 times against the test server with each build.

InputRequestsLive (ms)HTTP (ms)Debug, median and 10th to 90th (ms)Release, median and 10th to 90th (ms)
306 song titles132226759, 54 to 7223, 19 to 54
1,000 messages2492353 and 304234, 170 to 35851, 41 to 84

The two requests for 1,000 messages ran side by side. On a large input the debug build takes several times as long as the release build.