Performance and benchmarks
Compare August programs with C, Node, and Python versions doing the same tasks. Read the graphs, inspect the source, or download a program to measure work that resembles your application.
These measurements use the August 1.0.0 candidate with LLVM 23.1.2. Results apply to the shown workloads and host. For reliability and deployment limits, see production readiness.
The extended qualification adds eight C comparisons for numeric, collection and application work. The safety gyms exercise results, rejected operations, cleanup and memory checks separately. Each measured program has a downloadable project and a code/spec view in the gallery.
The DGX Spark results record a second physical ARM64 host, including all benchmark programs, safety gyms and real public native imports. Read each host's measurements separately.
Read the graphs
Execution and memory bars use lower is better. HTTP throughput uses higher is better. Read the workload name and units before comparing: a 20,000-entry map and a two-million-step CPU loop do different amounts of work. The execution panels have separate linear scales; compare implementations within a panel. Toggle implementations to compare them without changing the measurements. Select “Show observed ranges” to see the recorded minimum and maximum; these are not confidence intervals. Expand the chart’s values for a selectable data table.
All results below were recorded on October 7, 2026 (UTC): Apple M5, macOS Darwin 25.6.0, ARM64, LLVM 23.1.2 for August, Apple clang version 21.0.0 (clang-2100.3.34.2), Node 24.21.0, and Python 3.9.6. August and C use -O2 without LTO. Raw samples, checksums, build timings and environment include the LLVM tool and runtime identities.
Execution time
Execution time
Median ms · shorter is better. Each panel starts at zero and scales to the implementations shown.
StartupCode and spec ↗
- August1.42 ms
- C1.17 ms
- Node19.35 ms
- Python15.98 ms
Integer loop · 2 million stepsCode and spec ↗
- August7.63 ms
- C8.1 ms
- Node30.37 ms
- Python248.07 ms
Map + Set · 20,000 entriesCode and spec ↗
- August2.74 ms
- C1.66 ms
- Node22.84 ms
- Python18.81 ms
Map + Set · 200,000 entriesCode and spec ↗
- August17.09 ms
- C6.29 ms
- Node38.36 ms
- Python40.88 ms
JSON · 5,000 recordsCode and spec ↗
- August7.57 ms
- C1.95 ms
- Node21.35 ms
- Python32.27 ms
Exact values and sample ranges
| Program | Implementation | Median | Minimum | Maximum | Samples |
|---|---|---|---|---|---|
| Startup | August | 1.42 | 1.27 | 1.77 | 60 |
| Startup | C | 1.17 | 1.08 | 1.43 | 60 |
| Startup | Node | 19.35 | 17.74 | 21.86 | 60 |
| Startup | Python | 15.98 | 15.02 | 19.81 | 60 |
| Integer loop · 2 million steps | August | 7.63 | 7.31 | 9.72 | 60 |
| Integer loop · 2 million steps | C | 8.1 | 7.93 | 9.08 | 60 |
| Integer loop · 2 million steps | Node | 30.37 | 28.63 | 37.8 | 60 |
| Integer loop · 2 million steps | Python | 248.07 | 239.71 | 281.39 | 60 |
| Map + Set · 20,000 entries | August | 2.74 | 2.52 | 3.19 | 60 |
| Map + Set · 20,000 entries | C | 1.66 | 1.5 | 2.82 | 60 |
| Map + Set · 20,000 entries | Node | 22.84 | 21.2 | 27.95 | 60 |
| Map + Set · 20,000 entries | Python | 18.81 | 18 | 20.83 | 60 |
| Map + Set · 200,000 entries | August | 17.09 | 16.28 | 19.5 | 60 |
| Map + Set · 200,000 entries | C | 6.29 | 5.88 | 6.71 | 60 |
| Map + Set · 200,000 entries | Node | 38.36 | 36.5 | 41.01 | 60 |
| Map + Set · 200,000 entries | Python | 40.88 | 38.59 | 43.69 | 60 |
| JSON · 5,000 records | August | 7.57 | 7.13 | 8 | 60 |
| JSON · 5,000 records | C | 1.95 | 1.8 | 5.34 | 60 |
| JSON · 5,000 records | Node | 21.35 | 20.02 | 42.75 | 60 |
| JSON · 5,000 records | Python | 32.27 | 30.9 | 62.96 | 60 |
| Workload | August | C | Node | Python |
|---|---|---|---|---|
| Startup | 1.42 ms | 1.17 ms | 19.35 ms | 15.98 ms |
| CPU · 2 million iterations | 7.63 ms | 8.10 ms | 30.37 ms | 248.07 ms |
| Map + Set · 20,000 entries | 2.74 ms | 1.66 ms | 22.84 ms | 18.81 ms |
| Map + Set · 200,000 entries | 17.09 ms | 6.29 ms | 38.36 ms | 40.88 ms |
| JSON · 5,000 round trips | 7.57 ms | 1.95 ms | 21.35 ms | 32.27 ms |
Times include a fresh process's startup and exclude compilation: 3 warmups and 60 measured runs for each implementation, with the execution order rotated. The measurement client runs outside the compiler process, and every executable must produce the expected checksum. Node and Python start a new interpreter each time. These batch timings do not measure a warmed JIT. The startup row shows the cost of starting each process.
The programs below are the actual benchmark sources. The suite checks their printed results against the C, Node and Python references on every run.
The C reference is tailored to these inputs: it preallocates integer tables and checks JSON field types. August uses its normal Map, Set and typed JSON APIs. The programs produce the same checked results, but perform different amounts of validation and memory management. Read the complete sources below when comparing them.
The CPU program takes 7.63 ms in August and 8.10 ms in C on this host. The large-collection program takes 17.09 ms in August. The JSON timings include interpreter startup for Node and Python. Compare these results within their workloads.
Startup program
Save this as main.aug. Its result is 7; the measurement includes starting and stopping the executable.
main.aug
// aug-spec: "main.aug.md" explains this file. Read it before changes; refresh with aug spec.
print(value=7)CPU program
This loop performs two million dependent integer steps. Its result must be 819677333. Change the iteration count to measure a different workload; keep it the same across comparisons.
main.aug
// aug-spec: "main.aug.md" explains this file. Read it before changes; refresh with aug spec.
// A loop-carried dependency prevents removal of the computation.
int state = 123
int index = 0
while index < 2000000:
int product = state * 48271
state = product - (product / 2147483647) * 2147483647
index = index + 1
print(value=state)Map and Set program
This creates a Map and Set, inserts 20,000 values, checks membership, and sums values while iterating. Its results are 599970000 and true. The larger graph uses the same program with 20000 changed to 200000, producing 59999700000 and true.
main.aug
// aug-spec: "main.aug.md" explains this file. Read it before changes; refresh with aug spec.
own Map<int, int> values = {}
own Set<int> unique = {}
int index = 0
while index < 20000:
values.set(key=index, value=index * 3)
unique.add(value=index)
index = index + 1
int checksum = 0
for (key, value) in values:
if unique.contains(value=key):
checksum = checksum + value
print(value=checksum)
print(value=values.length() == unique.length())JSON program
These two files belong in one folder. The program parses JSON, reads a typed record, writes JSON, and checks the result 5,000 times. Its result is 250000.
data.aug
// aug-spec: "data.aug.md" explains this file. Read it before changes; refresh with aug spec.
record Payload(int id, string message, List<int> values)main.aug
// aug-spec: "main.aug.md" explains this file. Read it before changes; refresh with aug spec.
import parse from json
import Payload from data
int checksum = 0
int index = 0
try:
while index < 5000:
document = parse(input="{\"id\":7,\"message\":\"hello\",\"values\":[1,2,3]}")
payload = document.decode<Payload>()
encoded = Json(value=payload).stringify()
checksum = checksum + payload.id + encoded.length()
index = index + 1
print(value=checksum)
catch JsonError error:
exit(status=1)main.yaml
packages:
json: "https://github.com/GreenPandaStudios/augscript/src/stdlib/json#v0.19.0"HTTP throughput
HTTP throughput
Median requests/s · longer is better. Each panel starts at zero and scales to the implementations shown.
1 concurrent clientsCode and spec ↗
- August21,090 requests/s
- Node23,900 requests/s
16 concurrent clientsCode and spec ↗
- August57,162 requests/s
- Node55,639 requests/s
64 concurrent clientsCode and spec ↗
- August52,201 requests/s
- Node54,076 requests/s
Exact values and sample ranges
| Program | Implementation | Median | Minimum | Maximum | Samples |
|---|---|---|---|---|---|
| 1 concurrent clients | August | 21,090 | 20,383 | 21,108 | 5 |
| 1 concurrent clients | Node | 23,900 | 22,581 | 24,326 | 5 |
| 16 concurrent clients | August | 57,162 | 55,682 | 58,901 | 5 |
| 16 concurrent clients | Node | 55,639 | 50,948 | 56,652 | 5 |
| 64 concurrent clients | August | 52,201 | 51,058 | 52,526 | 5 |
| 64 concurrent clients | Node | 54,076 | 50,059 | 54,992 | 5 |
| Clients | August req/sec | Node req/sec | August p95 latency | Node p95 latency |
|---|---|---|---|---|
| 1 | 21,090 | 23,900 | 0.06 ms | 0.05 ms |
| 16 | 57,162 | 55,639 | 0.47 ms | 0.49 ms |
| 64 | 52,201 | 54,076 | 2.17 ms | 1.78 ms |
A real August GET /bench endpoint returns a newly constructed typed JSON record. The Node reference constructs and serializes the same response. Both run on loopback with HTTP/1.1 keep-alive, 1,000 warmup requests and five fresh-server rounds of 5,000 measured requests per concurrency level. Every response must have status 200, JSON content type, and the exact expected data. The measured runs had zero errors. p95 is the median of the five per-round p95 latencies; graph error bars show observed throughput min/max, not confidence intervals.
The Node load generator runs on the same machine and consumes CPU. This closed-loop test has no TLS, authentication, logging, database, outbound network calls or slow clients. Use it to compare this endpoint, not to size a production service. HTTP/2, HTTP/3 and streaming are not benchmarked here.
Use the table to compare throughput and latency at the concurrency you expect. Each server uses one process on one OS thread.
HTTP program
Place these files in one folder. Every request constructs a Reply and returns it as JSON. Port 0 selects a free port; the server prints the chosen port when it starts.
routes.aug
// aug-spec: "routes.aug.md" explains this file. Read it before changes; refresh with aug spec.
record Reply(int id, string message)
endpoint GET "/bench" as reply() :
return Reply(id=7, message="hello")main.aug
// aug-spec: "main.aug.md" explains this file. Read it before changes; refresh with aug spec.
import reply from routes
serve reply on port 0Start the app in one terminal:
aug run path/to/http-exampleSave the load generator below as http-load.mjs. In another terminal, replace PORT with the printed port. The comparison suite uses this load generator:
node http-load.mjs --url http://127.0.0.1:PORT/bench \
--concurrency 64 --requests 5000 --warmup 1000 --rounds 3The output includes requests/sec, p95 latency and validated request counts. An incorrect status, content type, JSON result, or timeout fails the run. The default expected result is {"id":7,"message":"hello"}. For your own JSON endpoint, pass --expected '{"your":"result"}'. This tool measures HTTP/1.1 without TLS. Its CLI rounds reuse your running server; the comparison suite starts a fresh server for each round.
The measurement uses a fixed number of keep-alive clients. Each client waits for its response before sending another request. Inspect the complete code below when reproducing a result.
HTTP load generator source
#!/usr/bin/env node
import assert from 'node:assert/strict';
import http from 'node:http';
import {resolve} from 'node:path';
import {fileURLToPath} from 'node:url';
export const statistics = samples => {
const sorted = [...samples].sort((a, b) => a - b), middle = Math.floor(sorted.length / 2);
return {samples, minimum:sorted[0], median:sorted.length % 2 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2,
p95:sorted[Math.ceil(sorted.length * .95) - 1]};
};
/** Closed-loop HTTP/1.1 load with a fixed number of keep-alive clients.
* Every response is checked; failed requests fail the measurement.
*/
export async function httpLoad(target, concurrency, count, expected = {id:7, message:'hello'}) {
assert.ok(Number.isInteger(concurrency) && concurrency > 0);
assert.ok(Number.isInteger(count) && count > 0);
const url = new URL(typeof target === 'number' ? `http://127.0.0.1:${target}/bench` : target);
assert.equal(url.protocol, 'http:', 'This benchmark measures HTTP/1.1 without TLS');
const agent = new http.Agent({keepAlive:true, maxSockets:concurrency}), latencies = [];
let next = 0;
const started = performance.now();
try {
await Promise.all(Array.from({length:concurrency}, async () => {
while (next++ < count) {
const begin = performance.now();
await new Promise((resolve, reject) => {
const req = http.get(url, {agent}, response => {
let body = ''; response.on('data', chunk => {body += chunk;});
response.on('error', reject); response.on('end', () => {
try {
assert.equal(response.statusCode, 200);
assert.match(response.headers['content-type'], /application\/json/);
assert.deepEqual(JSON.parse(body), expected);
latencies.push(performance.now() - begin); resolve();
} catch (error) { reject(error); }
});
});
req.setTimeout(10000, () => req.destroy(new Error('HTTP request timed out'))); req.on('error', reject);
});
}
}));
assert.equal(latencies.length, count);
const elapsedMs = performance.now() - started;
return {requests:count, errors:0, elapsedMs, requestsPerSecond:count * 1000 / elapsedMs, latencyMs:statistics(latencies)};
} finally { agent.destroy(); }
}
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
const option = (name, fallback) => {
const index = process.argv.indexOf('--' + name);
return index < 0 ? fallback : process.argv[index + 1];
};
try {
const url = option('url');
assert.ok(url, 'Use --url http://127.0.0.1:PORT/bench [--concurrency 64 --requests 5000 --warmup 1000 --rounds 3]');
const concurrency = Number(option('concurrency', 16)), requests = Number(option('requests', 5000));
const warmup = Number(option('warmup', 1000)), rounds = Number(option('rounds', 3));
assert.ok(Number.isInteger(rounds) && rounds > 0);
const expected = JSON.parse(option('expected', '{"id":7,"message":"hello"}'));
const results = [];
for (let i = 0; i < rounds; i++) {
await httpLoad(url, concurrency, warmup, expected);
const result = await httpLoad(url, concurrency, requests, expected);
if(!process.argv.includes('--raw-samples'))delete result.latencyMs.samples;
results.push(result);
}
const measured = statistics(results.map(result => result.requestsPerSecond));
console.log(JSON.stringify({url, concurrency, warmup, rounds:results, requestsPerSecond:measured}, null, 2));
} catch (error) { console.error(error.message); process.exitCode = 1; }
}Memory
Peak process memory
Median MiB · shorter is better. Each panel starts at zero and scales to the implementations shown.
Map + Set · 20,000 entries
- August4.7 MiB
- C4.36 MiB
- Node57.36 MiB
- Python13.22 MiB
Map + Set · 200,000 entries
- August36.86 MiB
- C25.39 MiB
- Node91.39 MiB
- Python55.27 MiB
Exact values and sample ranges
| Program | Implementation | Median | Minimum | Maximum | Samples |
|---|---|---|---|---|---|
| Map + Set · 20,000 entries | August | 4.7 | 4.7 | 4.72 | 3 |
| Map + Set · 20,000 entries | C | 4.36 | 4.36 | 4.36 | 3 |
| Map + Set · 20,000 entries | Node | 57.36 | 57.2 | 57.41 | 3 |
| Map + Set · 20,000 entries | Python | 13.22 | 13.19 | 13.22 | 3 |
| Map + Set · 200,000 entries | August | 36.86 | 36.86 | 36.86 | 3 |
| Map + Set · 200,000 entries | C | 25.39 | 25.39 | 25.39 | 3 |
| Map + Set · 200,000 entries | Node | 91.39 | 91.34 | 91.42 | 3 |
| Map + Set · 200,000 entries | Python | 55.27 | 55.25 | 55.28 | 3 |
| Workload | August | C | Node | Python |
|---|---|---|---|---|
| Startup | 1.3 MiB | 1.3 MiB | 46.5 MiB | 8.3 MiB |
| CPU · 2 million iterations | 1.3 MiB | 1.3 MiB | 52.5 MiB | 8.4 MiB |
| Map + Set · 20,000 entries | 4.7 MiB | 4.4 MiB | 57.4 MiB | 13.2 MiB |
| Map + Set · 200,000 entries | 36.9 MiB | 25.4 MiB | 91.4 MiB | 55.3 MiB |
| JSON · 5,000 round trips | 1.9 MiB | 1.5 MiB | 47.9 MiB | 9.7 MiB |
These are medians of three separate peak-RSS measurements through /usr/bin/time, in MiB, including the process runtime/interpreter. They are not allocation counts or retained heap after a GC. Long-running heap stability and leak behavior need separate soak tests.
Benchmark your own project
- Choose a representative finite workload in
main.aug. Give it a deterministic result so you can check correctness. - Run it once and verify the output. Keep input size, dependencies, native compiler, machine and background load recorded.
- Measure a release executable repeatedly. Compilation is excluded; process startup is included.
aug check path/to/project
aug run path/to/project
aug bench path/to/project --iterations 20 --warmup 3 --json > benchmark.jsonaug bench compiles with release optimization even when the project's normal setting is debug. Do not benchmark aug run: that command includes compiler work. Server programs run indefinitely, so use an HTTP load generator against a built server instead of aug bench. Record errors and latency as well as throughput. The comparison suite validates checksums; aug bench itself checks exit status, so verify your program's result first.
Reproduce the programs
Download a measured project, extract it, and prepare native dependencies. Each download contains the code and configuration shown in the gallery. Run finite programs with the commands under Benchmark your own project; use the HTTP load generator for the server. The complete C, Node, and Python reference programs are below, so you can save and inspect the comparison code too.
The 200,000-entry collection run changes 20000 to 200000 in the downloaded Map/Set program. Keep the matching input count in each reference implementation and verify the published checksum before comparing timings. Record the machine, compiler, toolchain version, input size, and flags with your results.
Maintaining the comparison suite describes the contributor workflow that produces the full graphs, raw samples, and checked source panels.
Comparison implementation sources
These are the complete reference programs used for the graphs. The C collection case preallocates integer tables, while August, Node and Python use their normal growing collections. Expected inputs and printed checksums match.
C reference
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "yyjson.h"
// A specialized integer table is a useful C baseline, not August's generic value representation.
typedef struct { int64_t key, value; unsigned char occupied; } Entry;
static size_t slot(Entry *table, size_t mask, int64_t key) {
uint64_t hash = (uint64_t)key;
hash ^= hash >> 30; hash *= UINT64_C(0xbf58476d1ce4e5b9);
hash ^= hash >> 27; hash *= UINT64_C(0x94d049bb133111eb); hash ^= hash >> 31;
size_t index = (size_t)hash & mask;
while (table[index].occupied && table[index].key != key) index = (index + 1) & mask;
return index;
}
int main(int argc, char **argv) {
if (argc != 3) return 2;
int64_t count = strtoll(argv[2], NULL, 10);
if (!strcmp(argv[1], "startup")) puts("7");
else if (!strcmp(argv[1], "cpu")) {
int64_t state = 123;
for (int64_t i = 0; i < count; i++) {
int64_t product = state * 48271;
state = product - (product / 2147483647) * 2147483647;
}
printf("%lld\n", (long long)state);
} else if (!strcmp(argv[1], "collections")) {
size_t capacity = 8; while (capacity < (size_t)count * 2) capacity *= 2;
Entry *values = calloc(capacity, sizeof(Entry)), *unique = calloc(capacity, sizeof(Entry));
if (!values || !unique) return 3;
for (int64_t i = 0; i < count; i++) {
values[slot(values, capacity - 1, i)] = (Entry){i, i * 3, 1};
unique[slot(unique, capacity - 1, i)] = (Entry){i, 0, 1};
}
int64_t checksum = 0;
for (size_t i = 0; i < capacity; i++) if (values[i].occupied && unique[slot(unique, capacity - 1, values[i].key)].occupied) checksum += values[i].value;
printf("%lld\ntrue\n", (long long)checksum);
free(values); free(unique);
} else if (!strcmp(argv[1], "json")) {
const char *input = "{\"id\":7,\"message\":\"hello\",\"values\":[1,2,3]}";
int64_t checksum = 0;
for (int64_t i = 0; i < count; i++) {
yyjson_doc *doc = yyjson_read(input, strlen(input), 0); if (!doc) return 4;
yyjson_val *root = yyjson_doc_get_root(doc);
yyjson_val *id = yyjson_obj_get(root, "id");
if (!yyjson_is_int(id) || !yyjson_is_str(yyjson_obj_get(root, "message")) || !yyjson_is_arr(yyjson_obj_get(root, "values"))) return 5;
size_t length = 0; char *encoded = yyjson_write(doc, 0, &length); if (!encoded) return 6;
checksum += yyjson_get_sint(id) + (int64_t)length;
free(encoded); yyjson_doc_free(doc);
}
printf("%lld\n", (long long)checksum);
} else return 2;
return 0;
}Node reference
const [workload, countText] = process.argv.slice(2), count = Number(countText);
if (workload === 'startup') console.log(7);
else if (workload === 'cpu') {
let state = 123;
for (let i = 0; i < count; i++) {
const product = state * 48271;
state = product - Math.trunc(product / 2147483647) * 2147483647;
}
console.log(state);
} else if (workload === 'collections') {
const values = new Map(), unique = new Set();
for (let i = 0; i < count; i++) { values.set(i, i * 3); unique.add(i); }
let checksum = 0;
for (const [key, value] of values) if (unique.has(key)) checksum += value;
console.log(checksum); console.log(values.size === unique.size);
} else if (workload === 'json') {
let checksum = 0;
for (let i = 0; i < count; i++) {
const value = JSON.parse('{"id":7,"message":"hello","values":[1,2,3]}');
checksum += value.id + JSON.stringify(value).length;
}
console.log(checksum);
} else if (workload === 'http') {
const { default: http } = await import('node:http');
http.createServer((req, res) => {
if (req.method !== 'GET' || req.url !== '/bench') { res.writeHead(404); res.end(); return; }
const body = JSON.stringify({ id: 7, message: 'hello' });
res.writeHead(200, { 'content-type': 'application/json', 'content-length': Buffer.byteLength(body) }); res.end(body);
}).listen(0, '127.0.0.1', function () { console.log('PORT ' + this.address().port); });
} else throw new Error('Unknown workload');Python reference
import sys
workload, count = sys.argv[1], int(sys.argv[2])
if workload == 'startup':
print(7)
elif workload == 'cpu':
state = 123
for index in range(count):
product = state * 48271
state = product - (product // 2147483647) * 2147483647
print(state)
elif workload == 'collections':
values, unique = {}, set()
for index in range(count):
values[index] = index * 3
unique.add(index)
checksum = sum(value for key, value in values.items() if key in unique)
print(checksum)
print(str(len(values) == len(unique)).lower())
elif workload == 'json':
import json
checksum = 0
for index in range(count):
value = json.loads('{"id":7,"message":"hello","values":[1,2,3]}')
checksum += value['id'] + len(json.dumps(value, separators=(',', ':')))
print(checksum)
else:
raise ValueError('Unknown workload')Production assessment
Measure your complete application under its expected load, including slow clients and failure cases. Short benchmarks do not establish heap stability, protocol conformance, or service capacity.
August remains experimental. See production readiness for deployment requirements, the roadmap for 1.0 work, and web and crypto limits for service-specific gaps.