| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| acton-0.31.0-1.aarch64.rpm | 2026-10-08 | 93.5 MB | |
| acton-0.31.0-1.x86_64.rpm | 2026-10-08 | 90.8 MB | |
| acton_0.31.0_amd64.deb | 2026-10-08 | 83.4 MB | |
| acton_0.31.0_arm64.deb | 2026-10-08 | 82.1 MB | |
| acton-linux-aarch64-0.31.0.tar.xz | 2026-10-08 | 107.2 MB | |
| acton-linux-x86_64-0.31.0.tar.xz | 2026-10-08 | 109.7 MB | |
| acton-macos-aarch64-0.31.0.tar.xz | 2026-10-08 | 118.1 MB | |
| acton-macos-x86_64-0.31.0.tar.xz | 2026-10-08 | 100.2 MB | |
| README.md | 2026-10-08 | 29.2 kB | |
| v0.31.0 source code.tar.gz | 2026-10-08 | 3.6 MB | |
| v0.31.0 source code.zip | 2026-10-08 | 4.4 MB | |
| Totals: 11 Items | 793.1 MB | 0 | |
This release brings major performance improvements across the compiler,
runtime, and standard library. Iteration is faster, many operations allocate
less, and actor scheduling and timers have lower overhead. Acton now uses its
own fork of the Boehm garbage collector, with performance improvements and
opt-in tuning options. The release also adds immutable collections, compact
numeric and bit containers, and a matrix module, alongside better performance
tools, cleaner project builds, and many other fixes and improvements.
Notable changes
Faster programs
Iteration now ends through ordinary control flow: next() returns a maybe
value instead of throwing an exception at the end. This makes some
iteration-heavy applications more than twice as fast. Range loops, numeric
conversions, hashing, and builtin operations allocate less, while actor
scheduling avoids unnecessary queue and synchronization work. Linux and macOS
now wait for timed messages on kernel timers instead of spinning, reducing CPU
time for timer-heavy workloads by more than 90%.
Garbage collection
Acton now uses its own fork of the Boehm garbage collector, with improvements
to parallel marking, thread-local allocation, and memory tracking. Improved
defaults apply automatically, while opt-in options let applications tune heap
growth, allocation budgets, mark storage, and dirty-page tracking through
Build.act. acton.rts.get_gc_info() reports the active mode and settings to
help inspect and compare configurations.
Linux and macOS applications can also choose mimalloc for C and native-library allocations. This is separate from garbage collection of Acton objects.
Performance benchmarks: acton test perf
acton test perf measures individual benchmarks at a fixed or calibrated
workload size. Reports include timing, allocation and memory use, GC statistics,
and optional hardware counters. Benchmarks mark their measured operations with
t.loop(), so setup and cleanup around the loop do not count toward operation
time or allocation totals.
Measurements can be compared with a saved report or another Git revision. For
example, acton test perf --name my_test --compare git:main builds both project
versions and measures main against the current working tree, including
uncommitted changes, at a shared workload size. Runs use fresh processes and
balanced pairs to reduce noise. Both versions use the running compiler and
runtime; utils/perf-compare supports measuring changes to the compiler,
runtime, or builtins themselves.
Scaling studies: acton test scale
acton test scale shows how a benchmark's cost grows with its workload. The
benchmark defines what scale means, such as the number of elements to sort,
and reads the chosen size with t.scale(). As with performance benchmarks,
only the body of the t.loop() loop contributes to operation time and
allocation totals.
The study increases the scale, usually doubling it, and measures each size in fresh processes until the observed growth settles over a broad range or a time or memory limit stops it. The report shows a table of growth between sizes and terminal charts of time, time per unit of scale, and allocated bytes.
A study can be compared with a saved study or a Git revision. By default,
acton test scale --compare git:main measures main and the current working
tree at the same completed workload sizes and overlays their curves. Explicit
end bounds allow both versions to explore independently.
New collections
Immutable ilist, idict, and iset values provide collection APIs without
mutation, with freeze() converting mutable collections into their immutable
forms. array, bitarray, and bitset offer compact storage for numeric and
Boolean workloads, while matrix adds numeric matrices with views that share
the underlying storage.
Language
- Add
after now delay: call()for delays that start when the statement runs, such as retry backoffs and pauses between requests. Plainaftercontinues to use the current message's scheduling baseline, keeping periodic work on a steady schedule and catching up after stalls. [#3162] - Add first-class immutable collection types by splitting collection protocols
into read-only and mutable parts.
IIndexed,ISliceable,ISequence,IMapping, andISetexpose access without mutation;ilist,idict, andisetimplement these read-only protocols, whilelist,dict, andsetadd the corresponding mutation operations. The genericFreeze[A]protocol andfreeze()consume mutable collections into their immutable forms, with hashableisetvalues usable as dictionary keys or nested sets. [#3122] [#3152] - Make augmented assignment use mutating protocol methods when a type overrides
them.
setapplies|=,&=,-=, and^=in place;listandbytearrayapply+=and*=in place; andbitsetandbitarrayupdate their packed storage and size. Aliases observe these changes, while immutable types retain value semantics andsum()leaves mutable start values unchanged. [#3226] [#3230] - End iteration through ordinary control flow instead of exceptions.
Iterator.__next__()andnext()now returnmaybe[A], usingjust(value)for items andnothing()for exhaustion. This makes some iteration-heavy real-world applications more than twice as fast. [#3137] - Treat
mutandpureeffects as temporarily interchangeable, allowing APIs written with either effect to compose while immutable local mutation rules are being redesigned. [#3084]
Compiler & Build
- Compile direct synchronous
forloops overrange()without allocating the range object on the heap, and pass statically known range bounds as unboxed integers. Nested range loops keep their private iterators in stack storage, while range values that may escape the loop remain heap allocated. [#3189] [#3224] - Compile fixed tuple-literal assignments into scalar temporaries and stores, avoiding a temporary tuple allocation while preserving left-to-right evaluation and simultaneous-assignment semantics. [#3223]
- Compile conversions between statically known fixed-width numeric types
without boxing, while retaining range checks for narrowing conversions, and
keep builtin sequence indices unboxed across lookup and mutable operations
where supported. The reported
nsieve 12benchmark fell from 4.2 seconds to 1.9 seconds. [#3177] - Generate allocation-free code for explicit
next()and__next__()calls even when producing and consuming the result are separated or the result is tested inside a larger condition, while preserving ordinarymaybevalues when they are passed elsewhere. [#3164] - Compile protocols that inherit another protocol with fixed type arguments without passing those type arguments to the wrong witness constructor, fixing C generation for nested protocol inheritance. [#3098]
- Keep imported protocol witnesses in import and definition order, so broad imported extensions no longer override more specific builtin witnesses and change inferred types. [#3198]
- Reuse statically known witnesses reached through multiple inherited protocols instead of constructing them repeatedly, including for list indexing and mutation. This makes affected benchmark programs run at three to six times their previous speed. [#3155]
- Compile augmented
<<=and>>=assignments with anintshift count for types such asbigint, matching the right-hand type accepted by ordinary shift expressions. [#3158] - Type check augmented
^=assignments for builtin and user-definedLogicaltypes instead of crashing the compiler. [#3163] - Stop passing project-only module selection options to distribution packages
such as
std, eliminating spurious Ziginvalid optionmessages in verbose build output and manually run build commands. [#3168] - Allow Zig to use the active macOS SDK again with current macOS 26 Command Line Tools instead of forcing its bundled headers through an obsolete compatibility workaround. [#3029]
- Preserve unchanged generated root stubs and Zig build files when their contents match, avoiding timestamp-only rewrites that invalidate otherwise reusable warm-build output. [#3099]
CLI & Project Workflow
- Generate
build.zigandbuild.zig.zonunderout/zig/instead of the project root, keeping generated build files out of source trees and removing the need for project-specific ignore rules. Files left in project roots by earlier versions are no longer used and can be deleted. [#3166] - Select an Acton project within a dependency archive with
subdirinBuild.actoracton pkg add --subdir, so monorepos can provide multiple packages from one archive while preserving sibling path dependencies. Package upgrades retain the selection, and local overrides point directly to the selected project root. [#3153] - Allow applications to pass literal Zig build options through
build_optionsinBuild.act, including GC mark layout, dirty-page tracking backend, and page-hash table size controls. The root application's choices apply across its build, invalidate cached tests when changed, and are recorded with performance results. Explicit Linux backends fail instead of silently falling back when unavailable. [#3140] [#3146] - Add GC build options for heap growth and allocation budgets, heap block and
mark-stack sizes, parallel range stealing, thread-local allocation, object
end padding, and moved
GC_reallocstorage. Acton now defaults to range stealing, a larger initial mark stack, immediate thread-local free lists, and collector-reclaimed small realloc copies; each default can be restored to the previous behavior inBuild.act, with runtime environment overrides where supported. [#3212] - Allow Linux and macOS applications to select mimalloc for ordinary C and
native-library allocation with
build_options = {"malloc": "mimalloc"}. The system allocator remains the default, and the option does not replace Boehm GC or change Acton object lifetimes. [#3188] - Allow Linux applications to set
gc_disable_thpinBuild.actto keep GC memory on ordinary pages, including newly allocated and reused heap memory. The updated bundled collector applies the setting without changing the default page policy. [#3142] [#3143] - Add dependency
followsdeclarations inBuild.actso a project can import a package selected through another dependency, while requiring every directly imported package to be declared by the importing project. [#3091] - Limit
acton test --moduleand--namebuilds to selected modules and their imports, including list and watch modes, so focused test runs avoid compiling unrelated test modules and ignore productionlibrariesgroups. [#3086] - Refresh dependency build files even when selected test builds do not compile
sources from that dependency, so switching from a full build to
acton test --moduleor--namedoes not leave stale modules in Zig build plans. [#3097] - Stop obsolete
acton test --watchcompiler and test process groups on cancellation or rebuild completion, carry pending edits into the next generation, and avoid running stale tests after a failed final compile. [#3088] - Report phase timings with
--timingfor build planning, Acton compilation, build preparation, Zig work, and test execution/cache handling, including watch mode and Zig cache summaries. [#3085]
Runtime & Standard Library
- Add compact fixed-size containers for numeric and Boolean workloads:
array[int]andarray[float]store unboxed values,bitarraypacks Boolean values, andbitsetrepresents bounded sets of non-negative integers. The newmatrixmodule provides fixed-size numeric matrices. Rows and columns are mutablevector_viewvalues, and rectangular, transposed, and reshaped views are matrices; all views share storage with the original. [#3202] - Reduce scheduler overhead and prevent lost wakeups when many runtime workers
are active. Workers keep short runs of ready work on the same actor to avoid
repeated queue operations and core migration, while yielding when other
actors wait. They also avoid the global sync-pause mutex when no pause is
requested, wait longer between retries on contended ready-queue spinlocks,
and use matching memory fences to keep newly queued actors from being left
asleep on Arm, including pinned-actor and synchronized-pause wakeups. Ready
queues, worker statistics, pause state, and per-worker actor-ID sequences
occupy separate cache lines so workers no longer contend on frequently
updated shared state. Actor IDs remain unique but are no longer allocated
from one global sequence. Fewer clock reads and direct hardware timer reads
on supported aarch64 and x86_64 CPUs further reduce the cost of short actor
work and improve sub-microsecond runtime timing statistics on macOS. Other
CPUs retain the monotonic clock fallback, and
--rts-verbosereports the worker clock in use. [#3205] [#3206] [#3208] [#3210] [#3214] [#3215] [#3218] [#3219] [#3221] - Read and write ready-queue heads and counts atomically, including where workers check a queue without taking its lock, removing data races that ThreadSanitizer reports at no added cost on x86_64 or aarch64. A queue's count is lowered only when a worker takes an actor, so it no longer drifts below the number of queued actors when workers race for the same actor. [#3232] [#3233]
- Run timed messages on monotonic clocks, so wrong or adjusted wall clocks no longer move pending delays. Linux and macOS use suspend-aware clocks and kernel timers instead of spinning through the last fraction of each delay, cutting CPU use by more than 90% in dense timer workloads. This trades busy-wait precision for normal OS wakeup precision, particularly on macOS; other platforms use libuv's monotonic clock and timer. [#3222] [#3231]
- Flush messages produced by a turn before an actor begins waiting for an awaited result, preventing another worker from resuming the actor concurrently and causing crashes, reordered messages, or incorrect timer baselines. [#3209]
- Use 64-bit lengths for
str,bytes, andbytearray, allowing their core operations to produce values larger than 2 GiB. Large widths, counts, and totals are checked without integer overflow; values beyond the supported limit raiseValueError, failed allocations raiseMemoryError, and bytearrays can grow beyond 1 GiB without looping.bytes()also rejects integers outside 0 through 255 before narrowing them. [#3191] [#3193] - Fix correctness and crash bugs in
strand shared sequence operations, including Unicode-aware tab expansion, empty-substring counting and replacement, separator splitting, negative search and slice bounds, negative replacement counts, quote escaping, large case conversions and slices, and negative-step list slice deletion. Numeric constructors now consume the whole input and consistently raiseValueErrorfor invalid arguments, whilechr()rejects surrogate code points. [#3185] [#3186] - Make concurrent
xml.decode()calls safe when parsing malformed documents and reduce parser pressure on the GC by keeping libxml2 allocations outside the GC heap, using per-parse error state, and parsing input in place. Each failed call now reports its own error and position instead of risking a double-free crash; the bundled libxml2 is updated to 2.15.4. [#3170] [#3175] - Fix correctness and crash bugs across
bytesandbytearray: ASCII-only case conversion and classification, representation quoting, tab expansion, suffix bounds, NUL-aware comparisons, separator splitting, partition results, stepped slice deletion, membership, and mutation range checks. Separator splitting no longer crashes withmaxsplit=0or repeatedly copies the remaining input, and derived bytearrays no longer alias their inputs.bytearray.hex()now returns exactly two characters per byte, and environment byte APIs reject embedded NULs instead of truncating names or values. [#3171] [#3173] [#3174] - Reuse fixed exceptions and their messages across builtin and standard-library
failures, and reuse immutable builtin strings for
None,True,False, and exactstrvalues. Fixed-error benchmarks allocate 15-19% fewer bytes and run 5-7% faster while preserving exception types and diagnostics. [#3182] [#3183] [#3184] - Add explicit C extension APIs for shared versus copied
strandbytesstorage, including length-taking variants that preserve embedded NULs and validate UTF-8. Existing conversion names retain their behavior during the migration. [#3179] - Enable mprotect dirty-page tracking for incremental and generational GC on arm64 macOS by building the bundled collector with its maintained build configuration. Runtime input buffers now stay outside protected GC pages, so process, TCP, and UDP reads remain reliable when this mode is enabled; x86_64 macOS retains the previous fallback. [#3172]
- Hash values from their existing byte representation instead of allocating a
byteswrapper, and hash tuple components with one hasher while preserving component boundaries.hash()also bypasses the stateful hasher allocation forint,u64,str, andbytes. This removes per-value and per-component allocations, making short values about 25% faster, two-component tuples roughly 45-50% faster, and 64 KiB strings almost ten times faster to hash. [#3160] [#3161] [#3216] - Add
acton.rts.get_gc_info()for inspecting the collector's mode, configured and active dirty-tracking backends, marking and collection policy, heap and mark-stack layout, thread-local allocation behavior, and current heap, free, and unmapped byte counts. [#3146] [#3212] - Add allocation-free
time.monotonic_ns()for reading the monotonic clock as an integer nanosecond count in deadline checks and other hot loops. [#3217] - Allocate
bigintlimb storage only when it is needed, and compute%without boxing a discarded quotient anddivmod()tuple, reducing heap work for zero values and remainder-heavy arithmetic. [#3229] - Add
math.ldexp(x, exp)andstd.math.ldexp(x, exp)for computingx * 2**expacross the full Actonintexponent range. [#3190] - Update the bundled collector so unlimited generational collection can use
parallel marking, idle
userfaultfdmonitoring sleeps, write protection handles Linux memory-mapping boundaries, and custom stop callbacks remain installed. Further fixes prevent misseduserfaultfdwrites, heap corruption in newly enabled thread-local and mark-stack modes, and allocation failure when a full heap can recover through collection. [#3144] [#3212] - Include
bytesandcomplexinatom, allowing APIs that accept immutable builtin values to receive them. Unsupportedatominputs to numeric constructors now raiseValueErrorinstead of terminating the process. [#3156] [#3159] - Return exactly two hexadecimal characters per input byte from
bytes.hex(), preventing unrelated memory from being included in the result and corrupting hash strings. [#3133] - Preserve embedded NUL characters in
strandbytesliterals,%formatting and interpolated strings,print(), regular-expression patterns and matches, and when decodingbytesorbytearraytostr. Comparisons, hashing, iteration, prefix and suffix checks, and JSON keys and values use the complete string; decoding rejects invalid UTF-8 or trailing JSON input after a NUL instead of accepting a truncated prefix, andjson.decode()rejects non-object roots. File operations reject paths containing a NUL withValueErrorinstead of operating on the prefix before it. Where libc is available, string comparisons use its optimized memory comparison rather than Zig's bytewise fallback. [#3125] [#3126] [#3128] [#3196] [#3199] [#3200] - Allow list equality and inequality when elements implement only
Eq, while ordering comparisons continue to requireOrd. [#3120] - Hash
bool, narrow integers, andfloatby value, so equal values in separate objects work reliably as set elements and dictionary keys. Positive and negative floating zero now produce the same hash. [#3141] - Add immutable
isetvalues whose hashable elements make the set hashable as a whole, allowing them to be used as dictionary keys and nested inside other sets while mutablesetvalues remain unhashable, and format them asiset({...})instr()andrepr()output. [#3103] [#3109] [#3135] - Return a valid empty string from
str.rstrip()andstr.strip()when they remove every character, including multibyte characters and custom strip sets. [#3138] - Report
re.Match.start_posandend_posand acceptre.match()'sstart_posin character positions rather than UTF-8 byte offsets, keeping regular-expression indexes consistent with Acton string indexing for non-ASCII text. [#3165] - Speed up
==and!=forstr,bytes,bytearray,bigint,list,dict, and tuple values by returning immediately when both operands refer to the same object instead of comparing their contents. This avoids unnecessary work for large values and containers, and means lists, dictionaries, and tuples compare equal to themselves even when they containNaN. [#3106] - Keep
range()empty when its step points away from the stop value instead of yielding an erroneous element. [#3157] - Fix
delandpop()on small dictionaries, which hold at most two entries. Removing a key could corrupt the dictionary's entry count, sostr()showed the dictionary as empty, serialization dropped its entries, and comparisons with the dictionary as the left operand checked only lengths, sod == otherwas true for anyotherof the same length. [#3236] - Use libc's optimized memory fill where available, reducing time and CPU use in GC-heavy workloads while retaining Zig's implementation on platforms that need it. [#3139]
- Implement builtin helpers such as
enumerate,filter,map,max,min,sum, andzipin Acton and streamline collection iteration to avoid allocation-heavy iterator handling in common builtin operations. [#3104] [#3131] - Join
str,bytes, andbytearrayparts fromlistandilistvalues without first materializing an identical temporary list, while preserving support for arbitrary iterables. [#3224] - Drain queued writes before closing server-side TCP connections, preventing
large response payloads from being truncated when
TCPListenConnection.close()followswrite(). [#3110] - Use a stable min-heap for runtime timers, making large batches of
aftercallbacks with increasing or equal deadlines scale logarithmically while preserving FIFO order for equal deadlines. [#3092] - Write each chunk passed to
file.WriteFile.write()at the current position instead of at offset 0, so later writes no longer overwrite earlier ones, and write all of the data when the system writes only part of it. [#3167] - Add
appendtofile.WriteFileto keep the content of a file and write at its end, and addfile.FS.rename()to rename a file or directory. [#3167]
Packages & Distribution
- Use Ubuntu 20.04 for Linux x86_64 CI and release artifacts to preserve glibc 2.31 compatibility after Debian 11 package downloads stopped working. [#3089] [#3096]
- Fix bundled native dependency inputs for ActonDB and TLSuv by removing the backend dependency symlink, resolving backend packages through sibling dependencies, and matching TLSuv's active mbed TLS and keychain source set. [#3093] [#3094]
Documentation
- Document division and remainder semantics for bounded signed and unsigned
integers and
bigint, including quotient rounding and remainder signs for negative operands. [#3114]
Testing & CI
- Expand performance testing into a repeatable workflow for measuring individual benchmarks and how they scale with workload size. [#3105] [#3107] [#3112] [#3113] [#3115] [#3117] [#3118] [#3121] [#3124] [#3127] [#3130] [#3134] [#3139] [#3203] [#3204] [#3207] [#3220] [#3222]
acton test perfcalibrates opt-int.loop()benchmarks within a configurable time budget, warms up and measures fresh invocations, accepts explicit or recorded workload scales, and includes dedicated builtin and collection benchmarks.acton test perf --compareaccepts saved measurements or a Git revision; for example,acton test perf --name my_test --compare git:maincompares a selected benchmark withmain. Git comparisons build both versions, measure interleaved balanced pairs of fresh processes at a shared workload scale, and report uncertainty from the paired results while including uncommitted current-tree changes.utils/perf-comparecompares changes to Acton's compiler, runtime, or builtins against a Git revision using identical benchmark sources and a shared workload scale. It runs balanced process pairs, reuses cached baseline builds, supports selecting another benchmark project, and saves the source snapshot and measurements.- GC benchmarks measure collection under temporary allocations while retaining and updating an inventory of records, supporting comparisons of runtime and collector settings at explicit workload sizes.
- Reports compare recorded baselines and show timing distributions, GC, allocation and memory statistics, peak RSS, and optional CPU, instruction, cycle, and IPC counters, with consistent elapsed-time measurement on macOS.
acton test scalevariest.scale()workloads across configurable start and end bounds, samples until growth stabilizes or resource limits stop the study, and supports three terminal charts for time, time per scale, and allocated bytes with readable binary units, plus journals and replay. Comparisons can use a recorded study or a Git revision, measuring the selected revision and the current working tree, including uncommitted changes, at the same completed workload sizes. Explicit end bounds apply to both revisions independently, and macOS scaling accounts for reclaimable memory before stopping.- Scaling charts default to zero-based linear axes, with
--logavailable for wide ranges; compared curves share axes and show both final values. - Comparison summaries and legends list the baseline before the current run, color each label to match its curve, and keep wrapped headings aligned.
acton test --rts-wthreads Nruns ordinary, performance, scaling, and stress test processes with a fixed worker count, includes it in cache and baseline identities, and enables reproducible scheduler scaling studies. The fleet benchmark measures a representative actor-and-timer workload, while the scheduling suite covers ring and independent-pair handoffs, deep mailboxes, fan-out, pipelines, latency under load, and work queued after replies. A timer-precision suite measures one-shot and periodic firing accuracy and CPU cost.- The cross-runtime
xlangrunner builds and interleaves equivalent Acton, Go, and Tokio scheduling workloads, then produces median summaries and interactive HTML charts for throughput, CPU cost, and latency. - Recorded performance and scaling baselines from different machines now
still show deltas with an explicit warning instead of rejecting the
comparison. Their live integration tests now run with the default
make testsuite and in CI. - Stop running full baseline performance comparisons on every pull request;
use
acton test perf --compareorutils/perf-comparewhen a change needs targeted measurement. [#3145] - Update standard-library tests to the supported testing signatures, restoring discovery and execution of tests that used the legacy callback form. [#3111]
Compatibility Notes
- The buffers backing
bytesandbytearraynow contain exactlynbytesbytes, with no trailing NUL. C extensions must use the length, and code that needs a C string must reject embedded NULs and make a terminated copy. Serialized values remain compatible. [#3180] - Generic code that only reads indexed, sliced, sequential, or mapping values
should use
IIndexed,ISliceable,ISequence, orIMapping.strandbytesimplementISliceable,ilistimplementsISequence, andidictimplementsIMappingrather than the corresponding mutable protocols. [#3152] - Custom iterators must return
just(value)for each item andnothing()at exhaustion. Code that callsnext()or__next__()directly must inspect the returnedmaybevalue. [#3137] - Require predicates passed to
filter()to returnbool; code that returned another truthy value must convert it explicitly. [#3131]