Skip to content

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.

Compare

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
Execution time · ms
ProgramImplementationMedianMinimumMaximumSamples
StartupAugust1.421.271.7760
StartupC1.171.081.4360
StartupNode19.3517.7421.8660
StartupPython15.9815.0219.8160
Integer loop · 2 million stepsAugust7.637.319.7260
Integer loop · 2 million stepsC8.17.939.0860
Integer loop · 2 million stepsNode30.3728.6337.860
Integer loop · 2 million stepsPython248.07239.71281.3960
Map + Set · 20,000 entriesAugust2.742.523.1960
Map + Set · 20,000 entriesC1.661.52.8260
Map + Set · 20,000 entriesNode22.8421.227.9560
Map + Set · 20,000 entriesPython18.811820.8360
Map + Set · 200,000 entriesAugust17.0916.2819.560
Map + Set · 200,000 entriesC6.295.886.7160
Map + Set · 200,000 entriesNode38.3636.541.0160
Map + Set · 200,000 entriesPython40.8838.5943.6960
JSON · 5,000 recordsAugust7.577.13860
JSON · 5,000 recordsC1.951.85.3460
JSON · 5,000 recordsNode21.3520.0242.7560
JSON · 5,000 recordsPython32.2730.962.9660
WorkloadAugustCNodePython
Startup1.42 ms1.17 ms19.35 ms15.98 ms
CPU · 2 million iterations7.63 ms8.10 ms30.37 ms248.07 ms
Map + Set · 20,000 entries2.74 ms1.66 ms22.84 ms18.81 ms
Map + Set · 200,000 entries17.09 ms6.29 ms38.36 ms40.88 ms
JSON · 5,000 round trips7.57 ms1.95 ms21.35 ms32.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
// 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
// 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
// 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
// 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
// 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

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.

Compare

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
HTTP throughput · requests/s
ProgramImplementationMedianMinimumMaximumSamples
1 concurrent clientsAugust21,09020,38321,1085
1 concurrent clientsNode23,90022,58124,3265
16 concurrent clientsAugust57,16255,68258,9015
16 concurrent clientsNode55,63950,94856,6525
64 concurrent clientsAugust52,20151,05852,5265
64 concurrent clientsNode54,07650,05954,9925
ClientsAugust req/secNode req/secAugust p95 latencyNode p95 latency
121,09023,9000.06 ms0.05 ms
1657,16255,6390.47 ms0.49 ms
6452,20154,0762.17 ms1.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
// 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
// aug-spec: "main.aug.md" explains this file. Read it before changes; refresh with aug spec.
import reply from routes
serve reply on port 0

Start the app in one terminal:

sh
aug run path/to/http-example

Save the load generator below as http-load.mjs. In another terminal, replace PORT with the printed port. The comparison suite uses this load generator:

sh
node http-load.mjs --url http://127.0.0.1:PORT/bench \
  --concurrency 64 --requests 5000 --warmup 1000 --rounds 3

The 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
js
#!/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.

Compare

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
Peak process memory · MiB
ProgramImplementationMedianMinimumMaximumSamples
Map + Set · 20,000 entriesAugust4.74.74.723
Map + Set · 20,000 entriesC4.364.364.363
Map + Set · 20,000 entriesNode57.3657.257.413
Map + Set · 20,000 entriesPython13.2213.1913.223
Map + Set · 200,000 entriesAugust36.8636.8636.863
Map + Set · 200,000 entriesC25.3925.3925.393
Map + Set · 200,000 entriesNode91.3991.3491.423
Map + Set · 200,000 entriesPython55.2755.2555.283
WorkloadAugustCNodePython
Startup1.3 MiB1.3 MiB46.5 MiB8.3 MiB
CPU · 2 million iterations1.3 MiB1.3 MiB52.5 MiB8.4 MiB
Map + Set · 20,000 entries4.7 MiB4.4 MiB57.4 MiB13.2 MiB
Map + Set · 200,000 entries36.9 MiB25.4 MiB91.4 MiB55.3 MiB
JSON · 5,000 round trips1.9 MiB1.5 MiB47.9 MiB9.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 ​

  1. Choose a representative finite workload in main.aug. Give it a deterministic result so you can check correctness.
  2. Run it once and verify the output. Keep input size, dependencies, native compiler, machine and background load recorded.
  3. Measure a release executable repeatedly. Compilation is excluded; process startup is included.
sh
aug check path/to/project
aug run path/to/project
aug bench path/to/project --iterations 20 --warmup 3 --json > benchmark.json

aug 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
c
#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
js
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
python
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.

The world runs on language