# 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 call | Median (ms) | 10th to 90th percentile (ms) |
| --- | --- | --- |
| A whole live call through a binding, 412 calls | 138 | 117 to 173 |
| The HTTP request inside a live call, 242 calls | 138 | 116 to 179 |
| A live call less its HTTP request, as recorded | 1.1 | 0.4 to 2.4 |
| A live call less its HTTP request, with the rounding taken out | 1.6 | 0.9 to 2.9 |
| The Rust core against a local test server, one request, 1,800 calls | 1.7 | 1.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 builds came from checkpoint 3, published on 2026-10-01 from commit `bfc180a10`. The published command and the published C library are debug builds. A release build of each from the same commit gives the comparison.
- The machine is an AMD Ryzen 7 5825U with 8 cores, 16 threads and 27 GB of memory, on Linux for x86_64. Other builds shared it all day. The load average stayed between 21.3 and 26.6 during the local runs. It was 25.1 when the paid run started and 20.7 when it ended. The spreads below include that noise.
- Each binding asked the same ten questions. Each question starts from the site's own sample for that function and keeps its input small. The answer cache was off for every timed call.
- Each binding timed its own calls with its language's clock. The clock wraps the call alone. The command was timed as a whole process.
- A test server on the same machine answers each request at once. The local runs were pinned to 4 of the 16 threads. Each binding made one warm-up call and 40 timed calls for each function, 5 times over.
- The paid run made one warm-up call for each binding. Then it made two timed calls to Jev for each function. It ran from 09:56 to 09:57 Eastern time.
- The Rust binding, built for release, stands for the core. "Added" is a binding's median minus the core's median for the same function. A number below zero means the two differ by less than the noise.
- The Rust binding reports the time of each HTTP request for all ten functions. The 14 C-interface bindings report it for every function except `decide`. They call `decide` through the typed C function, which returns no request times. The other bindings report none.

## 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](https://github.com/botassembly/thinkthen/blob/main/sdlc/records/qf-readme-first-run.md) 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.

| Binding | Added, median (ms) | Most added, one function (ms) | Live, median (ms) | Live, 10th to 90th (ms) |
| --- | --- | --- | --- | --- |
| [Ada](/install/ada/) | 1.1 | 3.6 in `recognize` | 156 | 135 to 244 |
| [C (debug library)](/install/c/) | 1.4 | 3.6 in `recognize` | 129 | 114 to 282 |
| [C (release library)](/install/c/) | -0.1 | 0.1 in `recognize` | none | none |
| [COBOL](/install/cobol/) | 1.1 | 2.9 in `recognize` | 135 | 120 to 148 |
| [C++](/install/cpp/) | 1.2 | 3.4 in `recognize` | 145 | 126 to 190 |
| [C#](/install/csharp/) | 1.4 | 3.8 in `recognize` | 137 | 123 to 164 |
| [Dart](/install/dart/) | 1.3 | 3.1 in `recognize` | 124 | 110 to 153 |
| [DuckDB](/install/duckdb/) | 1.8 | 5.2 in `relate` | 138 | 120 to 171 |
| [Go](/install/go/) | 1.4 | 2.8 in `recognize` | 130 | 117 to 150 |
| [Java](/install/java/) | 0.9 | 2.6 in `recognize` | 148 | 119 to 185 |
| [Kotlin](/install/kotlin/) | 1.3 | 3.8 in `recognize` | 134 | 113 to 190 |
| [Objective-C](/install/objective-c/) | 1.1 | 3.8 in `recognize` | 135 | 116 to 172 |
| [pandas](/install/pandas/) | 3.0 | 6.7 in `annotate` | 138 | 124 to 178 |
| [PHP](/install/php/) | 0.7 | 2.3 in `recognize` | 167 | 146 to 220 |
| [Polars](/install/polars/) | 1.0 | 1.6 in `recognize` | 130 | 110 to 157 |
| [PostgreSQL](/install/postgresql/) | 0.2 | 1.8 in `filter` | 127 | 102 to 136 |
| [Python](/install/python/) | 0.2 | 0.4 in `recognize` | 138 | 112 to 178 |
| [R](/install/r/) | 0.9 | 3.0 in `recognize` | 139 | 119 to 184 |
| [Ruby](/install/ruby/) | 0.1 | 0.4 in `recognize` | 144 | 117 to 181 |
| [Rust (the core)](/install/rust/) | 0.0 | none | 120 | 108 to 178 |
| [Scala](/install/scala/) | 1.7 | 4.2 in `recognize` | 129 | 115 to 159 |
| [SQLite](/install/sqlite/) | -0.1 | 1.3 in `filter` | 132 | 114 to 162 |
| [Swift](/install/swift/) | 0.8 | 2.6 in `recognize` | 148 | 131 to 171 |
| [TypeScript](/install/typescript/) | 0.2 | 0.4 in `relate` | 138 | 115 to 152 |
| [Zig](/install/zig/) | 0.8 | 2.4 in `recognize` | 142 | 120 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

| Function | Core, median (ms) | Core, 10th to 90th (ms) | Added, median binding (ms) | Live, median (ms) | Live, 10th to 90th (ms) |
| --- | --- | --- | --- | --- | --- |
| [`decide`](/functions/decide/) | 1.8 | 0.8 to 3.9 | 0.6 | 140 | 116 to 171 |
| [`choose`](/functions/choose/) | 1.7 | 0.8 to 3.0 | 1.0 | 141 | 115 to 170 |
| [`tag`](/functions/tag/) | 1.7 | 1.2 to 4.4 | 1.1 | 140 | 117 to 183 |
| [`score`](/functions/score/) | 1.6 | 1.0 to 4.5 | 1.0 | 134 | 119 to 156 |
| [`filter`](/functions/filter/) | 1.8 | 1.1 to 3.5 | 1.2 | 141 | 116 to 178 |
| [`rank`](/functions/rank/) | 1.9 | 1.6 to 4.8 | 0.9 | 137 | 115 to 187 |
| [`find`](/functions/find/) | 1.6 | 1.3 to 2.8 | 1.1 | 137 | 118 to 170 |
| [`annotate`](/functions/annotate/) | 1.9 | 1.0 to 4.0 | 1.3 | 139 | 117 to 172 |
| [`recognize`](/functions/recognize/) | 3.4 | 2.1 to 4.9 | 2.8 | 280 | 250 to 317 |
| [`relate`](/functions/relate/) | 1.6 | 0.9 to 2.3 | 1.1 | 134 | 117 to 178 |

[calls.csv](/learn/overhead/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 run | Debug, median (ms) | Debug, 10th to 90th (ms) | Release, median (ms) | Release, 10th to 90th (ms) |
| --- | --- | --- | --- | --- |
| A whole `decide` run against the test server | 22 | 16 to 33 | 18 | 13 to 21 |
| A `thinkthen --version` run | 7.3 | 6.4 to 12 | 3.8 | 3.4 to 6.0 |
| Time inside fsync and fdatasync during a `decide` run | 5.8 | 3.3 to 8.3 | 6.9 | 3.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.

| Input | Requests | Live (ms) | HTTP (ms) | Debug, median and 10th to 90th (ms) | Release, median and 10th to 90th (ms) |
| --- | --- | --- | --- | --- | --- |
| 306 song titles | 1 | 322 | 267 | 59, 54 to 72 | 23, 19 to 54 |
| 1,000 messages | 2 | 492 | 353 and 304 | 234, 170 to 358 | 51, 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.

[Caching and replay](/learn/caching/) · [Install](/install/) · [Functions](/functions/)

On GitHub: [github.com/botassembly/thinkthen](https://github.com/botassembly/thinkthen)
