Repository navigation
Releases
d6 1.0.0-rc5
The fifth release candidate fills in everyday collection and String argument
shapes, and makes JIT-compiled code substantially faster on calls,
comparisons and block loops. The cookies cut (0.2.4) was published with these
changes on 2026-11-12; no other bundled cut changed since rc4.
Changed
- More collection and String conveniences: a Hash
each_with_objectblock may take(key, value, memo); Hashvalues_at,fetch_valuesandfirst/first(n);zipover up to six arrays; Arrayslice_whenandintersect?;Int#step(limit, by); and Stringinsert(index, other)andprepend(other). - Everyday collection and String methods accept the argument shapes that used to raise an arity error:
any?/all?/none?/one?without a block (on Array;any?/all?/none?on Hash),each_slice(n)/each_cons(n)with a block,sum(start)(sum(0.0),sum("")),min(n)/max(n),uniqwith a key block,flatten(depth),Hash#mergewith a conflict block,String#chomp(suffix),squeeze(chars),index(needle, start), andFloat#floor(digits)/ceil(digits). New:Array#take_while/drop_whileandHash#delete_if/keep_if. - With the JIT on (
DIAMOND_JIT=1), Arrayselect,each_with_indexandreduceand Inttimesrun their blocks as native loops, aseachandmapalready did: about half the instructions per element foreach_with_index,reduceandtimes, and a quarter fewer forselect. - With the JIT on, compiled code runs much faster on calls and comparisons: fib takes half the cycles, mandelbrot a third less, poly a quarter less, and sieve, trees, nbody and objects 15-20% less. Values are now written as whole 8-byte words and copied in 8-byte halves, so the loads that read them back no longer wait for the stores to retire, and a comparison followed by a branch on its result branches directly.
- With the JIT on, Array
mapandselectpush into their result array inline instead of calling C for every element: about 30% fewer instructions per element (map 156 to 113, select keeping everything 177 to 123, with an empty block). - Session cookies are marked
SecureunderDIAMOND_ENV=production, matching active_auth: the cookies cut'sCookieSession(0.2.4, with asecure:override, and nowPath=/) and the project_board example. Apps behind a TLS proxy such as nginx keep speaking plain HTTP. The guestbook, project_board, chat (wss:) and library examples were checked behind nginx over HTTPS; bookmarks, job_service and task_board are loopback-only single-user tools. - The brand is written D6 everywhere it names the language: guides, READMEs, the docs site, registry pages, the VS Code extension, CLI help text and GitHub release titles. Commands, file names and code stay lowercase (
d6 build,.d6,d6-lang). - A thread that fails and is never joined now prints its uncaught exception and backtrace on stderr (
d6: runtime error in unjoined thread: ...) instead of losing it silently. Exit status is unchanged. - Bundled cut maintenance avoids redundant timestamp formatting in text logging and simplifies database-config and static-file reads, including file cleanup on read errors. Network-safety comments now describe its string-only checks without outdated claims about runtime capabilities.
Diamond 1.0.0-rc4
The fourth release candidate extends rc3's compact disk representation to
runtime metadata. No bundled cut versions changed since rc3.
Changed
- Compiled programs keep string constants and type-set members at their used sizes in memory, avoiding the expansion of compact caches and standalone binaries into mostly unused 4 KB string buffers and eight-member type records. Compiler, template and thread copies retain independent metadata ownership. Existing bytecode caches rebuild once for format 22.
- A reproducible standalone-application soak exercises populated Pheint GraphQL APIs and authenticated Skindicate reads and writes with the JIT at one and four workers. The RC4 candidate used 50–67% less loaded RSS than rc3 in these runs, with successful read-backs and database integrity checks. See the measurement report for results and limits.
Diamond 1.0.0-rc3
The third release candidate reduces cache and executable sizes, fixes native
collection dispatch in JIT-compiled blocks, and expands general runtime
verification. No bundled cut versions changed since rc2.
Changed
- The runtime test suite now checks cross-feature failure recovery and native-service workflows across interpreter, JIT, GC-stress, cached-bytecode and standalone-binary execution.
- Registry deployments build the runtime at
-O3(was-O2), turn on the JIT throughDIAMOND_JIT=1in the env file, andtools/deploy_registry.sh --revision=REFdeploys a release tag instead of HEAD. - Bytecode caches and
d6 buildbinaries store type sets compactly (count, flag and used members only) instead of as fixed 354-byte records: Pheint's cache went from 71 MB to 7.3 MB and its binary from 76 MB to 11 MB. The cache format version is now 21, so existing caches are rebuilt once.
Fixed
- JIT-compiled blocks dispatch methods on captured instance-variable arrays, hashes and strings correctly instead of treating them as user-defined instances. This fixes Pheint's benchmark worker-pool initialization.
- The docs site rendered the "Compatibility links" lists on the syntax and I/O pages as raw Markdown. Its renderer now keeps inline anchors,
<br>,<kbd>,<sup>and<sub>inside paragraphs and list items, and an anchor on a line of its own no longer swallows the list below it.
Diamond 1.0.0-rc2
The second release candidate fixes captured-local retries, JIT error handling
and supervised cancellation, and local type flow around safe calls. No cuts
changed since rc1.
Changed
- Developer documentation reflects retry/capture semantics, JIT error and cancellation contracts, and current CI and test-running procedures.
- Ubuntu 22.04 is no longer a supported deployment target and has been removed from portable CI validation.
Fixed
- Retrying a rescued body preserves access to locals captured by blocks, including captures in rescue and ensure clauses.
- Differential fuzzing actually executes the JIT, with a terminating timeout and a bounded interpreter preflight.
- JIT failures after captured-local writes preserve those writes instead of replaying the closure.
- Supervised workers stop promptly inside JIT Array block loops and native loops calling compiled blocks.
- Safe calls preserve local type facts when nil skips their arguments, and join conditional assignments.
- Release downloads select the exact requested asset when falling back to formatted GitHub API responses.
Diamond 1.0.0-rc1
The first release candidate for d6 1.0. Since 0.29.0: safe navigation (&.), and a faster JIT for
array stores and Int ops with a constant operand (loop and sieve now beat YJIT).
Added
- Safe navigation (
&.).value&.name(args)callsnameunlessvalueis nil; then the
arguments are skipped and the call is nil. Only nil short-circuits, each&.covers one call,
writers (pet&.name = "Max") and block calls work, and a line starting with&.continues a
chain. The result is the method's return type joined withNil, and the receiver is treated as
non-nil inside the call (core syntax). The language
server resolves the receiver left of&.through its non-nil view, sopets["rex"]&.,
pet&.on aPet | Nilandowner&.pet()&.complete, hover and jump to definition; a plain
.after a nullable value stays unresolved. The formatter and the VS Code grammar know&..
Changed
- Portable tarballs withdrawn from releases. Releases no longer attach the static
d6-VERSION-linux-*.tar.gzbuilds introduced in 0.29.0 while their limits (no JIT, baseline
x86-64, nod6 build) are worked out. CI still builds and tests them, and
tools/build_portable.shstill makes them. - Package file names keep the upstream version. A pre-release such as 1.0.0-rc1 is packaged
as version1.0.0~rc1(so the final 1.0.0 upgrades it), but the files are named
d6-lang_1.0.0-rc1-1_amd64.debandd6-lang-1.0.0-rc1-1.fc44.x86_64.rpm. - Faster
array[i] = valuein compiled code. The JIT stores straight into a young, unfrozen,
unconstrained Array when the index is an in-bounds Int, and only falls back to the full
INDEX_SETpath for promoted (write-barrier), frozen, typed or out-of-range cases.
Array.new(n, value)and[value] * nfill without a per-element modulo. sieve: −50%
instructions, −16% cycles. - Cheaper Int ops with a constant operand in compiled code.
i + 1,x * 7,i < 1000
andx == 0check only the variable operand's kind and use the constant as an immediate;
x % kfor a positive constant is a mask (powers of two) or a multiply by the reciprocal
instead of a hardware divide. With the JIT on, loop and sieve now beat YJIT in the local
benchmark suite (loop −24% cycles, fib −5%).
Diamond 0.29.0
Added
- A portable d6 for any Linux. Each release now attaches
d6-VERSION-linux-x86_64.tar.gz
andd6-VERSION-linux-aarch64.tar.gz. They holdd6,facet,d6-lspandd6-dapas
fully static musl executables, with SQLite, PostgreSQL, MySQL/MariaDB (MySQL 8 and MariaDB
authentication plugins included), TLS, zlib and BCrypt linked in. Before each release they
are checked in clean Ubuntu 26.04 and 22.04, Debian 12, Fedora 44, Arch, Alpine and
openSUSE Leap containers. They target baseline x86-64 rather than x86-64-v3, have no JIT
and cannot rund6 build.tools/build_portable.shbuilds them in an Alpine container
(deployment).
Changed
- Self-hosted compiler closer to native.
selfhost/parser.d6now compiles-> Selfreturn
types,attr_reader item: Tin generic classes, generic type arguments on classes inside a
module (Relation[T]), everyFileandDirmethod,Fiber.yield,
TCPServer.listen'shost:/backlog:options with the non-blocking TCP forms, and do-block
contracts on generic functions (generic_collect(21) do [] endbinds T to Int). It also
compiles every runtime builtin call:Time,Thread.new,Channel,Supervisor.new,
Process,IO,SQLite3/MySQL/PostgreSQL, TLS and UDP sockets,DNS,Signal,
Tensor,SemVer, and the crypto and encoding calls, so Active Record runs through it. Run
through it, every one of the 1,587tests/casesprograms now compiles and runs (up from
1,342 of 1,586), including the Callable contracts native infers: do blocks on constructors and
on a union of callables, generic function references (identity) fixed to the type a parameter
expects, andBox.new(41)known as aBox[Int]. ProgramBuildertype sets can sayCallable[[], T]. Adeclare_type_setdescriptor takes
an optional seventhBoolmarking a written parameter list, so a typed zero-parameter Callable
differs fromCallable[0, T]as it does in the native compiler.TimeandTensorvalues cross threads. They can beThread.newarguments and results,
go through aChannel, and be the result ofProgramBuilder#run, copied as plain data.- The JIT compiles functions that call methods on values of unknown type. A method call
whose receiver the JIT could not prove the type of (an untyped parameter, an Array element, a
polymorphic call site) used to keep the whole function interpreted; it now compiles to a call
into the interpreter's own dispatch. Functions that pass a block capturing their locals, and
theMathfunctions, compile too. WithDIAMOND_JIT=1, the counts benchmark runs about 37%
fewer instructions, nbody and poly about 14% fewer, and sort 13% fewer. - Faster recursion under the JIT. A compiled function calling itself now calls its own
machine code directly, with the callee's registers on the native stack, instead of going
back through the VM. WithDIAMOND_JIT=1, fib runs 53% fewer instructions (160 to 121 ms)
and trees 14% fewer. The call-depth limit and its stack-overflow error are unchanged. - Blocks run as machine code under the JIT. The body of a
do ... endblock or closure now
compiles, including the captured variables it reads and writes. Functions that read a
top-level constant compile too. WithDIAMOND_JIT=1, the blocks and iterate benchmarks run
36% fewer instructions (91 to 76 ms and 152 to 116 ms), and regex 5% fewer. Captured
variables are now read and written by inline machine code instead of a call per access, and
a compiled block is entered directly from each/map/times, which takes blocks a
further 20% (70 to 60 ms) and iterate 15% (112 to 95 ms). - Closure calls and string interpolation compile under the JIT. A function that calls a
block or closure it holds, or builds a string with#{...}, no longer stays interpreted. With
DIAMOND_JIT=1, the closures benchmark runs 5.6% fewer cycles and regex 6% fewer. - Functions with
begin/rescue/ensurecompile under the JIT. Exception handlers,
retry,raise, andbreak/next/returnthroughensureno longer keep a function
interpreted, with the same errors and backtraces. WithDIAMOND_JIT=1, the exceptions
benchmark runs 14% fewer cycles. - A memory budget keeps a VM on the interpreter under
DIAMOND_JIT=1.DIAMOND_MAX_MEMORY_BYTES
was not enforced in compiled code: a compiled loop that only grew one array ran past the
budget until the system ran out of memory. Instruction and wall-clock budgets already did
this. - Many more functions compile under the JIT.
!,puts/print, class-method calls
(Name.method(),self.method()), class variables, destructuring assignment, unary minus,
symbols,.class(), and calls that pass keywords, a*spreador explicit type arguments no
longer keep a function interpreted. These run the interpreter's own code for the
instruction, so results and errors are unchanged. Across the test cases, benchmarks and
examples, functions the JIT turned away fell from 3,041 to 1,333. - Generic functions and methods of generic classes compile under the JIT. Each compiled
call binds its type variables on entry, from the arguments, explicit type arguments
(f[Int](https://github.com/diamond-language/diamond/blob/v0.29.0/x),obj.method[Int](https://github.com/diamond-language/diamond/blob/v0.29.0/x)), the receiver or a block's creator, exactly as the
interpreter does, so type checks and errors are unchanged.-> Selfmethods compile too.
Functions the JIT turned away fell from 1,333 to 553. WithDIAMOND_JIT=1, the
typed_dispatch benchmark runs 7% fewer instructions and the new generic_calls 4.6% fewer. - The JIT compiles functions with more than 256 registers. A function that big (a long
hash literal, two large methods the ORM packages share) used to run interpreted; it now
compiles, with its registers on the heap as the interpreter keeps them. Functions the JIT
turns away fell from 553 to 457, and native calls to smaller functions cost the same as before. - The JIT compiles variadic functions and methods.
def f(head, *rest)and
def f(*rest, &block)now compile; the rest Array is built in the compiled code, and their
tail calls to themselves still run in constant stack. A call with more arguments than the
function has registers runs in the interpreter, as does a function whose parameter setup
writes where its rest arguments wait (a default before the rest parameter). Functions the
JIT turns away fell from 457 to 416. - The JIT compiles blocks that
breakorreturn, and the functions they land in. A
function whose block breaks out of the call it was passed to, or returns from the function,
used to run interpreted, and so did the block. Both compile now, ensure clauses still run on
the way out, and a break from a block whose call has finished is still a rescuable error.
About 70 fewer functions are turned away. - The JIT compiles functions that use the builtins. Bitwise and shift operators,
<=>,
to_i/to_f/to_sym,casepatterns, JSON,Regexp.new, encodings, digests, ciphers,
Time,Fiber, sockets, processes, database opens,Thread.new,define_methodand the
rest of the builtin calls no longer send a function back to the interpreter. Across the test
cases, benchmarks and examples the functions the JIT turns away fell from 363 to 17, and
those 17 only stop under the debugger. - Compiled code calls other compiled functions and methods directly. With the JIT on, a
call from compiled code to another compiled function, or to a method of an object whose class
the call site has seen before (up to four classes per site), no longer goes through the
interpreter's call machinery, and getters and setters called that way compile too. Instruction
counts fell 49% on a polymorphicshapes[i].area()loop, 38% on small objects, 28% on an
n-body simulation and 20% on structs; method dispatch benchmarks run 44 to 73% fewer.
Redefining a method withredefine_methodordefine_methodstill takes effect at once. - A block boxes only the locals it reads. Creating a
do ... endblock used to move every
local visible at that point into a heap cell, and every later read of those locals went
through the cell. Now only the ones the block's body names are, so the rest stay plain
registers: an n-body step that runs onebodies.each() do ... endper call does 11% fewer
instructions in the interpreter. - Compiled code handles objects inline. With the JIT on,
Class.newallocates and calls a
compiledinitializedirectly, a type check against a single class,x == nilon an object
whose class has no==, instance variable reads and writes, and calls to plain getters and
setters (attr_accessor,def x() = @x) no longer go through C. Together with the native
calls above, instruction counts fell 63% on an n-body simulation, 61% on small objects, 54%
on polymorphic calls, 49% on binary trees and 41% on structs. - Compiled code reads Array elements inline. With the JIT on,
values[i]for a
non-negative Int index inside the array is a direct load; everything else takes the usual
path. The sieve benchmark runs 15% fewer instructions. - Closures and captured variables are cheaper to create. Swept closures (up to 16 captures)
and capture cells are kept for reuse, like small objects, strings and arrays already were.
A closure-heavy benchmark runs 31% fewer instructions in the interpreter and 47% fewer with
the JIT. - Compiled code runs Array
eachandmaploops itself. With the JIT on, a block passed
toeachormapon an Array is called natively from the loop instead of through C for
every element. A map-heavy benchmark runs 8% fewer instructions and aneach-heavy on...
Diamond 0.28.0
Changed
- Cheaper getter and setter calls. A call site that keeps reaching the same
attr_reader,
attr_writerorattr_accessormethod on the same class now remembers that it is one, and
later calls read or write the field after a class check and a value check, skipping the method
lookup's per-call checks. Typed and untyped receivers both benefit, and errors (a wrong-typed
value, a frozen receiver) are the same as before. The n-body simulation runs about 13% fewer
instructions (8% fewer cycles), building and walking trees 5% fewer cycles, and struct- and
object-heavy code 2-5% fewer. - Cheaper class-typed getters and call results. A getter declared as one class, with or
withoutNil(attr_reader left: Node | Nil), now checks its value with one class comparison
instead of walking its declared type each call; a subclass instance or a wrong-typed value
still gets the full check and the same error. Method, function and==call results are
copied back in a form the processor can forward straight from the callee's stores. The
n-body simulation runs about 10% fewer cycles, object- and struct-heavy code 4-5% fewer, and
building and walking trees about 3% fewer. - Cheaper
newfor simple constructors. Aninitializethat only checks its parameters'
types and copies them into fields (def initialize(x: Int, y: Int) @x = x; @y = y end, and
everystruct's) no longer runs as a method call:newchecks the arguments and fills the
fields directly. Any argument it can't settle at once runsinitializeas before, so errors
and backtraces are unchanged. Struct-heavy code runs about 17% fewer cycles, object
allocation 13% fewer and building binary trees 8% fewer. WithDIAMOND_TRACE_JIT, such
constructors no longer appear among the compiled functions, since they never run. - Cheaper short Strings and Arrays. The collector keeps swept Strings of up to about 100
bytes, sorted by size, and swept Array headers for the next allocations instead of returning
them tomalloc, as it already did for small instances; together the pools stay within one
nursery's worth of memory.Regexp#matchalso collects its group positions on the stack and
builds its result Array at its final size. Text processing runs about 34% fewer cycles, regular
expressions 28%, Hash-heavy code 23%, String building 20% and exception handling 17%. - Faster regular expressions on ASCII text. The matcher steps over an ASCII character and
tests\wagainst it without asking the UTF-8 decoder, which it used to call for every
character. Results are unchanged for all text; mostly-ASCII input matches about 15% faster.
Diamond 0.27.0
Changed
- Cheaper calls. A call no longer copies its arguments into a fresh register block when they
already sit in order, which a single argument always does. A typed function's parameter checks
run as a few inline tests on entry instead of one dispatched instruction each when the types are
simple (Int, Float, Bool, String, Symbol, an exact class, or one of these or Nil); anything else,
such as a subclass instance, still goes through the full check with the same error. And a body
ending in a ternary returns straight from each branch. Recursivefibruns about 11% fewer
instructions and cycles, building and walking binary trees about 9% fewer instructions, and
object-heavy code 2-3% fewer. - Cheaper objects. Reading or writing an instance variable checks the instance's shape directly
instead of going through a per-site cache, a trivial getter or setter is recognized once per
program instead of on every call, and the collector keeps swept small instances (up to eight
fields, at most one nursery's worth) for the next allocations instead of returning them to
malloc. Allocating a million small objects and calling a method on each runs about 16% fewer
cycles; struct-heavy code 15% fewer, building and walking trees 10% and an n-body simulation 18%.
DIAMOND_TRACE_FIELDS, which reported the field cache's hits and misses, is gone. - Cheaper call setup. Each function's call context is built once when a program starts instead
of on every call into it, a warm method call site makes the whole call in one cache lookup, and
a method call's arguments are copied without a library call. A method call costs about 150 fewer
instructions. Closures and object-heavy code run about 8% fewer instructions, struct-heavy code
4%, exceptions 6% and an n-body simulation 4%. The call-site caches are now 8-way, so a program
whose hot call sites happen to share a set no longer evicts one on every pass. Reading an
instance variable no longer makes a function call (polymorphic shape code 4% fewer
instructions), and a body ending in a ternary returns a parameter or local from its true branch
without copying it first (recursivefib3% fewer). - Typed fields. The compiler now tracks Int and Float instance variables. When every write to
a field stores an Int (or every write a Float), the class's owninitializesets the field
before anything else runs, and nothing else can write it (no subclass, no module writing fields
by name, nodefine_methodorcompile_method), reading the field is known to give that type.
Arithmetic on such fields uses the Int fast paths, and a method or generatedattr_reader
that returns one skips its return check. A call to a method defined further down the class now
knows that method's declared Int, Float, String, Bool or Symbol return, as it already did for a
method defined above. Object-heavy code runs about 5% fewer instructions, polymorphic shape code
6% and an n-body simulation 3%.
Fixed
- A program with more than about 245 classes could fail to compile with an error like
expected Target, got Inton a method returning an instance variable of class type: the
field's class was stored as an 8-bit type id.
Diamond 0.26.0
Added
-
A documentation site.
tools/docsite/build.d6(written in d6) turnsdocs/into the site
served at getdiamond.codes/docs: a new beginner-friendly guide indocs/guide/for people who
already know another language, followed by the existing reference pages, with a sidebar,
an on-this-page panel and d6 syntax highlighting. Guide examples have a Run button that runs
them in the browser with the homepage's WebAssembly build, and every# =>claim in the guide
is checked bymake test-docs.make test-docsitebuilds the site and checks every link. -
Plain
d6 build. With no source file,d6 buildbuilds the project's entry file in
the current directory (main.d6, asfacet newgenerates, elseapp.d6or a legacyapp.di) and, unless-osays otherwise, names the binary after the
directory:cd greeter && d6 buildproduces./greeter.d6 build tool.d6still produces
tool.
Changed
-
Fewer regex allocations. Matches use call-local temporary capture storage
for small patterns, and boolean matches skip unused result storage. -
Faster regular expressions. The native regex library is now optimized in
every build, reducing the work and instruction-cache pressure of matching. -
Faster interpreter dispatch. Hot instructions reuse their operand bounds
check to prepare the next opcode, removing a separate end-of-code check. -
Int division rounds down.
/on twoInts now floors (rounds toward negative infinity),
like Ruby and Python and like%already did, instead of truncating toward zero:-7 / 2is
-4(was-3) and7 / -2is-4.(a / b) * b + a % b == anow holds for every sign mix,
arbitrary-precisionInts included. Results with both operands the same sign, or an exact
quotient, are unchanged. -
A leaner interpreter loop. With GCC on x86-64, the release build compiles the interpreter's
dispatch function on its own with flags that free one more CPU register for it, and arithmetic
on an Int constant no longer stalls on reading back the previous instruction's result. Across the
benchmark suite, programs run about 3.5% fewer cycles on average. Calls and loops over
collections gain the most: recursivefiband raising and rescuing exceptions each run about 8%
fewer cycles, and block iteration (each,times,reduce) about 9% fewer. A bare integer
counting loop runs about 7% more. With the JIT off, a call to a plain function also skips a
forwarding layer and two no-op frees: recursivefibruns another 8% fewer instructions, and
closures 5% fewer cycles. Method calls andnewskip the same layer: allocating objects and
calling their methods runs 5% fewer cycles. The interpreter now checks for pending signals and
for a supervisor or thread stop on backward jumps and calls instead of before every
instruction; resource limits and opcode counting still see every instruction. That cuts the
counting loop's cost by 14% (more than winning back the 7% above), a sieve by 19%, Float-heavy
mandelbrotby 14% andfibby 12%; across the suite, about 4% fewer cycles on average. -
Profile-guided release builds. With GCC,
make release(and so the .deb and .rpm packages)
now builds an instrumented binary, runs a few small training programs fromtools/pgo/, and
rebuilds using the profile, which adds well under a minute. Hot code is packed together and cold
code moved out of the way, so the processor's instruction cache stops thrashing: object-heavy code
runs about 14% faster, text processing 15%, tree building 13% and JSON 10%, and the benchmark suite
about 6.6% faster on average; tight integer loops are 1-2% slower.make release PGO=0builds
without it. clang builds are unchanged. -
Faster setters and Float arithmetic. A setter whose whole body stores its argument
(attr_accessor'sx=, or a one-fieldinitialize) is answered by writing the field,
without a call, the way getters already were; and Float-with-Float+ - * /no longer
leaves the interpreter's arithmetic instruction. An n-body simulation runs about 19% fewer
instructions and a Mandelbrot loop about 37% fewer. Both shortcuts also settle class-typed
fields (-> Node | Nil), so a binary-tree walk runs about 21% fewer. A warm call site
answers such a getter or setter before the method-call machinery is entered at all,
another 19% off the n-body simulation and 5-6% off object-heavy loops. -
Faster Array indexing and
Array.new. Reading or writing an Array element at an
in-bounds Int index is answered in the interpreter's own instruction, andArray * Int
(whichArray.new(n, fill)compiles to) fills the result natively instead of pushing one
element at a time: a sieve of Eratosthenes to a million runs about 63% fewer instructions. -
Native
each,each_with_index,reduceandtimes. Likemapandselectbefore
them, Array'seach,each_with_indexandreduceand Int'stimescall their block
from a loop in C instead of the prelude's d6 loop, about 45% fewer instructions per block
call.break,next,return, raises and errors behave as before. -
Native
Hash#fetch. A found key, or a missing key with a fallback, is answered with one
native lookup instead of the prelude'sinclude_key?plus[]: counting into a Hash with
counts[k] = counts.fetch(k, 0) + 1runs about 49% fewer instructions. A missing key with
no fallback raises the sameIndexErroras before. -
sub/gsubwith a String pattern skip the Regexp. When both the pattern and the
replacement are Strings and the replacement has no\escapes, the replace is a plain
substring search instead of compiling a Regexp on every call: a small-string workload
mixinggsubwithupcase,include?andsliceruns about 41% fewer instructions.
Interpolating an Int ("#{n}",to_s,join) no longer goes throughprintf: another
10% off that workload and 16% off a string-building one. Raising an exception builds its
backtrace entries the same way, about 15% off a loop that raises in one call of five.
Diamond 0.25.0
Changed
-
sortis native for Int and String arrays. A blocklesssortof an array whose elements are
all Ints or all Strings now runs in C: building and sorting 50,000 Ints ten times went from 1.26 s
to 81 ms. Anything else (a block, mixed kinds, Floats, big integers, nested arrays) sorts as before. -
The JIT compiles
%and Float arithmetic. A function using%or a Float literal used to
stay in the interpreter; both now compile, and Float-with-Float+ - * /and comparisons run
as native SSE2 code. A tight Int loop with%runs about 6x faster underDIAMOND_JIT=1, and a
Mandelbrot loop about 4x. -
Faster startup and recursive calls. Loading a cached program no longer formats its whole
bytecode while checking it, which halves the work a short script does at startup. A function
calling itself reuses its call context instead of rebuilding it, and underDIAMOND_JIT=1a
compiled function's call to itself skips checks that can't change; recursivefibruns about
19% faster with the JIT and 4% faster without. -
Method calls and untyped arithmetic are cheaper. A method whose body only returns an
instance variable (def x() -> Int = @x) is now answered by reading the field, without setting
up a call; return-type checks, arity, privacy and overrides behave as before. Int-,*and/
on values the compiler can't prove are Ints no longer take the big-integer path. An
object-heavy benchmark went from 457 ms to 339 ms and a block-heavy one from 378 ms to 349 ms. -
Array#mapandArray#selectrun natively. Their loop now runs in C and calls the block
directly, instead of through a d6 loop that paid for its own length, index and push calls on
every element.break,next,returnand exceptions inside the block behave as before, and
so do the error messages for a wrong argument.==and!=on two Ints that the compiler
couldn't prove were Ints no longer take the general comparison path. The block-heavy benchmark
went from 349 ms to 199 ms. -
Cheaper block calls,
push,length,sumandRange#to_a.mapandselectset up
their block's call once rather than once per element, and a block reads or assigns a variable
it captured without first re-checking that the variable is shared. Calls topushand
lengthskip about ten other method-name tests that used to come first. A blocklesssumof
plain Ints adds them in C, andto_aon a bounded Int range fills the array in C. Anything
else (a block, Floats, big integers, an endless range, overflow) works as before. The
block-heavy benchmark went from 208 ms to 123 ms. -
Blocks load only the outer variables they use. A block used to fetch every local variable
of the method around it on every call, whether it used them or not. It now fetches only the
ones its body names, unless it creates another block ordefor callsdebugger(). Debug
sessions still see every variable. The block-heavy benchmark went from 127 ms to 109 ms. -
Cheaper method calls. A warm call site that keeps seeing one class now goes straight to
the method it cached, without first testing the name againsttap,to_s,dupand the other
methods every object answers. A class that defines its owntaporto_sstill gets its own.
A typed parameter is checked without a function call when its value is exactly the declared
type or class. A plain method call (no type arguments, at most seven arguments) also goes
through fewer nested calls before reaching the method body. A loop of a million one-argument
method calls runs about 11% fewer instructions, and the object-heavy benchmark about 9%. -
Faster interpreter loop. Each bytecode handler now jumps straight to the next one through
a table instead of returning to one centralswitch. Arithmetic and comparisons with an Int
literal operand, such asi < 3000000,n - 1ori += 1, compile to a single instruction
instead of a constant load plus the operation, and each Int operator has its own handler
rather than sharing one that tested the opcode again. A typed parameter declaredIntor
Floatis checked without a call. The counting loop benchmark went from 1330 ms to about
720 ms, recursivefibfrom about 500 ms to 340 ms, and the object-heavy one from 376 ms to
about 300 ms. JIT-compiled code is unchanged.
Fixed
- Return annotations naming a later class. When a method's return annotation named a class
declared further down the file, a later caller checking that method's result against the class
failed to compile with "expected R, got Nil". It now compiles, and a real mismatch is still
reported with the right class names.