MIRROR: javascript for ๐Ÿœ's, a tiny runtime with big ambitions
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

put arena hint for linux only

+108 -36
+45 -7
README.md
··· 17 17 18 18 - [Why Ant?](#why-ant) 19 19 - [Installation](#installation) 20 + - [Benchmarks](#benchmarks) 20 21 - [Spec conformance](#spec-conformance) 21 22 - [Building Ant](#building-ant) 22 23 - [Security](#security) ··· 25 26 26 27 ## Why Ant? 27 28 28 - | | Ant | Node | Bun | Deno | 29 - | ------------------- | ----------- | --------- | -------- | --------- | 30 - | Binary size | **~9 MB** | ~120 MB | ~60 MB | ~90 MB | 31 - | Cold start | **~3-5 ms** | ~30-50 ms | ~5-10 ms | ~20-30 ms | 32 - | Engine | Ant Silver | V8 | JSC | V8 | 33 - | JIT | โœ“ | โœ“ | โœ“ | โœ“ | 34 - | WinterTC conformant | โœ“ | partial | โœ“ | โœ“ | 29 + | | Ant | Node | Bun | Deno | 30 + | ------------------- | ---------- | ------- | ------ | ------ | 31 + | Binary size | **~9 MB** | ~120 MB | ~60 MB | ~90 MB | 32 + | Cold start | **~5 ms** | ~31 ms | ~13 ms | ~25 ms | 33 + | Engine | Ant Silver | V8 | JSC | V8 | 34 + | JIT | โœ“ | โœ“ | โœ“ | โœ“ | 35 + | WinterTC conformant | โœ“ | partial | โœ“ | โœ“ | 35 36 36 37 Ant is designed for environments where size and startup time matter: serverless functions, edge computing, embedded systems, CLI tools, and anywhere you'd want JavaScript but can't afford a 50MB+ runtime. 37 38 ··· 54 55 | js-zoo (ES2016+) | ~86% | | 55 56 | js-zoo (overall) | **88%** | 1211/1368 passing | 56 57 | test262 | ~50% | Improving, focus is on real-world coverage | 58 + 59 + ## Benchmarks 60 + 61 + ### Cold start 62 + 63 + Measures the time to import [Hono](https://hono.dev), register routes, and exit. Each runtime loads he same `bench-coldstart.js` script from `examples/npm/hono/` that creates a Hono app with two routes, prints "ready", and calls `process.exit(0)`. No HTTP server is actually started, this isolates module resolution and initialization overhead. 64 + 65 + Measured with hyperfine (10 warmup runs, 100 timed runs): 66 + 67 + ```bash 68 + hyperfine --warmup 10 --runs 100 \ 69 + 'ant examples/npm/hono/bench-coldstart.js' \ 70 + 'node examples/npm/hono/bench-coldstart.js' \ 71 + 'bun examples/npm/hono/bench-coldstart.js' \ 72 + 'deno run --allow-read --allow-env examples/npm/hono/bench-coldstart.js' 73 + ``` 74 + 75 + | Runtime | Mean | Min | Max | Relative | 76 + | ------- | ---------- | ------- | -------- | ------------ | 77 + | **Ant** | **5.7 ms** | 5.0 ms | 7.3 ms | **1.00** | 78 + | Bun | 12.8 ms | 11.6 ms | 16.4 ms | 2.24ร— slower | 79 + | Deno | 24.8 ms | 22.2 ms | 29.4 ms | 4.32ร— slower | 80 + | Node | 31.1 ms | 27.1 ms | 151.7 ms | 5.41ร— slower | 81 + 82 + <details> 83 + <summary>Environment</summary> 84 + 85 + | Detail | Value | 86 + | -------- | --------------------------------- | 87 + | Hardware | Apple M4 Pro, 24 GB RAM, 14 cores | 88 + | OS | macOS 15.7.5 (arm64) | 89 + | Ant | 0.9.1 | 90 + | Node | 25.9.0 | 91 + | Bun | 1.3.13 | 92 + | Deno | 2.7.12 | 93 + 94 + </details> 57 95 58 96 ## Building Ant 59 97
+10
examples/npm/hono/bench-coldstart.js
··· 1 + import { Hono } from 'hono'; 2 + 3 + const app = new Hono(); 4 + 5 + app.get('/', c => c.text('hello hono!')); 6 + app.get('/json', c => c.json({ message: 'hello' })); 7 + 8 + // signal that startup is complete, then exit 9 + console.log('ready'); 10 + process.exit(0);
+52 -28
include/arena.h
··· 16 16 #include <errno.h> 17 17 #endif 18 18 19 - #define ANT_ARENA_MIN (32 * 1024) 20 - #define ANT_ARENA_MAX (64ULL * 1024 * 1024 * 1024) 21 - 22 - #define ANT_ARENA_THRESHOLD (256ULL * 1024 * 1024) 23 - #define ARENA_GROW_INCREMENT (8ULL * 1024 * 1024) 24 - #define ANT_CLOSURE_ARENA_MAX (2ULL * 1024 * 1024 * 1024) 25 - 26 - // preferred base for mmap on platforms where the kernel may hand out 27 - // addresses above the 47-bit NaN-boxing ceiling. We pick 0x100000000 28 - // inside the 47-bit range and above the typical text/data segments. 29 - #define ANT_MMAP_HINT ((void *)0x100000000ULL) 30 - 31 19 typedef struct { 32 20 uint8_t *base; 33 21 size_t committed; ··· 39 27 void *free_list; 40 28 } ant_fixed_arena_t; 41 29 30 + #define ANT_ARENA_MIN (32 * 1024) 31 + #define ANT_ARENA_MAX (64ULL * 1024 * 1024 * 1024) 32 + 33 + #define ANT_ARENA_THRESHOLD (256ULL * 1024 * 1024) 34 + #define ARENA_GROW_INCREMENT (8ULL * 1024 * 1024) 35 + #define ANT_CLOSURE_ARENA_MAX (2ULL * 1024 * 1024 * 1024) 36 + 37 + // the kernel can hand out mmap addresses above the 47-bit NaN-boxing ceiling. 38 + // ant_mmap_low() probes the low VA range with MAP_FIXED_NOREPLACE 39 + // before falling back to an unpinned mmap. only needed on Linux 40 + #ifdef __linux__s 41 + 42 + #ifndef MAP_FIXED_NOREPLACE 43 + #define MAP_FIXED_NOREPLACE 0x100000 44 + #endif 45 + 46 + #define ANT_MMAP_LOW_START 0x10000000000ULL 47 + #define ANT_MMAP_LOW_END 0x7F0000000000ULL 48 + #define ANT_MMAP_LOW_STEP 0x10000000000ULL 49 + 50 + static inline void *ant_mmap_low(size_t size, int prot, int extra_flags) { 51 + int flags = MAP_PRIVATE | MAP_ANON | extra_flags; 52 + 53 + for ( 54 + uintptr_t addr = ANT_MMAP_LOW_START; 55 + addr + size <= ANT_MMAP_LOW_END; 56 + addr += ANT_MMAP_LOW_STEP 57 + ) { 58 + void *p = mmap((void *)addr, size, prot, flags | MAP_FIXED_NOREPLACE, -1, 0); 59 + if (p != MAP_FAILED) return p; 60 + } 61 + 62 + void *p = mmap(NULL, size, prot, flags, -1, 0); 63 + return (p == MAP_FAILED) ? NULL : p; 64 + } 65 + 66 + #endif 67 + 42 68 #ifdef _WIN32 43 69 44 70 static inline void *ant_arena_reserve(size_t max_size) { ··· 70 96 #else 71 97 72 98 static inline void *ant_arena_reserve(size_t max_size) { 73 - void *p = mmap(ANT_MMAP_HINT, max_size, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); 74 - if (p == MAP_FAILED) return NULL; 75 - if ((uintptr_t)p >> 47) { 76 - munmap(p, max_size); 77 - p = mmap(NULL, max_size, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); 78 - if (p == MAP_FAILED) return NULL; 79 - } 80 - return mantissa_chk(p, "mmap"); 99 + #ifdef __linux__ 100 + void *p = ant_mmap_low(max_size, PROT_NONE, 0); 101 + #else 102 + void *p = mmap(NULL, max_size, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0); 103 + if (p == MAP_FAILED) p = NULL; 104 + #endif 105 + return p ? mantissa_chk(p, "mmap") : NULL; 81 106 } 82 107 83 108 static inline int ant_arena_commit(void *base, size_t old_size, size_t new_size) { ··· 105 130 } 106 131 107 132 static inline void *ant_os_alloc(size_t size) { 108 - void *p = mmap(ANT_MMAP_HINT, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 109 - if (p == MAP_FAILED) return NULL; 110 - if ((uintptr_t)p >> 47) { 111 - munmap(p, size); 112 - p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 113 - if (p == MAP_FAILED) return NULL; 114 - } 115 - return mantissa_chk(p, "mmap"); 133 + #ifdef __linux__ 134 + void *p = ant_mmap_low(size, PROT_READ | PROT_WRITE, 0); 135 + #else 136 + void *p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 137 + if (p == MAP_FAILED) p = NULL; 138 + #endif 139 + return p ? mantissa_chk(p, "mmap") : NULL; 116 140 } 117 141 118 142 static inline int ant_arena_decommit(void *base, size_t old_size, size_t new_size) {
+1 -1
meson.build
··· 94 94 elif host_machine.system() == 'darwin' 95 95 ant_link_args += ['-Wl,-stack_size,0x2000000'] 96 96 else 97 - ant_link_args += ['-Wl,-z,stacksize=134217728'] 97 + ant_link_args += ['-Wl,-z,stack-size=134217728'] 98 98 endif 99 99 if get_option('linker_map') 100 100 ant_linkmap = meson.project_build_root() + '/ant.linkmap'