Import Slovo 1.0.0-beta monorepo

This commit is contained in:
sanjin 2026-05-22 08:38:43 +02:00
commit 19b0454b3f
1205 changed files with 153575 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
# Rust
/target/
compiler/target/
# Build outputs
/out/
/build/
*.ll
!tests/*.ll
*.bc
*.o
*.s
# Benchmark and local runtime artifacts
benchmarks/**/build/
compiler/glagol-std-layout-local-fs-alpha.txt
__pycache__/
*.pyc
# Editor and OS files
.DS_Store
.vscode/
.idea/
*.swp
*.swo

View File

@ -0,0 +1,39 @@
# Slovo Monorepo 1.0.0-beta
Date: 2026-05-22
Status: public monorepo baseline prepared from the paired Slovo design and
Glagol compiler beta repositories.
## Purpose
Make `Hermeticum/slovo` the canonical repository for:
- the Slovo language contract
- Glagol compiler implementation
- source-authored standard library
- runtime, examples, benchmarks, and publication documents
## Layout Contract
- language design docs: `docs/language/`
- compiler docs: `docs/compiler/`
- whitepapers and generated PDFs: `docs/papers/`
- standard library source: `lib/std/`
- compiler source and tests: `compiler/`
- hosted runtime: `runtime/`
- examples and benchmark harnesses: `examples/` and `benchmarks/`
## Beta Boundary
`1.0.0-beta` is a real general-purpose beta, not a stable release. It supports
ordinary local command-line tools and libraries inside the documented beta
surface. It does not claim stable ABI/layout, generic collections, remote
registries, networking/async, LSP/watch/debug-adapter support, or frozen stable
standard-library APIs.
## Publication Rule
The public repository must not contain machine-local paths, private checkout
names, or generated PDFs that drift after regeneration. The release gate must
fail if tracked PDFs change after `scripts/render-doc-pdfs.sh`.

23
.llm/ROADMAP_TO_STABLE.md Normal file
View File

@ -0,0 +1,23 @@
# Roadmap From 1.0.0-beta To Stable
## Stable Criteria
Slovo reaches stable `1.0.0` only after:
- compatibility rules are documented and exercised across multiple beta releases
- formatter and diagnostics schemas are frozen for promoted features
- `lib/std` has clear API stability tiers
- package/workspace behavior has deterministic version and dependency policy
- conformance tests cover normal user projects, not only narrow fixtures
- benchmark publication remains repeatable without becoming a performance claim
## Recommended Sequence
1. Harden monorepo release tooling, clean docs, and public install/build flow.
2. Stabilize `lib/std` module boundaries and document beta-vs-stable APIs.
3. Broaden numeric completeness with explicit `f32` and additional integer
policy where needed.
4. Improve collection and string breadth without exposing unstable ABI details.
5. Expand package/workspace discipline before remote registry work.
6. Add editor-facing diagnostics and generated documentation improvements.
7. Freeze migration/deprecation policy and cut stable only after beta feedback.

31
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,31 @@
# Contributing
Slovo is currently in `1.0.0-beta`. Changes should preserve the beta support
boundary: a feature is supported only when the language docs, compiler,
formatter, diagnostics, examples, tests, and release notes agree.
## Local Checks
From the repository root:
```bash
cargo fmt --manifest-path compiler/Cargo.toml --check
cargo test --manifest-path compiler/Cargo.toml
./scripts/release-gate.sh
```
`./scripts/release-gate.sh` regenerates publication PDFs and fails if tracked
PDFs drift after regeneration.
## Standard Library
Source-authored standard-library modules live in `lib/std`. Keep imports
explicit and keep compiler/runtime support aligned with the promoted language
contract.
## Release Discipline
- `exp-*` labels are historical experimental milestones.
- `1.0.0-beta` is the first real general-purpose beta.
- `1.0.0` should wait for compatibility hardening, conformance coverage, and
stable standard-library policy.

10
LICENSE Normal file
View File

@ -0,0 +1,10 @@
Slovo is distributed under the terms of both the MIT license and the Apache
License (Version 2.0).
Copyright (c) 2026 Sanjin Gumbarevic.
You may use this project under either license, at your option.
SPDX-License-Identifier: MIT OR Apache-2.0
See LICENSE-MIT and LICENSE-APACHE for full license text.

186
LICENSE-APACHE Normal file
View File

@ -0,0 +1,186 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and
distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the
copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other
entities that control, are controlled by, or are under common control with
that entity. For the purposes of this definition, "control" means (i) the
power, direct or indirect, to cause the direction or management of such
entity, whether by contract or otherwise, or (ii) ownership of fifty percent
(50%) or more of the outstanding shares, or (iii) beneficial ownership of
such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising
permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation source, and
configuration files.
"Object" form shall mean any form resulting from mechanical transformation or
translation of a Source form, including but not limited to compiled object
code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form,
made available under the License, as indicated by a copyright notice that is
included in or attached to the work (an example is provided in the Appendix
below).
"Derivative Works" shall mean any work, whether in Source or Object form,
that is based on (or derived from) the Work and for which the editorial
revisions, annotations, elaborations, or other modifications represent, as a
whole, an original work of authorship. For the purposes of this License,
Derivative Works shall not include works that remain separable from, or
merely link (or bind by name) to the interfaces of, the Work and Derivative
Works thereof.
"Contribution" shall mean any work of authorship, including the original
version of the Work and any modifications or additions to that Work or
Derivative Works thereof, that is intentionally submitted to Licensor for
inclusion in the Work by the copyright owner or by an individual or Legal
Entity authorized to submit on behalf of the copyright owner. For the
purposes of this definition, "submitted" means any form of electronic, verbal,
or written communication sent to the Licensor or its representatives,
including but not limited to communication on electronic mailing lists, source
code control systems, and issue tracking systems that are managed by, or on
behalf of, the Licensor for the purpose of discussing and improving the Work,
but excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on
behalf of whom a Contribution has been received by Licensor and subsequently
incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable copyright license to
reproduce, prepare Derivative Works of, publicly display, publicly perform,
sublicense, and distribute the Work and such Derivative Works in Source or
Object form.
3. Grant of Patent License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide,
non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this
section) patent license to make, have made, use, offer to sell, sell, import,
and otherwise transfer the Work, where such license applies only to those
patent claims licensable by such Contributor that are necessarily infringed by
their Contribution(s) alone or by combination of their Contribution(s) with
the Work to which such Contribution(s) was submitted. If You institute patent
litigation against any entity (including a cross-claim or counterclaim in a
lawsuit) alleging that the Work or a Contribution incorporated within the Work
constitutes direct or contributory patent infringement, then any patent
licenses granted to You under this License for that Work shall terminate as
of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and in
Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy
of this License; and
(b) You must cause any modified files to carry prominent notices stating that
You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You
distribute, all copyright, patent, trademark, and attribution notices from the
Source form of the Work, excluding those notices that do not pertain to any
part of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution,
then any Derivative Works that You distribute must include a readable copy of
the attribution notices contained within such NOTICE file, excluding those
notices that do not pertain to any part of the Derivative Works, in at least
one of the following places: within a NOTICE text file distributed as part of
the Derivative Works; within the Source form or documentation, if provided
along with the Derivative Works; or, within a display generated by the
Derivative Works, if and wherever such third-party notices normally appear.
The contents of the NOTICE file are for informational purposes only and do not
modify the License. You may add Your own attribution notices within
Derivative Works that You distribute, alongside or as an addendum to the
NOTICE text from the Work, provided that such additional attribution notices
cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction, or
distribution of Your modifications, or for any such Derivative Works as a
whole, provided Your use, reproduction, and distribution of the Work otherwise
complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without any
additional terms or conditions. Notwithstanding the above, nothing herein
shall supersede or modify the terms of any separate license agreement you may
have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as
required for reasonable and customary use in describing the origin of the Work
and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in
writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any risks
associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in
tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to in
writing, shall any Contributor be liable to You for damages, including any
direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability
to use the Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all other
commercial damages or losses), even if such Contributor has been advised of
the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work
or Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such
obligations, You may act only on Your own behalf and on Your sole
responsibility, not on behalf of any other Contributor, and only if You agree
to indemnify, defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason of your
accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own
identifying information. Do not include the brackets. The text should be
enclosed in the appropriate comment syntax for the file format. We also
recommend that a file or class name and description of purpose be included on
the same "printed page" as the copyright notice for easier identification
within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

21
LICENSE-MIT Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 Sanjin Gumbarevic
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

88
README.md Normal file
View File

@ -0,0 +1,88 @@
# Slovo
Slovo (ⰔⰎⰑⰂⰑ) is a typed structural programming language and toolchain.
This repository is the canonical public monorepo for the language design,
standard library source, compiler, runtime, examples, benchmarks, and technical
documents.
Current release: `1.0.0-beta`.
## Repository Layout
```text
compiler/ Glagol, the first Slovo compiler
runtime/ C runtime used by hosted native builds
lib/std/ source-authored Slovo standard-library facades
examples/ compiler-supported Slovo examples and projects
benchmarks/ local benchmark comparison harnesses
docs/language/ language manifest, specs, roadmap, and release notes
docs/compiler/ compiler manifest, roadmap, and release notes
docs/papers/ whitepapers and generated publication PDFs
scripts/ local release and document tooling
```
## Beta Scope
`1.0.0-beta` supports practical local command-line programs and libraries with:
- modules, explicit imports, packages, and local workspaces
- `new`, `check`, `fmt`, `test`, `doc`, and `build`
- `i32`, `i64`, `u32`, `u64`, `f64`, `bool`, `string`, and internal `unit`
- structs, enums, fixed arrays, concrete vectors, option/result families, and
current `match`
- explicit `std/*.slo` imports from `lib/std`, installed `share/slovo/std`, or
`SLOVO_STD_PATH`
- hosted native builds through LLVM IR, Clang, and `runtime/runtime.c`
Still deferred before stable: generics, maps/sets, broad package registry
semantics, networking/async, LSP/watch/debug-adapter guarantees, stable ABI and
layout, and a stable standard-library compatibility freeze.
## Build And Test
```bash
cargo test --manifest-path compiler/Cargo.toml
```
Run the full local release gate:
```bash
./scripts/release-gate.sh
```
Build the compiler binary:
```bash
cargo build --manifest-path compiler/Cargo.toml
./compiler/target/debug/glagol --version
```
Create and check a project:
```bash
./compiler/target/debug/glagol new hello
SLOVO_STD_PATH="$PWD/lib/std" ./compiler/target/debug/glagol check hello
SLOVO_STD_PATH="$PWD/lib/std" ./compiler/target/debug/glagol test hello
```
Build a native executable when Clang is available:
```bash
SLOVO_STD_PATH="$PWD/lib/std" ./compiler/target/debug/glagol build hello -o hello/bin
```
## Documentation
- [Language Manifest](docs/language/MANIFEST.md)
- [Language Specification](docs/language/SPEC-v1.md)
- [Compiler Manifest](docs/compiler/GLAGOL_COMPILER_MANIFEST.md)
- [Slovo Whitepaper](docs/papers/SLOVO_WHITEPAPER.md)
- [Glagol Whitepaper](docs/papers/GLAGOL_WHITEPAPER.md)
Generated PDFs live beside their Markdown sources in `docs/papers/`.
## License
Slovo is licensed under either the MIT License or the Apache License, Version
2.0, at your option.

View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,72 @@
# Array Index Loop Benchmark Scaffold
Release: `exp-119`.
This benchmark compares immutable fixed-array indexing plus scalar accumulation
across Slovo, C, Rust, Python, Clojure, and Common Lisp/SBCL on the same machine.
It is not a published benchmark result, performance threshold, optimizer
claim, or cross-machine comparison.
## Files
- `src/main.slo`: Slovo project benchmark fixture
- `c/array_index_loop.c`: C comparison implementation
- `rust/array_index_loop.rs`: Rust comparison implementation
- `python/array_index_loop.py`: Python comparison implementation
- `clojure/array_index_loop.clj`: Clojure comparison implementation
- `common-lisp/array_index_loop.lisp`: Common Lisp/SBCL comparison implementation
- `run.py`: build/run/timing harness
All implementations print checksum `3875007` for loop count `1000000`.
Hot-loop mode uses loop count `10000000` and checksum `38750007`. The runner
supplies the loop count at runtime so native compilers cannot fold the loop
into a constant answer.
## Commands
Run from the Glagol repository root:
```bash
python3 benchmarks/array-index-loop/run.py --list
python3 benchmarks/array-index-loop/run.py --dry-run
python3 benchmarks/array-index-loop/run.py --only python --repeats 3 --warmups 1
python3 benchmarks/array-index-loop/run.py --mode hot-loop --only slovo --only c --only rust
```
To include Slovo, build or point at a Glagol binary and make sure host Clang is
available:
```bash
cargo build --manifest-path compiler/Cargo.toml --bin glagol
python3 benchmarks/array-index-loop/run.py --glagol compiler/target/debug/glagol
```
The runner skips missing C/Rust/Slovo/Clojure/SBCL toolchains where possible.
Use `--only` multiple times to select implementations:
```bash
python3 benchmarks/array-index-loop/run.py --only slovo --only c --only rust --only clojure --only common_lisp
```
Clojure is detected with `clojure` on PATH, `CLOJURE`, or `CLOJURE_JAR`.
Common Lisp is detected with `sbcl` on PATH, `SBCL`, or `--sbcl`.
## Comparison Method
- The runner builds each implementation once before timing. The reported
numbers measure execution only, not compile time.
- Slovo timings use `glagol build`, which currently lowers to LLVM and then
invokes host `clang -O2` with `runtime/runtime.c`.
- C timings use `clang -O2 -std=c11`.
- Rust timings use `rustc -C opt-level=3 -C debuginfo=0`.
- The measured loop keeps one fixed immutable `i32` array in local scope,
indexes it with a loop-carried `% 8` position, and accumulates the selected
value into an `i32` checksum.
Timing is cold-process local-machine evidence only. Clojure timings include
JVM and Clojure startup, while Common Lisp timings include SBCL script
startup.
Hot-loop mode is startup-amortized local evidence. It runs a larger loop count
and reports total time plus normalized time for the base `1000000` loop count.

View File

@ -0,0 +1,10 @@
{
"benchmark": "array-index-loop",
"source_stem": "array_index_loop",
"loop_count": 1000000,
"expected_checksum": "3875007",
"stdin": "1000000\n",
"hot_loop_count": 10000000,
"hot_expected_checksum": "38750007",
"hot_stdin": "10000000\n"
}

View File

@ -0,0 +1,33 @@
#include <stdint.h>
#include <stdio.h>
#define LOOP_COUNT 1000000
#define EXPECTED_CHECKSUM 3875007
static int32_t configured_loop_count(void) {
int32_t value = LOOP_COUNT;
if (scanf("%d", &value) != 1 || value <= 0) {
return LOOP_COUNT;
}
return value;
}
static int32_t array_index_loop(int32_t limit) {
static const int32_t digits[8] = {3, 1, 4, 1, 5, 9, 2, 6};
int32_t i = 0;
int32_t acc = 7;
while (i < limit) {
acc = acc + digits[i % 8];
acc = acc > 1000000000 ? acc - 1000000000 : acc;
i = i + 1;
}
return acc;
}
int main(void) {
int32_t result = array_index_loop(configured_loop_count());
printf("%d\n", result);
return result == EXPECTED_CHECKSUM ? 0 : 1;
}

View File

@ -0,0 +1,29 @@
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(def loop-count 1000000)
(def expected-checksum 3875007)
(def digits [3 1 4 1 5 9 2 6])
(defn configured-loop-count []
(try
(let [line (read-line)
value (Integer/parseInt (.trim ^String line))]
(if (pos? value) value loop-count))
(catch Exception _
loop-count)))
(defn array-index-loop [limit]
(loop [i 0
acc 7]
(if (< i limit)
(let [next (+ acc (nth digits (rem i 8)))
bounded (if (> next 1000000000)
(- next 1000000000)
next)]
(recur (inc i) bounded))
acc)))
(let [result (array-index-loop (configured-loop-count))]
(println result)
(System/exit (if (= result expected-checksum) 0 1)))

View File

@ -0,0 +1,34 @@
(declaim (optimize (speed 3) (safety 0) (debug 0)))
(defconstant +loop-count+ 1000000)
(defconstant +expected-checksum+ 3875007)
(defparameter +digits+ #(3 1 4 1 5 9 2 6))
(declaim (ftype (function () fixnum) configured-loop-count))
(defun configured-loop-count ()
(handler-case
(let ((line (read-line *standard-input* nil nil)))
(if line
(let ((value (parse-integer line :junk-allowed t)))
(if (> value 0) value +loop-count+))
+loop-count+))
(error () +loop-count+)))
(declaim (ftype (function (fixnum) fixnum) array-index-loop))
(defun array-index-loop (limit)
(declare (type fixnum limit))
(loop with i of-type fixnum = 0
with acc of-type fixnum = 7
while (< i limit)
do (let* ((next (+ acc (svref +digits+ (rem i 8))))
(bounded (if (> next 1000000000)
(- next 1000000000)
next)))
(declare (type fixnum next bounded))
(setf acc bounded
i (+ i 1)))
finally (return acc)))
(let ((result (array-index-loop (configured-loop-count))))
(format t "~D~%" result)
(sb-ext:exit :code (if (= result +expected-checksum+) 0 1)))

View File

@ -0,0 +1,34 @@
LOOP_COUNT = 1_000_000
EXPECTED_CHECKSUM = 3_875_007
DIGITS = [3, 1, 4, 1, 5, 9, 2, 6]
def configured_loop_count() -> int:
try:
value = int(input().strip())
except (EOFError, ValueError):
return LOOP_COUNT
return value if value > 0 else LOOP_COUNT
def array_index_loop(limit: int) -> int:
i = 0
acc = 7
while i < limit:
acc += DIGITS[i % 8]
acc = acc - 1_000_000_000 if acc > 1_000_000_000 else acc
i += 1
return acc
def main() -> int:
result = array_index_loop(configured_loop_count())
print(result)
return 0 if result == EXPECTED_CHECKSUM else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Run the local array-index-loop benchmark scaffold."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from runner import main
if __name__ == "__main__":
raise SystemExit(main(Path(__file__).resolve().parent, sys.argv[1:]))

View File

@ -0,0 +1,40 @@
const LOOP_COUNT: i32 = 1_000_000;
const EXPECTED_CHECKSUM: i32 = 3_875_007;
const DIGITS: [i32; 8] = [3, 1, 4, 1, 5, 9, 2, 6];
fn configured_loop_count() -> i32 {
let mut input = String::new();
if std::io::stdin().read_line(&mut input).is_err() {
return LOOP_COUNT;
}
input
.trim()
.parse::<i32>()
.ok()
.filter(|value| *value > 0)
.unwrap_or(LOOP_COUNT)
}
fn array_index_loop(limit: i32) -> i32 {
let mut i = 0;
let mut acc = 7;
while i < limit {
acc += DIGITS[(i % 8) as usize];
acc = if acc > 1_000_000_000 {
acc - 1_000_000_000
} else {
acc
};
i += 1;
}
acc
}
fn main() {
let result = array_index_loop(configured_loop_count());
println!("{}", result);
std::process::exit(if result == EXPECTED_CHECKSUM { 0 } else { 1 });
}

View File

@ -0,0 +1,4 @@
[project]
name = "array-index-loop"
source_root = "src"
entry = "main"

View File

@ -0,0 +1,60 @@
; Benchmark scaffold fixture for local-machine array-index timing comparisons only.
; Keep LOOP_COUNT and EXPECTED_CHECKSUM aligned with the C/Rust/Python fixtures.
; The runner supplies the loop count through stdin or argv so native compilers
; cannot fold the benchmark loop into a constant answer.
(module main)
(fn loop_count () -> i32
1000000)
(fn expected_checksum () -> i32
3875007)
(fn parse_stdin_loop_count () -> (result i32 i32)
(let input (result string i32) (std.io.read_stdin_result))
(match input
((ok text)
(std.string.parse_i32_result text))
((err code)
(err i32 i32 code))))
(fn parse_arg_loop_count () -> (result i32 i32)
(std.string.parse_i32_result (std.process.arg 1)))
(fn configured_stdin_loop_count () -> i32
(let parsed_stdin (result i32 i32) (parse_stdin_loop_count))
(if (is_ok parsed_stdin)
(unwrap_ok parsed_stdin)
(loop_count)))
(fn configured_loop_count () -> i32
(let parsed_arg (result i32 i32) (parse_arg_loop_count))
(if (is_ok parsed_arg)
(unwrap_ok parsed_arg)
(configured_stdin_loop_count)))
(fn values () -> (array i32 8)
(array i32 3 1 4 1 5 9 2 6))
(fn array_index_loop ((limit i32)) -> i32
(let digits (array i32 8) (values))
(var i i32 0)
(var acc i32 7)
(while (< i limit)
(set acc (+ acc (index digits (% i 8))))
(set acc (if (> acc 1000000000)
(- acc 1000000000)
acc))
(set i (+ i 1)))
acc)
(fn main () -> i32
(let result i32 (array_index_loop (configured_loop_count)))
(std.io.print_i32 result)
(if (= result (expected_checksum))
0
1))
(test "array index loop checksum is deterministic"
(= (array_index_loop (loop_count)) (expected_checksum)))

View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,73 @@
# Array Struct Field Loop Benchmark Scaffold
Release: `exp-122`.
This benchmark compares immutable fixed-array struct-field access plus scalar
accumulation across Slovo, C, Rust, Python, Clojure, and Common Lisp/SBCL on
the same machine.
It is not a published benchmark result, performance threshold, optimizer
claim, or cross-machine comparison.
## Files
- `src/main.slo`: Slovo project benchmark fixture
- `c/array_struct_field_loop.c`: C comparison implementation
- `rust/array_struct_field_loop.rs`: Rust comparison implementation
- `python/array_struct_field_loop.py`: Python comparison implementation
- `clojure/array_struct_field_loop.clj`: Clojure comparison implementation
- `common-lisp/array_struct_field_loop.lisp`: Common Lisp/SBCL comparison implementation
- `run.py`: build/run/timing harness
All implementations print checksum `3875011` for loop count `1000000`.
Hot-loop mode uses loop count `10000000` and checksum `38750011`. The runner
supplies the loop count at runtime so native compilers cannot fold the loop
into a constant answer.
## Commands
Run from the Glagol repository root:
```bash
python3 benchmarks/array-struct-field-loop/run.py --list
python3 benchmarks/array-struct-field-loop/run.py --dry-run
python3 benchmarks/array-struct-field-loop/run.py --only python --repeats 3 --warmups 1
python3 benchmarks/array-struct-field-loop/run.py --mode hot-loop --only slovo --only c --only rust
```
To include Slovo, build or point at a Glagol binary and make sure host Clang is
available:
```bash
cargo build --manifest-path compiler/Cargo.toml --bin glagol
python3 benchmarks/array-struct-field-loop/run.py --glagol compiler/target/debug/glagol
```
The runner skips missing C/Rust/Slovo/Clojure/SBCL toolchains where possible.
Use `--only` multiple times to select implementations:
```bash
python3 benchmarks/array-struct-field-loop/run.py --only slovo --only c --only rust --only clojure --only common_lisp
```
Clojure is detected with `clojure` on PATH, `CLOJURE`, or `CLOJURE_JAR`.
Common Lisp is detected with `sbcl` on PATH, `SBCL`, or `--sbcl`.
## Comparison Method
- The runner builds each implementation once before timing. The reported
numbers measure execution only, not compile time.
- Slovo timings use `glagol build`, which currently lowers to LLVM and then
invokes host `clang -O2` with `runtime/runtime.c`.
- C timings use `clang -O2 -std=c11`.
- Rust timings use `rustc -C opt-level=3 -C debuginfo=0`.
- The measured loop keeps one immutable struct value in local scope, indexes
the struct's fixed `i32` array field with a loop-carried `% 8` position, and
accumulates the selected value into an `i32` checksum.
Timing is cold-process local-machine evidence only. Clojure timings include
JVM and Clojure startup, while Common Lisp timings include SBCL script
startup.
Hot-loop mode is startup-amortized local evidence. It runs a larger loop count
and reports total time plus normalized time for the base `1000000` loop count.

View File

@ -0,0 +1,10 @@
{
"benchmark": "array-struct-field-loop",
"source_stem": "array_struct_field_loop",
"loop_count": 1000000,
"expected_checksum": "3875011",
"stdin": "1000000\n",
"hot_loop_count": 10000000,
"hot_expected_checksum": "38750011",
"hot_stdin": "10000000\n"
}

View File

@ -0,0 +1,41 @@
#include <stdint.h>
#include <stdio.h>
#define LOOP_COUNT 1000000
#define EXPECTED_CHECKSUM 3875011
struct array_record {
int32_t digits[8];
};
static int32_t configured_loop_count(void) {
int32_t value = LOOP_COUNT;
if (scanf("%d", &value) != 1 || value <= 0) {
return LOOP_COUNT;
}
return value;
}
static struct array_record make_record(void) {
return (struct array_record){.digits = {3, 1, 4, 1, 5, 9, 2, 6}};
}
static int32_t array_struct_field_loop(int32_t limit) {
struct array_record record = make_record();
int32_t i = 0;
int32_t acc = 11;
while (i < limit) {
acc = acc + record.digits[i % 8];
acc = acc > 1000000000 ? acc - 1000000000 : acc;
i = i + 1;
}
return acc;
}
int main(void) {
int32_t result = array_struct_field_loop(configured_loop_count());
printf("%d\n", result);
return result == EXPECTED_CHECKSUM ? 0 : 1;
}

View File

@ -0,0 +1,29 @@
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(def loop-count 1000000)
(def expected-checksum 3875011)
(def record {:digits [3 1 4 1 5 9 2 6]})
(defn configured-loop-count []
(try
(let [line (read-line)
value (Integer/parseInt (.trim ^String line))]
(if (pos? value) value loop-count))
(catch Exception _
loop-count)))
(defn array-struct-field-loop [limit]
(loop [i 0
acc 11]
(if (< i limit)
(let [next (+ acc (nth (:digits record) (rem i 8)))
bounded (if (> next 1000000000)
(- next 1000000000)
next)]
(recur (inc i) bounded))
acc)))
(let [result (array-struct-field-loop (configured-loop-count))]
(println result)
(System/exit (if (= result expected-checksum) 0 1)))

View File

@ -0,0 +1,39 @@
(declaim (optimize (speed 3) (safety 0) (debug 0)))
(defconstant +loop-count+ 1000000)
(defconstant +expected-checksum+ 3875011)
(defstruct array-record
(digits #(3 1 4 1 5 9 2 6) :type simple-vector))
(declaim (ftype (function () fixnum) configured-loop-count))
(defun configured-loop-count ()
(handler-case
(let ((line (read-line *standard-input* nil nil)))
(if line
(let ((value (parse-integer line :junk-allowed t)))
(if (> value 0) value +loop-count+))
+loop-count+))
(error () +loop-count+)))
(declaim (ftype (function (fixnum) fixnum) array-struct-field-loop))
(defun array-struct-field-loop (limit)
(declare (type fixnum limit))
(let ((record (make-array-record)))
(loop with i of-type fixnum = 0
with acc of-type fixnum = 11
while (< i limit)
do (let* ((digits (array-record-digits record))
(next (+ acc (svref digits (rem i 8))))
(bounded (if (> next 1000000000)
(- next 1000000000)
next)))
(declare (type simple-vector digits)
(type fixnum next bounded))
(setf acc bounded
i (+ i 1)))
finally (return acc))))
(let ((result (array-struct-field-loop (configured-loop-count))))
(format t "~D~%" result)
(sb-ext:exit :code (if (= result +expected-checksum+) 0 1)))

View File

@ -0,0 +1,38 @@
LOOP_COUNT = 1_000_000
EXPECTED_CHECKSUM = 3_875_011
def configured_loop_count() -> int:
try:
value = int(input().strip())
except (EOFError, ValueError):
return LOOP_COUNT
return value if value > 0 else LOOP_COUNT
def make_record() -> dict[str, list[int]]:
return {"digits": [3, 1, 4, 1, 5, 9, 2, 6]}
def array_struct_field_loop(limit: int) -> int:
record = make_record()
i = 0
acc = 11
while i < limit:
acc += record["digits"][i % 8]
acc = acc - 1_000_000_000 if acc > 1_000_000_000 else acc
i += 1
return acc
def main() -> int:
result = array_struct_field_loop(configured_loop_count())
print(result)
return 0 if result == EXPECTED_CHECKSUM else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Run the local array-struct-field-loop benchmark scaffold."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from runner import main
if __name__ == "__main__":
raise SystemExit(main(Path(__file__).resolve().parent, sys.argv[1:]))

View File

@ -0,0 +1,50 @@
const LOOP_COUNT: i32 = 1_000_000;
const EXPECTED_CHECKSUM: i32 = 3_875_011;
struct ArrayRecord {
digits: [i32; 8],
}
fn configured_loop_count() -> i32 {
let mut input = String::new();
if std::io::stdin().read_line(&mut input).is_err() {
return LOOP_COUNT;
}
input
.trim()
.parse::<i32>()
.ok()
.filter(|value| *value > 0)
.unwrap_or(LOOP_COUNT)
}
fn make_record() -> ArrayRecord {
ArrayRecord {
digits: [3, 1, 4, 1, 5, 9, 2, 6],
}
}
fn array_struct_field_loop(limit: i32) -> i32 {
let record = make_record();
let mut i = 0;
let mut acc = 11;
while i < limit {
acc += record.digits[(i % 8) as usize];
acc = if acc > 1_000_000_000 {
acc - 1_000_000_000
} else {
acc
};
i += 1;
}
acc
}
fn main() {
let result = array_struct_field_loop(configured_loop_count());
println!("{}", result);
std::process::exit(if result == EXPECTED_CHECKSUM { 0 } else { 1 });
}

View File

@ -0,0 +1,4 @@
[project]
name = "array-struct-field-loop"
source_root = "src"
entry = "main"

View File

@ -0,0 +1,71 @@
; Benchmark scaffold fixture for local-machine array-struct-field timing
; comparisons only. Keep LOOP_COUNT and EXPECTED_CHECKSUM aligned with the
; C/Rust/Python fixtures. The runner supplies the loop count through stdin or
; argv so native compilers cannot fold the benchmark loop into a constant
; answer.
(module main)
(struct ArrayRecord
(digits (array i32 8))
(wides (array i64 2))
(ratios (array f64 3))
(flags (array bool 3))
(words (array string 3)))
(fn loop_count () -> i32
1000000)
(fn expected_checksum () -> i32
3875011)
(fn parse_stdin_loop_count () -> (result i32 i32)
(let input (result string i32) (std.io.read_stdin_result))
(match input
((ok text)
(std.string.parse_i32_result text))
((err code)
(err i32 i32 code))))
(fn parse_arg_loop_count () -> (result i32 i32)
(std.string.parse_i32_result (std.process.arg 1)))
(fn configured_stdin_loop_count () -> i32
(let parsed_stdin (result i32 i32) (parse_stdin_loop_count))
(if (is_ok parsed_stdin)
(unwrap_ok parsed_stdin)
(loop_count)))
(fn configured_loop_count () -> i32
(let parsed_arg (result i32 i32) (parse_arg_loop_count))
(if (is_ok parsed_arg)
(unwrap_ok parsed_arg)
(configured_stdin_loop_count)))
(fn make_record () -> ArrayRecord
(ArrayRecord (digits (array i32 3 1 4 1 5 9 2 6)) (wides (array i64 40i64 41i64)) (ratios (array f64 1.5 2.5 3.5)) (flags (array bool false true true)) (words (array string "sun" "moon" "star"))))
(fn int_at ((record ArrayRecord) (i i32)) -> i32
(index (. record digits) i))
(fn array_struct_field_loop ((limit i32)) -> i32
(let record ArrayRecord (make_record))
(var i i32 0)
(var acc i32 11)
(while (< i limit)
(set acc (+ acc (int_at record (% i 8))))
(set acc (if (> acc 1000000000)
(- acc 1000000000)
acc))
(set i (+ i 1)))
acc)
(fn main () -> i32
(let result i32 (array_struct_field_loop (configured_loop_count)))
(std.io.print_i32 result)
(if (= result (expected_checksum))
0
1))
(test "array struct field loop checksum is deterministic"
(= (array_struct_field_loop (loop_count)) (expected_checksum)))

1
benchmarks/branch-loop/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,30 @@
# Branch Loop Benchmark Scaffold
Release: `exp-40`; Common Lisp/SBCL comparison added by `exp-41`; hot-loop
mode added by `exp-42`.
This benchmark compares a deterministic branch-heavy integer loop across
Slovo, C, Rust, Python, Clojure, and Common Lisp/SBCL on the same machine.
It is not a published benchmark result, performance threshold, optimizer
claim, or cross-machine comparison.
All implementations print checksum `1185071` for loop count `1000000`.
Hot-loop mode uses loop count `10000000` and checksum `220775`. The runner
supplies the loop count at runtime.
## Comparison Method
- The runner builds each implementation once before timing. The reported
numbers measure execution only, not compile time.
- Slovo timings use `glagol build`, which currently lowers to LLVM and then
invokes host `clang -O2` with `runtime/runtime.c`.
- C timings use `clang -O2 -std=c11`.
- Rust timings use `rustc -C opt-level=3 -C debuginfo=0`.
Timing is cold-process local-machine evidence only. Clojure timings include
JVM and Clojure startup, while Common Lisp timings include SBCL script
startup.
Hot-loop mode is startup-amortized local evidence. It reports total time plus
normalized time for the base `1000000` loop count.

View File

@ -0,0 +1,10 @@
{
"benchmark": "branch-loop",
"source_stem": "branch_loop",
"loop_count": 1000000,
"expected_checksum": "1185071",
"stdin": "1000000\n",
"hot_loop_count": 10000000,
"hot_expected_checksum": "220775",
"hot_stdin": "10000000\n"
}

View File

@ -0,0 +1,33 @@
#include <stdint.h>
#include <stdio.h>
#define LOOP_COUNT 1000000
#define EXPECTED_CHECKSUM 1185071
static int32_t configured_loop_count(void) {
int32_t value = LOOP_COUNT;
if (scanf("%d", &value) != 1 || value <= 0) {
return LOOP_COUNT;
}
return value;
}
static int32_t branch_loop(int32_t limit) {
int32_t i = 0;
int32_t acc = 7;
while (i < limit) {
acc = acc > 1000000000 ? acc - 1000000000 : acc;
acc = i < 500000 ? acc + 3 : acc + 7;
acc = acc > 1234567 ? acc - 1234567 : acc + 11;
i = i + 1;
}
return acc;
}
int main(void) {
int32_t result = branch_loop(configured_loop_count());
printf("%d\n", result);
return result == EXPECTED_CHECKSUM ? 0 : 1;
}

View File

@ -0,0 +1,33 @@
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(def loop-count 1000000)
(def expected-checksum 1185071)
(defn configured-loop-count []
(try
(let [line (read-line)
value (Integer/parseInt (.trim ^String line))]
(if (pos? value) value loop-count))
(catch Exception _
loop-count)))
(defn branch-loop [limit]
(loop [i 0
acc 7]
(if (< i limit)
(let [bounded (if (> acc 1000000000)
(- acc 1000000000)
acc)
branched (if (< i 500000)
(+ bounded 3)
(+ bounded 7))
next (if (> branched 1234567)
(- branched 1234567)
(+ branched 11))]
(recur (inc i) next))
acc)))
(let [result (branch-loop (configured-loop-count))]
(println result)
(System/exit (if (= result expected-checksum) 0 1)))

View File

@ -0,0 +1,38 @@
(declaim (optimize (speed 3) (safety 0) (debug 0)))
(defconstant +loop-count+ 1000000)
(defconstant +expected-checksum+ 1185071)
(declaim (ftype (function () fixnum) configured-loop-count))
(defun configured-loop-count ()
(handler-case
(let ((line (read-line *standard-input* nil nil)))
(if line
(let ((value (parse-integer line :junk-allowed t)))
(if (> value 0) value +loop-count+))
+loop-count+))
(error () +loop-count+)))
(declaim (ftype (function (fixnum) fixnum) branch-loop))
(defun branch-loop (limit)
(declare (type fixnum limit))
(loop with i of-type fixnum = 0
with acc of-type fixnum = 7
while (< i limit)
do (let* ((bounded (if (> acc 1000000000)
(- acc 1000000000)
acc))
(branched (if (< i 500000)
(+ bounded 3)
(+ bounded 7)))
(next (if (> branched 1234567)
(- branched 1234567)
(+ branched 11))))
(declare (type fixnum bounded branched next))
(setf acc next
i (+ i 1)))
finally (return acc)))
(let ((result (branch-loop (configured-loop-count))))
(format t "~D~%" result)
(sb-ext:exit :code (if (= result +expected-checksum+) 0 1)))

View File

@ -0,0 +1,34 @@
LOOP_COUNT = 1_000_000
EXPECTED_CHECKSUM = 1_185_071
def configured_loop_count() -> int:
try:
value = int(input().strip())
except (EOFError, ValueError):
return LOOP_COUNT
return value if value > 0 else LOOP_COUNT
def branch_loop(limit: int) -> int:
i = 0
acc = 7
while i < limit:
acc = acc - 1_000_000_000 if acc > 1_000_000_000 else acc
acc = acc + 3 if i < 500_000 else acc + 7
acc = acc - 1_234_567 if acc > 1_234_567 else acc + 11
i += 1
return acc
def main() -> int:
result = branch_loop(configured_loop_count())
print(result)
return 0 if result == EXPECTED_CHECKSUM else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Run the local branch-loop benchmark scaffold."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from runner import main
if __name__ == "__main__":
raise SystemExit(main(Path(__file__).resolve().parent, sys.argv[1:]))

View File

@ -0,0 +1,44 @@
const LOOP_COUNT: i32 = 1_000_000;
const EXPECTED_CHECKSUM: i32 = 1_185_071;
fn configured_loop_count() -> i32 {
let mut input = String::new();
if std::io::stdin().read_line(&mut input).is_err() {
return LOOP_COUNT;
}
input
.trim()
.parse::<i32>()
.ok()
.filter(|value| *value > 0)
.unwrap_or(LOOP_COUNT)
}
fn branch_loop(limit: i32) -> i32 {
let mut i = 0;
let mut acc = 7;
while i < limit {
acc = if acc > 1_000_000_000 {
acc - 1_000_000_000
} else {
acc
};
acc = if i < 500_000 { acc + 3 } else { acc + 7 };
acc = if acc > 1_234_567 {
acc - 1_234_567
} else {
acc + 11
};
i += 1;
}
acc
}
fn main() {
let result = branch_loop(configured_loop_count());
println!("{}", result);
std::process::exit(if result == EXPECTED_CHECKSUM { 0 } else { 1 });
}

View File

@ -0,0 +1,4 @@
[project]
name = "branch-loop"
source_root = "src"
entry = "main"

View File

@ -0,0 +1,58 @@
; Benchmark scaffold fixture for local-machine branch timing comparisons only.
(module main)
(fn loop_count () -> i32
1000000)
(fn expected_checksum () -> i32
1185071)
(fn parse_stdin_loop_count () -> (result i32 i32)
(let input (result string i32) (std.io.read_stdin_result))
(match input
((ok text)
(std.string.parse_i32_result text))
((err code)
(err i32 i32 code))))
(fn parse_arg_loop_count () -> (result i32 i32)
(std.string.parse_i32_result (std.process.arg 1)))
(fn configured_stdin_loop_count () -> i32
(let parsed_stdin (result i32 i32) (parse_stdin_loop_count))
(if (is_ok parsed_stdin)
(unwrap_ok parsed_stdin)
(loop_count)))
(fn configured_loop_count () -> i32
(let parsed_arg (result i32 i32) (parse_arg_loop_count))
(if (is_ok parsed_arg)
(unwrap_ok parsed_arg)
(configured_stdin_loop_count)))
(fn branch_loop ((limit i32)) -> i32
(var i i32 0)
(var acc i32 7)
(while (< i limit)
(set acc (if (> acc 1000000000)
(- acc 1000000000)
acc))
(set acc (if (< i 500000)
(+ acc 3)
(+ acc 7)))
(set acc (if (> acc 1234567)
(- acc 1234567)
(+ acc 11)))
(set i (+ i 1)))
acc)
(fn main () -> i32
(let result i32 (branch_loop (configured_loop_count)))
(std.io.print_i32 result)
(if (= result (expected_checksum))
0
1))
(test "branch loop checksum is deterministic"
(= (branch_loop (loop_count)) (expected_checksum)))

View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,73 @@
# Enum Struct Payload Loop Benchmark Scaffold
Release: `exp-122`.
This benchmark compares enum payload selection, enum `match`, and immutable
fixed-array access through a struct payload plus scalar accumulation across
Slovo, C, Rust, Python, Clojure, and Common Lisp/SBCL on the same machine.
It is not a published benchmark result, performance threshold, optimizer
claim, or cross-machine comparison.
## Files
- `src/main.slo`: Slovo project benchmark fixture
- `c/enum_struct_payload_loop.c`: C comparison implementation
- `rust/enum_struct_payload_loop.rs`: Rust comparison implementation
- `python/enum_struct_payload_loop.py`: Python comparison implementation
- `clojure/enum_struct_payload_loop.clj`: Clojure comparison implementation
- `common-lisp/enum_struct_payload_loop.lisp`: Common Lisp/SBCL comparison implementation
- `run.py`: build/run/timing harness
All implementations print checksum `3500013` for loop count `1000000`.
Hot-loop mode uses loop count `10000000` and checksum `35000013`. The runner
supplies the loop count at runtime so native compilers cannot fold the loop
into a constant answer.
## Commands
Run from the Glagol repository root:
```bash
python3 benchmarks/enum-struct-payload-loop/run.py --list
python3 benchmarks/enum-struct-payload-loop/run.py --dry-run
python3 benchmarks/enum-struct-payload-loop/run.py --only python --repeats 3 --warmups 1
python3 benchmarks/enum-struct-payload-loop/run.py --mode hot-loop --only slovo --only c --only rust
```
To include Slovo, build or point at a Glagol binary and make sure host Clang is
available:
```bash
cargo build --manifest-path compiler/Cargo.toml --bin glagol
python3 benchmarks/enum-struct-payload-loop/run.py --glagol compiler/target/debug/glagol
```
The runner skips missing C/Rust/Slovo/Clojure/SBCL toolchains where possible.
Use `--only` multiple times to select implementations:
```bash
python3 benchmarks/enum-struct-payload-loop/run.py --only slovo --only c --only rust --only clojure --only common_lisp
```
Clojure is detected with `clojure` on PATH, `CLOJURE`, or `CLOJURE_JAR`.
Common Lisp is detected with `sbcl` on PATH, `SBCL`, or `--sbcl`.
## Comparison Method
- The runner builds each implementation once before timing. The reported
numbers measure execution only, not compile time.
- Slovo timings use `glagol build`, which currently lowers to LLVM and then
invokes host `clang -O2` with `runtime/runtime.c`.
- C timings use `clang -O2 -std=c11`.
- Rust timings use `rustc -C opt-level=3 -C debuginfo=0`.
- The measured loop alternates between two enum payload variants, matches the
selected variant, reads one fixed `i32` array through the bound struct
payload, and accumulates the selected value into an `i32` checksum.
Timing is cold-process local-machine evidence only. Clojure timings include
JVM and Clojure startup, while Common Lisp timings include SBCL script
startup.
Hot-loop mode is startup-amortized local evidence. It runs a larger loop count
and reports total time plus normalized time for the base `1000000` loop count.

View File

@ -0,0 +1,10 @@
{
"benchmark": "enum-struct-payload-loop",
"source_stem": "enum_struct_payload_loop",
"loop_count": 1000000,
"expected_checksum": "3500013",
"stdin": "1000000\n",
"hot_loop_count": 10000000,
"hot_expected_checksum": "35000013",
"hot_stdin": "10000000\n"
}

View File

@ -0,0 +1,75 @@
#include <stdint.h>
#include <stdio.h>
#define LOOP_COUNT 1000000
#define EXPECTED_CHECKSUM 3500013
typedef struct {
int32_t digits[8];
} Packet;
typedef enum {
PACKET_MISSING = 0,
PACKET_LIVE = 1,
PACKET_CACHED = 2,
} PacketTag;
typedef struct {
PacketTag tag;
Packet payload;
} PacketState;
static int32_t configured_loop_count(void) {
int32_t value = LOOP_COUNT;
if (scanf("%d", &value) != 1 || value <= 0) {
return LOOP_COUNT;
}
return value;
}
static PacketState live_state(void) {
PacketState state = {PACKET_LIVE, {{2, 7, 1, 8, 2, 8, 1, 8}}};
return state;
}
static PacketState cached_state(void) {
PacketState state = {PACKET_CACHED, {{1, 6, 1, 8, 0, 3, 4, 5}}};
return state;
}
static PacketState select_state(int32_t i, PacketState live, PacketState cached) {
return i % 2 == 0 ? live : cached;
}
static int32_t state_digit(PacketState state, int32_t index) {
switch (state.tag) {
case PACKET_LIVE:
case PACKET_CACHED:
return state.payload.digits[index];
case PACKET_MISSING:
default:
return 0;
}
}
static int32_t enum_struct_payload_loop(int32_t limit) {
PacketState live = live_state();
PacketState cached = cached_state();
int32_t i = 0;
int32_t acc = 13;
while (i < limit) {
PacketState state = select_state(i, live, cached);
acc = acc + state_digit(state, i % 8);
acc = acc > 1000000000 ? acc - 1000000000 : acc;
i = i + 1;
}
return acc;
}
int main(void) {
int32_t result = enum_struct_payload_loop(configured_loop_count());
printf("%d\n", result);
return result == EXPECTED_CHECKSUM ? 0 : 1;
}

View File

@ -0,0 +1,46 @@
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(def loop-count 1000000)
(def expected-checksum 3500013)
(defrecord Packet [digits])
(defn configured-loop-count []
(try
(let [line (read-line)
value (Integer/parseInt (.trim ^String line))]
(if (pos? value) value loop-count))
(catch Exception _
loop-count)))
(defn live-state []
{:tag :live :payload (->Packet [2 7 1 8 2 8 1 8])})
(defn cached-state []
{:tag :cached :payload (->Packet [1 6 1 8 0 3 4 5])})
(defn select-state [i live cached]
(if (zero? (rem i 2)) live cached))
(defn state-digit [state index]
(case (:tag state)
:missing 0
(nth (:digits ^Packet (:payload state)) index)))
(defn enum-struct-payload-loop [limit]
(loop [i 0
acc 13
live (live-state)
cached (cached-state)]
(if (< i limit)
(let [next (+ acc (state-digit (select-state i live cached) (rem i 8)))
bounded (if (> next 1000000000)
(- next 1000000000)
next)]
(recur (inc i) bounded live cached))
acc)))
(let [result (enum-struct-payload-loop (configured-loop-count))]
(println result)
(System/exit (if (= result expected-checksum) 0 1)))

View File

@ -0,0 +1,66 @@
(declaim (optimize (speed 3) (safety 0) (debug 0)))
(defconstant +loop-count+ 1000000)
(defconstant +expected-checksum+ 3500013)
(defstruct (packet (:constructor %make-packet (digits))) digits)
(defstruct (packet-state (:constructor %make-packet-state (tag payload))) tag payload)
(declaim (ftype (function () fixnum) configured-loop-count))
(defun configured-loop-count ()
(handler-case
(let ((line (read-line *standard-input* nil nil)))
(if line
(let ((value (parse-integer line :junk-allowed t)))
(if (> value 0) value +loop-count+))
+loop-count+))
(error () +loop-count+)))
(declaim (ftype (function () packet-state) live-state))
(defun live-state ()
(%make-packet-state :live (%make-packet #(2 7 1 8 2 8 1 8))))
(declaim (ftype (function () packet-state) cached-state))
(defun cached-state ()
(%make-packet-state :cached (%make-packet #(1 6 1 8 0 3 4 5))))
(declaim (ftype (function (fixnum packet-state packet-state) packet-state) select-state))
(defun select-state (i live cached)
(declare (type fixnum i))
(if (zerop (rem i 2))
live
cached))
(declaim (ftype (function (packet-state fixnum) fixnum) state-digit))
(defun state-digit (state index)
(declare (type packet-state state)
(type fixnum index))
(let* ((payload (packet-state-payload state))
(digits (packet-digits payload))
(value (svref digits index)))
(declare (type simple-vector digits)
(type fixnum value))
(case (packet-state-tag state)
(:missing 0)
(otherwise value))))
(declaim (ftype (function (fixnum) fixnum) enum-struct-payload-loop))
(defun enum-struct-payload-loop (limit)
(declare (type fixnum limit))
(loop with i of-type fixnum = 0
with acc of-type fixnum = 13
with live of-type packet-state = (live-state)
with cached of-type packet-state = (cached-state)
while (< i limit)
do (let* ((next (+ acc (state-digit (select-state i live cached) (rem i 8))))
(bounded (if (> next 1000000000)
(- next 1000000000)
next)))
(declare (type fixnum next bounded))
(setf acc bounded
i (+ i 1)))
finally (return acc)))
(let ((result (enum-struct-payload-loop (configured-loop-count))))
(format t "~D~%" result)
(sb-ext:exit :code (if (= result +expected-checksum+) 0 1)))

View File

@ -0,0 +1,50 @@
LOOP_COUNT = 1_000_000
EXPECTED_CHECKSUM = 3_500_013
LIVE_STATE = {"tag": "live", "payload": {"digits": [2, 7, 1, 8, 2, 8, 1, 8]}}
CACHED_STATE = {"tag": "cached", "payload": {"digits": [1, 6, 1, 8, 0, 3, 4, 5]}}
def configured_loop_count() -> int:
try:
value = int(input().strip())
except (EOFError, ValueError):
return LOOP_COUNT
return value if value > 0 else LOOP_COUNT
def select_state(i: int) -> dict[str, object]:
return LIVE_STATE if i % 2 == 0 else CACHED_STATE
def state_digit(state: dict[str, object], index: int) -> int:
if state["tag"] == "missing":
return 0
payload = state["payload"]
assert isinstance(payload, dict)
digits = payload["digits"]
assert isinstance(digits, list)
return digits[index]
def enum_struct_payload_loop(limit: int) -> int:
i = 0
acc = 13
while i < limit:
acc += state_digit(select_state(i), i % 8)
acc = acc - 1_000_000_000 if acc > 1_000_000_000 else acc
i += 1
return acc
def main() -> int:
result = enum_struct_payload_loop(configured_loop_count())
print(result)
return 0 if result == EXPECTED_CHECKSUM else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Run the local enum-struct-payload-loop benchmark scaffold."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from runner import main
if __name__ == "__main__":
raise SystemExit(main(Path(__file__).resolve().parent, sys.argv[1:]))

View File

@ -0,0 +1,78 @@
const LOOP_COUNT: i32 = 1_000_000;
const EXPECTED_CHECKSUM: i32 = 3_500_013;
struct Packet {
digits: [i32; 8],
}
enum PacketState {
Missing,
Live(Packet),
Cached(Packet),
}
fn configured_loop_count() -> i32 {
let mut input = String::new();
if std::io::stdin().read_line(&mut input).is_err() {
return LOOP_COUNT;
}
input
.trim()
.parse::<i32>()
.ok()
.filter(|value| *value > 0)
.unwrap_or(LOOP_COUNT)
}
fn live_state() -> PacketState {
PacketState::Live(Packet {
digits: [2, 7, 1, 8, 2, 8, 1, 8],
})
}
fn cached_state() -> PacketState {
PacketState::Cached(Packet {
digits: [1, 6, 1, 8, 0, 3, 4, 5],
})
}
fn select_state<'a>(i: i32, live: &'a PacketState, cached: &'a PacketState) -> &'a PacketState {
if i % 2 == 0 {
live
} else {
cached
}
}
fn state_digit(state: &PacketState, index: i32) -> i32 {
match state {
PacketState::Missing => 0,
PacketState::Live(payload) | PacketState::Cached(payload) => payload.digits[index as usize],
}
}
fn enum_struct_payload_loop(limit: i32) -> i32 {
let live = live_state();
let cached = cached_state();
let mut i = 0;
let mut acc = 13;
while i < limit {
acc += state_digit(select_state(i, &live, &cached), i % 8);
acc = if acc > 1_000_000_000 {
acc - 1_000_000_000
} else {
acc
};
i += 1;
}
acc
}
fn main() {
let result = enum_struct_payload_loop(configured_loop_count());
println!("{}", result);
std::process::exit(if result == EXPECTED_CHECKSUM { 0 } else { 1 });
}

View File

@ -0,0 +1,4 @@
[project]
name = "enum-struct-payload-loop"
source_root = "src"
entry = "main"

View File

@ -0,0 +1,93 @@
; Benchmark scaffold fixture for local-machine enum-struct-payload timing
; comparisons only. Keep LOOP_COUNT and EXPECTED_CHECKSUM aligned with the
; C/Rust/Python fixtures. The runner supplies the loop count through stdin or
; argv so native compilers cannot fold the benchmark loop into a constant
; answer.
(module main)
(struct Packet
(digits (array i32 8)))
(enum PacketState
Missing
(Live Packet)
(Cached Packet))
(fn loop_count () -> i32
1000000)
(fn expected_checksum () -> i32
3500013)
(fn parse_stdin_loop_count () -> (result i32 i32)
(let input (result string i32) (std.io.read_stdin_result))
(match input
((ok text)
(std.string.parse_i32_result text))
((err code)
(err i32 i32 code))))
(fn parse_arg_loop_count () -> (result i32 i32)
(std.string.parse_i32_result (std.process.arg 1)))
(fn configured_stdin_loop_count () -> i32
(let parsed_stdin (result i32 i32) (parse_stdin_loop_count))
(if (is_ok parsed_stdin)
(unwrap_ok parsed_stdin)
(loop_count)))
(fn configured_loop_count () -> i32
(let parsed_arg (result i32 i32) (parse_arg_loop_count))
(if (is_ok parsed_arg)
(unwrap_ok parsed_arg)
(configured_stdin_loop_count)))
(fn live_digits () -> (array i32 8)
(array i32 2 7 1 8 2 8 1 8))
(fn cached_digits () -> (array i32 8)
(array i32 1 6 1 8 0 3 4 5))
(fn live_state () -> PacketState
(PacketState.Live (Packet (digits (live_digits)))))
(fn cached_state () -> PacketState
(PacketState.Cached (Packet (digits (cached_digits)))))
(fn select_state ((i i32) (live PacketState) (cached PacketState)) -> PacketState
(if (= (% i 2) 0)
live
cached))
(fn state_digit ((state PacketState) (i i32)) -> i32
(match state
((PacketState.Missing)
0)
((PacketState.Live payload)
(index (. payload digits) i))
((PacketState.Cached payload)
(index (. payload digits) i))))
(fn enum_struct_payload_loop ((limit i32)) -> i32
(let live PacketState (live_state))
(let cached PacketState (cached_state))
(var i i32 0)
(var acc i32 13)
(while (< i limit)
(set acc (+ acc (state_digit (select_state i live cached) (% i 8))))
(set acc (if (> acc 1000000000)
(- acc 1000000000)
acc))
(set i (+ i 1)))
acc)
(fn main () -> i32
(let result i32 (enum_struct_payload_loop (configured_loop_count)))
(std.io.print_i32 result)
(if (= result (expected_checksum))
0
1))
(test "enum struct payload loop checksum is deterministic"
(= (enum_struct_payload_loop (loop_count)) (expected_checksum)))

1
benchmarks/math-loop/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,72 @@
# Math Loop Benchmark Scaffold
Release: established by `exp-39`; Clojure comparison and shared runner
updated by `exp-40`; Common Lisp/SBCL comparison added by `exp-41`;
hot-loop mode added by `exp-42`.
This directory is a local timing harness for comparing one deterministic
math-heavy loop across Slovo, C, Rust, Python, Clojure, and Common Lisp/SBCL
on the same machine.
It is not a published benchmark result, performance threshold, optimizer
claim, or cross-machine comparison.
## Files
- `src/main.slo`: Slovo project benchmark fixture
- `c/math_loop.c`: C comparison implementation
- `rust/math_loop.rs`: Rust comparison implementation
- `python/math_loop.py`: Python comparison implementation
- `clojure/math_loop.clj`: Clojure comparison implementation
- `common-lisp/math_loop.lisp`: Common Lisp/SBCL comparison implementation
- `run.py`: build/run/timing harness
All implementations print the same checksum, `5000001`, for the default loop
count, `1000000`. Hot-loop mode uses loop count `10000000` and checksum
`50000001`. The runner supplies the loop count at runtime so native compilers
cannot fold the loop into a constant answer.
## Commands
Run from the Glagol repository root:
```bash
python3 benchmarks/math-loop/run.py --list
python3 benchmarks/math-loop/run.py --dry-run
python3 benchmarks/math-loop/run.py --only python --repeats 3 --warmups 1
python3 benchmarks/math-loop/run.py --mode hot-loop --only slovo --only c --only rust
```
To include Slovo, build or point at a Glagol binary and make sure host Clang is
available:
```bash
cargo build --manifest-path compiler/Cargo.toml --bin glagol
python3 benchmarks/math-loop/run.py --glagol compiler/target/debug/glagol
```
The runner skips missing C/Rust/Slovo/Clojure/SBCL toolchains where possible.
Use `--only` multiple times to select implementations:
```bash
python3 benchmarks/math-loop/run.py --only slovo --only c --only rust --only clojure --only common_lisp
```
Clojure is detected with `clojure` on PATH, `CLOJURE`, or `CLOJURE_JAR`.
Common Lisp is detected with `sbcl` on PATH, `SBCL`, or `--sbcl`.
## Comparison Method
- The runner builds each implementation once before timing. The reported
numbers measure execution only, not compile time.
- Slovo timings use `glagol build`, which currently lowers to LLVM and then
invokes host `clang -O2` with `runtime/runtime.c`.
- C timings use `clang -O2 -std=c11`.
- Rust timings use `rustc -C opt-level=3 -C debuginfo=0`.
Timing is cold-process local-machine evidence only. Clojure timings include
JVM and Clojure startup, while Common Lisp timings include SBCL script
startup.
Hot-loop mode is startup-amortized local evidence. It runs a larger loop count
and reports total time plus normalized time for the base `1000000` loop count.

View File

@ -0,0 +1,10 @@
{
"benchmark": "math-loop",
"source_stem": "math_loop",
"loop_count": 1000000,
"expected_checksum": "5000001",
"stdin": "1000000\n",
"hot_loop_count": 10000000,
"hot_expected_checksum": "50000001",
"hot_stdin": "10000000\n"
}

View File

@ -0,0 +1,32 @@
#include <stdint.h>
#include <stdio.h>
#define LOOP_COUNT 1000000
#define EXPECTED_CHECKSUM 5000001
static int32_t math_loop(int32_t limit) {
int32_t i = 0;
int32_t acc = 1;
while (i < limit) {
acc = acc + ((i + 3) * 2);
acc = acc > 1000000000 ? acc - 1000000000 : acc;
i = i + 1;
}
return acc;
}
static int32_t configured_loop_count(void) {
int32_t value = LOOP_COUNT;
if (scanf("%d", &value) != 1 || value <= 0) {
return LOOP_COUNT;
}
return value;
}
int main(void) {
int32_t result = math_loop(configured_loop_count());
printf("%d\n", result);
return result == EXPECTED_CHECKSUM ? 0 : 1;
}

View File

@ -0,0 +1,28 @@
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(def loop-count 1000000)
(def expected-checksum 5000001)
(defn configured-loop-count []
(try
(let [line (read-line)
value (Integer/parseInt (.trim ^String line))]
(if (pos? value) value loop-count))
(catch Exception _
loop-count)))
(defn math-loop [limit]
(loop [i 0
acc 1]
(if (< i limit)
(let [next (+ acc (* (+ i 3) 2))
bounded (if (> next 1000000000)
(- next 1000000000)
next)]
(recur (inc i) bounded))
acc)))
(let [result (math-loop (configured-loop-count))]
(println result)
(System/exit (if (= result expected-checksum) 0 1)))

View File

@ -0,0 +1,33 @@
(declaim (optimize (speed 3) (safety 0) (debug 0)))
(defconstant +loop-count+ 1000000)
(defconstant +expected-checksum+ 5000001)
(declaim (ftype (function () fixnum) configured-loop-count))
(defun configured-loop-count ()
(handler-case
(let ((line (read-line *standard-input* nil nil)))
(if line
(let ((value (parse-integer line :junk-allowed t)))
(if (> value 0) value +loop-count+))
+loop-count+))
(error () +loop-count+)))
(declaim (ftype (function (fixnum) fixnum) math-loop))
(defun math-loop (limit)
(declare (type fixnum limit))
(loop with i of-type fixnum = 0
with acc of-type fixnum = 1
while (< i limit)
do (let* ((next (+ acc (* (+ i 3) 2)))
(bounded (if (> next 1000000000)
(- next 1000000000)
next)))
(declare (type fixnum next bounded))
(setf acc bounded
i (+ i 1)))
finally (return acc)))
(let ((result (math-loop (configured-loop-count))))
(format t "~D~%" result)
(sb-ext:exit :code (if (= result +expected-checksum+) 0 1)))

View File

@ -0,0 +1,33 @@
LOOP_COUNT = 1_000_000
EXPECTED_CHECKSUM = 5_000_001
def math_loop(limit: int) -> int:
i = 0
acc = 1
while i < limit:
acc = acc + ((i + 3) * 2)
acc = acc - 1_000_000_000 if acc > 1_000_000_000 else acc
i += 1
return acc
def configured_loop_count() -> int:
try:
value = int(input().strip())
except (EOFError, ValueError):
return LOOP_COUNT
return value if value > 0 else LOOP_COUNT
def main() -> int:
result = math_loop(configured_loop_count())
print(result)
return 0 if result == EXPECTED_CHECKSUM else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Run the local math-loop benchmark scaffold."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from runner import main
if __name__ == "__main__":
raise SystemExit(main(Path(__file__).resolve().parent, sys.argv[1:]))

View File

@ -0,0 +1,39 @@
const LOOP_COUNT: i32 = 1_000_000;
const EXPECTED_CHECKSUM: i32 = 5_000_001;
fn math_loop(limit: i32) -> i32 {
let mut i = 0;
let mut acc = 1;
while i < limit {
acc = acc + ((i + 3) * 2);
acc = if acc > 1_000_000_000 {
acc - 1_000_000_000
} else {
acc
};
i += 1;
}
acc
}
fn configured_loop_count() -> i32 {
let mut input = String::new();
if std::io::stdin().read_line(&mut input).is_err() {
return LOOP_COUNT;
}
input
.trim()
.parse::<i32>()
.ok()
.filter(|value| *value > 0)
.unwrap_or(LOOP_COUNT)
}
fn main() {
let result = math_loop(configured_loop_count());
println!("{}", result);
std::process::exit(if result == EXPECTED_CHECKSUM { 0 } else { 1 });
}

View File

@ -0,0 +1,4 @@
[project]
name = "math-loop"
source_root = "src"
entry = "main"

View File

@ -0,0 +1,56 @@
; Benchmark scaffold fixture for local-machine timing comparisons only.
; Keep LOOP_COUNT and EXPECTED_CHECKSUM aligned with the C/Rust/Python fixtures.
; The runner supplies the loop count through stdin so native compilers cannot
; fold the benchmark loop into a constant answer.
(module main)
(fn loop_count () -> i32
1000000)
(fn expected_checksum () -> i32
5000001)
(fn parse_stdin_loop_count () -> (result i32 i32)
(let input (result string i32) (std.io.read_stdin_result))
(match input
((ok text)
(std.string.parse_i32_result text))
((err code)
(err i32 i32 code))))
(fn parse_arg_loop_count () -> (result i32 i32)
(std.string.parse_i32_result (std.process.arg 1)))
(fn configured_stdin_loop_count () -> i32
(let parsed_stdin (result i32 i32) (parse_stdin_loop_count))
(if (is_ok parsed_stdin)
(unwrap_ok parsed_stdin)
(loop_count)))
(fn configured_loop_count () -> i32
(let parsed_arg (result i32 i32) (parse_arg_loop_count))
(if (is_ok parsed_arg)
(unwrap_ok parsed_arg)
(configured_stdin_loop_count)))
(fn math_loop ((limit i32)) -> i32
(var i i32 0)
(var acc i32 1)
(while (< i limit)
(set acc (+ acc (* (+ i 3) 2)))
(set acc (if (> acc 1000000000)
(- acc 1000000000)
acc))
(set i (+ i 1)))
acc)
(fn main () -> i32
(let result i32 (math_loop (configured_loop_count)))
(std.io.print_i32 result)
(if (= result (expected_checksum))
0
1))
(test "math loop checksum is deterministic"
(= (math_loop (loop_count)) (expected_checksum)))

1
benchmarks/parse-loop/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,34 @@
# Parse Loop Benchmark Scaffold
Release: `exp-40`; Common Lisp/SBCL comparison added by `exp-41`; hot-loop
mode added by `exp-42`.
This benchmark compares repeated signed decimal `i32` parsing across Slovo, C,
Rust, Python, Clojure, and Common Lisp/SBCL on the same machine.
It is not a published benchmark result, performance threshold, optimizer
claim, or cross-machine comparison.
All implementations print checksum `345000001` for loop count `1000000` and
parse text `12345`. Hot-loop mode uses loop count `10000000` and checksum
`450000001`. The runner supplies the loop count and parse text at runtime.
## Comparison Method
- The runner builds each implementation once before timing. The reported
numbers measure execution only, not compile time.
- Slovo timings use `glagol build`, which currently lowers to LLVM and then
invokes host `clang -O2` with `runtime/runtime.c`.
- C timings use `clang -O2 -std=c11`.
- Rust timings use `rustc -C opt-level=3 -C debuginfo=0`.
- The parse implementations are intentionally comparable by input and checksum,
not identical by parser internals: Slovo uses `std.string.parse_i32_result`,
C uses `strtol`, Rust uses `parse::<i32>()`, Python uses `int`, Clojure uses
`Integer/parseInt`, and Common Lisp uses `parse-integer`.
Timing is cold-process local-machine evidence only. Clojure timings include
JVM and Clojure startup, while Common Lisp timings include SBCL script
startup.
Hot-loop mode is startup-amortized local evidence. It reports total time plus
normalized time for the base `1000000` loop count.

View File

@ -0,0 +1,11 @@
{
"benchmark": "parse-loop",
"source_stem": "parse_loop",
"loop_count": 1000000,
"expected_checksum": "345000001",
"stdin": "1000000\n",
"hot_loop_count": 10000000,
"hot_expected_checksum": "450000001",
"hot_stdin": "10000000\n",
"run_args": ["12345"]
}

View File

@ -0,0 +1,34 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define LOOP_COUNT 1000000
#define EXPECTED_CHECKSUM 345000001
static int32_t configured_loop_count(void) {
int32_t value = LOOP_COUNT;
if (scanf("%d", &value) != 1 || value <= 0) {
return LOOP_COUNT;
}
return value;
}
static int32_t parse_loop(int32_t limit, const char *text) {
int32_t i = 0;
int32_t acc = 1;
while (i < limit) {
acc = acc + (int32_t)strtol(text, NULL, 10);
acc = acc > 1000000000 ? acc - 1000000000 : acc;
i = i + 1;
}
return acc;
}
int main(int argc, char **argv) {
const char *text = argc > 1 ? argv[1] : "12345";
int32_t result = parse_loop(configured_loop_count(), text);
printf("%d\n", result);
return result == EXPECTED_CHECKSUM ? 0 : 1;
}

View File

@ -0,0 +1,31 @@
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(def loop-count 1000000)
(def expected-checksum 345000001)
(defn configured-loop-count []
(try
(let [line (read-line)
value (Integer/parseInt (.trim ^String line))]
(if (pos? value) value loop-count))
(catch Exception _
loop-count)))
(defn parse-text []
(or (first *command-line-args*) "12345"))
(defn parse-loop [limit text]
(loop [i 0
acc 1]
(if (< i limit)
(let [next (+ acc (Integer/parseInt ^String text))
bounded (if (> next 1000000000)
(- next 1000000000)
next)]
(recur (inc i) bounded))
acc)))
(let [result (parse-loop (configured-loop-count) (parse-text))]
(println result)
(System/exit (if (= result expected-checksum) 0 1)))

View File

@ -0,0 +1,38 @@
(declaim (optimize (speed 3) (safety 0) (debug 0)))
(defconstant +loop-count+ 1000000)
(defconstant +expected-checksum+ 345000001)
(declaim (ftype (function () fixnum) configured-loop-count))
(defun configured-loop-count ()
(handler-case
(let ((line (read-line *standard-input* nil nil)))
(if line
(let ((value (parse-integer line :junk-allowed t)))
(if (> value 0) value +loop-count+))
+loop-count+))
(error () +loop-count+)))
(declaim (ftype (function () string) parse-text))
(defun parse-text ()
(or (second sb-ext:*posix-argv*) "12345"))
(declaim (ftype (function (fixnum string) fixnum) parse-loop))
(defun parse-loop (limit text)
(declare (type fixnum limit)
(type string text))
(loop with i of-type fixnum = 0
with acc of-type fixnum = 1
while (< i limit)
do (let* ((next (+ acc (parse-integer text)))
(bounded (if (> next 1000000000)
(- next 1000000000)
next)))
(declare (type fixnum next bounded))
(setf acc bounded
i (+ i 1)))
finally (return acc)))
(let ((result (parse-loop (configured-loop-count) (parse-text))))
(format t "~D~%" result)
(sb-ext:exit :code (if (= result +expected-checksum+) 0 1)))

View File

@ -0,0 +1,40 @@
import sys
LOOP_COUNT = 1_000_000
EXPECTED_CHECKSUM = 345_000_001
def configured_loop_count() -> int:
try:
value = int(input().strip())
except (EOFError, ValueError):
return LOOP_COUNT
return value if value > 0 else LOOP_COUNT
def parse_text() -> str:
return sys.argv[1] if len(sys.argv) > 1 else "12345"
def parse_loop(limit: int, text: str) -> int:
i = 0
acc = 1
while i < limit:
acc += int(text)
acc = acc - 1_000_000_000 if acc > 1_000_000_000 else acc
i += 1
return acc
def main() -> int:
result = parse_loop(configured_loop_count(), parse_text())
print(result)
return 0 if result == EXPECTED_CHECKSUM else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Run the local parse-loop benchmark scaffold."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from runner import main
if __name__ == "__main__":
raise SystemExit(main(Path(__file__).resolve().parent, sys.argv[1:]))

View File

@ -0,0 +1,44 @@
const LOOP_COUNT: i32 = 1_000_000;
const EXPECTED_CHECKSUM: i32 = 345_000_001;
fn configured_loop_count() -> i32 {
let mut input = String::new();
if std::io::stdin().read_line(&mut input).is_err() {
return LOOP_COUNT;
}
input
.trim()
.parse::<i32>()
.ok()
.filter(|value| *value > 0)
.unwrap_or(LOOP_COUNT)
}
fn parse_text() -> String {
std::env::args().nth(1).unwrap_or_else(|| "12345".to_string())
}
fn parse_loop(limit: i32, text: &str) -> i32 {
let mut i = 0;
let mut acc = 1;
while i < limit {
acc += text.parse::<i32>().unwrap();
acc = if acc > 1_000_000_000 {
acc - 1_000_000_000
} else {
acc
};
i += 1;
}
acc
}
fn main() {
let text = parse_text();
let result = parse_loop(configured_loop_count(), &text);
println!("{}", result);
std::process::exit(if result == EXPECTED_CHECKSUM { 0 } else { 1 });
}

View File

@ -0,0 +1,4 @@
[project]
name = "parse-loop"
source_root = "src"
entry = "main"

View File

@ -0,0 +1,59 @@
; Benchmark scaffold fixture for local-machine parse timing comparisons only.
(module main)
(fn loop_count () -> i32
1000000)
(fn expected_checksum () -> i32
345000001)
(fn parse_stdin_loop_count () -> (result i32 i32)
(let input (result string i32) (std.io.read_stdin_result))
(match input
((ok text)
(std.string.parse_i32_result text))
((err code)
(err i32 i32 code))))
(fn parse_arg_loop_count () -> (result i32 i32)
(std.string.parse_i32_result (std.process.arg 2)))
(fn configured_stdin_loop_count () -> i32
(let parsed_stdin (result i32 i32) (parse_stdin_loop_count))
(if (is_ok parsed_stdin)
(unwrap_ok parsed_stdin)
(loop_count)))
(fn configured_loop_count () -> i32
(let parsed_arg (result i32 i32) (parse_arg_loop_count))
(if (is_ok parsed_arg)
(unwrap_ok parsed_arg)
(configured_stdin_loop_count)))
(fn parse_text () -> string
(std.process.arg 1))
(fn parsed_value ((text string)) -> i32
(unwrap_ok (std.string.parse_i32_result text)))
(fn parse_loop ((limit i32) (text string)) -> i32
(var i i32 0)
(var acc i32 1)
(while (< i limit)
(set acc (+ acc (parsed_value text)))
(set acc (if (> acc 1000000000)
(- acc 1000000000)
acc))
(set i (+ i 1)))
acc)
(fn main () -> i32
(let result i32 (parse_loop (configured_loop_count) (parse_text)))
(std.io.print_i32 result)
(if (= result (expected_checksum))
0
1))
(test "parse loop checksum is deterministic"
(= (parse_loop (loop_count) "12345") (expected_checksum)))

522
benchmarks/runner.py Normal file
View File

@ -0,0 +1,522 @@
#!/usr/bin/env python3
"""Shared local benchmark runner for Glagol benchmark scaffolds."""
from __future__ import annotations
import argparse
import json
import os
import shutil
import statistics
import subprocess
import sys
import time
from dataclasses import dataclass
from pathlib import Path
from typing import Callable
TIMING_SCOPE = "local-machine comparison only"
TIMING_MODES = ["cold-process", "hot-loop"]
@dataclass(frozen=True)
class BenchmarkSpec:
name: str
source_stem: str
loop_count: int
expected_checksum: str
stdin_text: str
hot_loop_count: int
hot_expected_checksum: str
hot_stdin_text: str
run_args: list[str]
@dataclass(frozen=True)
class RunParameters:
mode: str
loop_count: int
expected_checksum: str
stdin_text: str
base_loop_count: int
@dataclass(frozen=True)
class Implementation:
name: str
language: str
source: Path
build: Callable[[Path, BenchmarkSpec, argparse.Namespace], tuple[list[str], list[str] | None]]
run: Callable[[Path, BenchmarkSpec, argparse.Namespace], list[str]]
def main(root: Path, argv: list[str]) -> int:
spec = read_spec(root)
implementations = available_implementations(root, spec)
parser = argparse.ArgumentParser(description=f"Run local {spec.name} timing comparisons.")
parser.add_argument("--list", action="store_true", help="print benchmark metadata and exit")
parser.add_argument("--json", action="store_true", help="emit JSON for --list or results")
parser.add_argument("--dry-run", action="store_true", help="print planned commands without running them")
parser.add_argument(
"--mode",
choices=TIMING_MODES,
default="cold-process",
help="cold-process measures one normal run; hot-loop uses an amplified loop count and normalized results",
)
parser.add_argument("--only", choices=[impl.name for impl in implementations], action="append")
parser.add_argument("--repeats", type=positive_int, default=5)
parser.add_argument("--warmups", type=non_negative_int, default=1)
parser.add_argument("--glagol", help="path to the glagol compiler binary")
parser.add_argument("--cc", help="path to the C compiler")
parser.add_argument("--clojure", help="path to the clojure command")
parser.add_argument("--clojure-jar", help="path to a clojure jar for `java -cp ... clojure.main`")
parser.add_argument("--sbcl", help="path to the SBCL executable for Common Lisp comparisons")
args = parser.parse_args(argv)
selected = select_implementations(implementations, args.only)
if args.list:
emit_list(root, spec, selected, args.json)
return 0
if args.dry_run:
emit_dry_run(root, spec, selected, args)
return 0
results = run_benchmarks(root, spec, selected, args)
emit_results(spec, results, args.json)
return 1 if any(result["status"] == "failed" for result in results) else 0
def read_spec(root: Path) -> BenchmarkSpec:
data = json.loads((root / "benchmark.json").read_text(encoding="utf-8"))
loop_count = int(data["loop_count"])
return BenchmarkSpec(
name=str(data["benchmark"]),
source_stem=str(data["source_stem"]),
loop_count=loop_count,
expected_checksum=str(data["expected_checksum"]),
stdin_text=str(data.get("stdin", f"{loop_count}\n")),
hot_loop_count=int(data.get("hot_loop_count", loop_count)),
hot_expected_checksum=str(data.get("hot_expected_checksum", data["expected_checksum"])),
hot_stdin_text=str(data.get("hot_stdin", f"{int(data.get('hot_loop_count', loop_count))}\n")),
run_args=[str(item) for item in data.get("run_args", [])],
)
def available_implementations(root: Path, spec: BenchmarkSpec) -> list[Implementation]:
candidates = [
Implementation("slovo", "Slovo", root / "src" / "main.slo", slovo_build, slovo_run),
Implementation("c", "C", root / "c" / f"{spec.source_stem}.c", c_build, c_run),
Implementation("rust", "Rust", root / "rust" / f"{spec.source_stem}.rs", rust_build, rust_run),
Implementation("python", "Python", root / "python" / f"{spec.source_stem}.py", python_build, python_run),
Implementation("clojure", "Clojure", root / "clojure" / f"{spec.source_stem}.clj", clojure_build, clojure_run),
Implementation(
"common_lisp",
"Common Lisp (SBCL)",
root / "common-lisp" / f"{spec.source_stem}.lisp",
common_lisp_build,
common_lisp_run,
),
]
return [impl for impl in candidates if impl.source.is_file()]
def slovo_compiler(root: Path, args: argparse.Namespace) -> str | None:
if args.glagol:
return args.glagol
env_path = os.environ.get("GLAGOL")
if env_path:
return env_path
candidate = root.parents[1] / "compiler" / "target" / "debug" / executable("glagol")
if candidate.is_file():
return str(candidate)
return shutil.which("glagol")
def executable(name: str) -> str:
return f"{name}.exe" if os.name == "nt" else name
def slovo_build(root: Path, spec: BenchmarkSpec, args: argparse.Namespace) -> tuple[list[str], list[str] | None]:
compiler = slovo_compiler(root, args)
if compiler is None:
return [], ["missing glagol compiler; set GLAGOL or pass --glagol"]
if os.environ.get("GLAGOL_CLANG") is None and shutil.which("clang") is None:
return [], ["missing clang for glagol build; set GLAGOL_CLANG"]
output = build_dir(root) / executable(f"slovo-{spec.name}")
return [compiler, "build", str(root), "-o", str(output)], None
def slovo_run(root: Path, spec: BenchmarkSpec, _args: argparse.Namespace) -> list[str]:
params = run_parameters(spec, _args.mode)
return [str(build_dir(root) / executable(f"slovo-{spec.name}")), *spec.run_args, str(params.loop_count)]
def c_build(root: Path, spec: BenchmarkSpec, args: argparse.Namespace) -> tuple[list[str], list[str] | None]:
compiler = args.cc or os.environ.get("CC") or first_available(["clang", "cc", "gcc"])
if compiler is None:
return [], ["missing C compiler; set CC or pass --cc"]
output = build_dir(root) / executable(f"c-{spec.name}")
return [compiler, "-O2", "-std=c11", str(root / "c" / f"{spec.source_stem}.c"), "-o", str(output)], None
def c_run(root: Path, spec: BenchmarkSpec, _args: argparse.Namespace) -> list[str]:
return [str(build_dir(root) / executable(f"c-{spec.name}")), *spec.run_args]
def rust_build(root: Path, spec: BenchmarkSpec, _args: argparse.Namespace) -> tuple[list[str], list[str] | None]:
rustc = first_available(["rustc"])
if rustc is None:
return [], ["missing rustc"]
output = build_dir(root) / executable(f"rust-{spec.name}")
return [
rustc,
"-C",
"opt-level=3",
"-C",
"debuginfo=0",
str(root / "rust" / f"{spec.source_stem}.rs"),
"-o",
str(output),
], None
def rust_run(root: Path, spec: BenchmarkSpec, _args: argparse.Namespace) -> list[str]:
return [str(build_dir(root) / executable(f"rust-{spec.name}")), *spec.run_args]
def python_build(_root: Path, _spec: BenchmarkSpec, _args: argparse.Namespace) -> tuple[list[str], list[str] | None]:
return [], None
def python_run(root: Path, spec: BenchmarkSpec, _args: argparse.Namespace) -> list[str]:
return [sys.executable, str(root / "python" / f"{spec.source_stem}.py"), *spec.run_args]
def clojure_build(_root: Path, _spec: BenchmarkSpec, args: argparse.Namespace) -> tuple[list[str], list[str] | None]:
if clojure_command(args) is None:
return [], ["missing clojure command; set CLOJURE, pass --clojure, or set CLOJURE_JAR"]
return [], None
def clojure_run(root: Path, spec: BenchmarkSpec, args: argparse.Namespace) -> list[str]:
source = str(root / "clojure" / f"{spec.source_stem}.clj")
command = clojure_command(args)
assert command is not None
return [*command, source, *spec.run_args]
def clojure_command(args: argparse.Namespace) -> list[str] | None:
if args.clojure:
return [args.clojure]
env_path = os.environ.get("CLOJURE")
if env_path:
return [env_path]
found = shutil.which("clojure")
if found:
return [found]
jar = args.clojure_jar or os.environ.get("CLOJURE_JAR")
java = shutil.which("java")
if jar and java:
return [java, "-cp", jar, "clojure.main"]
return None
def common_lisp_build(_root: Path, _spec: BenchmarkSpec, args: argparse.Namespace) -> tuple[list[str], list[str] | None]:
if sbcl_command(args) is None:
return [], ["missing SBCL; set SBCL or pass --sbcl"]
return [], None
def common_lisp_run(root: Path, spec: BenchmarkSpec, args: argparse.Namespace) -> list[str]:
sbcl = sbcl_command(args)
assert sbcl is not None
return [
sbcl,
"--noinform",
"--disable-debugger",
"--script",
str(root / "common-lisp" / f"{spec.source_stem}.lisp"),
*spec.run_args,
]
def sbcl_command(args: argparse.Namespace) -> str | None:
if args.sbcl:
return args.sbcl
env_path = os.environ.get("SBCL")
if env_path:
return env_path
return shutil.which("sbcl")
def build_dir(root: Path) -> Path:
return root / "build"
def positive_int(value: str) -> int:
parsed = int(value)
if parsed <= 0:
raise argparse.ArgumentTypeError("value must be greater than zero")
return parsed
def non_negative_int(value: str) -> int:
parsed = int(value)
if parsed < 0:
raise argparse.ArgumentTypeError("value must be zero or greater")
return parsed
def select_implementations(implementations: list[Implementation], names: list[str] | None) -> list[Implementation]:
if not names:
return implementations
selected_names = set(names)
return [impl for impl in implementations if impl.name in selected_names]
def emit_list(root: Path, spec: BenchmarkSpec, implementations: list[Implementation], as_json: bool) -> None:
metadata = {
"benchmark": spec.name,
"loop_count": spec.loop_count,
"hot_loop_count": spec.hot_loop_count,
"expected_checksum": spec.expected_checksum,
"hot_expected_checksum": spec.hot_expected_checksum,
"timing_scope": TIMING_SCOPE,
"timing_modes": TIMING_MODES,
"loop_count_source": "stdin",
"run_args": spec.run_args,
"implementations": [
{"name": impl.name, "language": impl.language, "source": str(impl.source.relative_to(root))}
for impl in implementations
],
}
if as_json:
print(json.dumps(metadata, indent=2, sort_keys=True))
return
print(f"{spec.name}: {TIMING_SCOPE}")
print(f"loop_count={spec.loop_count}")
print(f"hot_loop_count={spec.hot_loop_count}")
print("loop_count_source=stdin")
if spec.run_args:
print(f"run_args={' '.join(spec.run_args)}")
print(f"expected_checksum={spec.expected_checksum}")
print("implementations:")
for impl in implementations:
print(f" {impl.name}: {impl.language} ({impl.source.relative_to(root)})")
def emit_dry_run(root: Path, spec: BenchmarkSpec, implementations: list[Implementation], args: argparse.Namespace) -> None:
params = run_parameters(spec, args.mode)
print(f"{spec.name}: {TIMING_SCOPE}")
print(f"mode={params.mode}")
print(f"loop_count={params.loop_count}")
print(f"expected_checksum={params.expected_checksum}")
for impl in implementations:
build_command, skip_reasons = impl.build(root, spec, args)
print(f"{impl.name}:")
if skip_reasons:
print(f" skip: {'; '.join(skip_reasons)}")
continue
if build_command:
print(f" build: {format_command(build_command)}")
else:
print(" build: none")
print(f" stdin: {params.stdin_text.rstrip()}")
print(f" run: {format_command(impl.run(root, spec, args))}")
def run_benchmarks(root: Path, spec: BenchmarkSpec, implementations: list[Implementation], args: argparse.Namespace) -> list[dict[str, object]]:
build_dir(root).mkdir(exist_ok=True)
return [run_one(root, spec, impl, args) for impl in implementations]
def run_one(root: Path, spec: BenchmarkSpec, impl: Implementation, args: argparse.Namespace) -> dict[str, object]:
params = run_parameters(spec, args.mode)
build_command, skip_reasons = impl.build(root, spec, args)
if skip_reasons:
return skipped_result(impl, skip_reasons)
if build_command:
build = run_command(build_command)
if build.returncode != 0:
return failed_result(impl, "build failed", build)
run_command_line = impl.run(root, spec, args)
for _ in range(args.warmups):
warmup = run_command(run_command_line, params.stdin_text)
if not run_succeeded_for_params(warmup, params):
return failed_result(impl, "warmup failed", warmup)
timings: list[int] = []
for _ in range(args.repeats):
start = time.perf_counter_ns()
run = run_command(run_command_line, params.stdin_text)
elapsed = time.perf_counter_ns() - start
if not run_succeeded_for_params(run, params):
return failed_result(impl, "run failed", run)
timings.append(elapsed)
min_ms = ns_to_ms(min(timings))
median_ms = ns_to_ms(int(statistics.median(timings)))
max_ms = ns_to_ms(max(timings))
normalization_factor = params.loop_count / params.base_loop_count
return {
"name": impl.name,
"language": impl.language,
"status": "ok",
"timing_mode": params.mode,
"loop_count": params.loop_count,
"base_loop_count": params.base_loop_count,
"normalization_factor": normalization_factor,
"repeats": args.repeats,
"warmups": args.warmups,
"checksum": params.expected_checksum,
"min_ms": min_ms,
"median_ms": median_ms,
"max_ms": max_ms,
"normalized_min_ms": min_ms / normalization_factor,
"normalized_median_ms": median_ms / normalization_factor,
"normalized_max_ms": max_ms / normalization_factor,
"timing_scope": TIMING_SCOPE,
}
def skipped_result(impl: Implementation, reasons: list[str]) -> dict[str, object]:
return {"name": impl.name, "language": impl.language, "status": "skipped", "reason": "; ".join(reasons)}
def failed_result(impl: Implementation, message: str, process: subprocess.CompletedProcess[str]) -> dict[str, object]:
return {
"name": impl.name,
"language": impl.language,
"status": "failed",
"reason": message,
"returncode": process.returncode,
"stdout": process.stdout,
"stderr": process.stderr,
}
def emit_results(spec: BenchmarkSpec, results: list[dict[str, object]], as_json: bool) -> None:
if as_json:
print(
json.dumps(
{
"benchmark": spec.name,
"base_loop_count": spec.loop_count,
"timing_scope": TIMING_SCOPE,
"results": results,
},
indent=2,
sort_keys=True,
)
)
return
mode = next((str(result["timing_mode"]) for result in results if result["status"] == "ok"), "unknown")
if mode == "hot-loop":
loop_count = next((int(result["loop_count"]) for result in results if result["status"] == "ok"), spec.hot_loop_count)
print(
f"{spec.name}: {TIMING_SCOPE} "
f"(mode=hot-loop, loop_count={loop_count}, normalized_to={spec.loop_count})"
)
else:
print(f"{spec.name}: {TIMING_SCOPE}")
for result in results:
status = result["status"]
name = result["name"]
if status == "ok":
if result["timing_mode"] == "hot-loop":
print(
"{name}: total_min={min_ms:.3f}ms total_median={median_ms:.3f}ms "
"total_max={max_ms:.3f}ms normalized_median={normalized_median_ms:.3f}ms".format(
name=name,
min_ms=result["min_ms"],
median_ms=result["median_ms"],
max_ms=result["max_ms"],
normalized_median_ms=result["normalized_median_ms"],
)
)
else:
print(
"{name}: min={min_ms:.3f}ms median={median_ms:.3f}ms max={max_ms:.3f}ms".format(
name=name,
min_ms=result["min_ms"],
median_ms=result["median_ms"],
max_ms=result["max_ms"],
)
)
else:
print(f"{name}: {status} ({result['reason']})")
def run_command(command: list[str], stdin_text: str | None = None) -> subprocess.CompletedProcess[str]:
return subprocess.run(
command,
input=stdin_text,
text=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
check=False,
)
def normalized_stdout(stdout: str) -> str:
lines = [line.strip() for line in stdout.splitlines() if line.strip()]
if not lines:
return ""
return lines[-1]
def run_parameters(spec: BenchmarkSpec, mode: str) -> RunParameters:
if mode == "hot-loop":
return RunParameters(
mode=mode,
loop_count=spec.hot_loop_count,
expected_checksum=spec.hot_expected_checksum,
stdin_text=spec.hot_stdin_text,
base_loop_count=spec.loop_count,
)
return RunParameters(
mode="cold-process",
loop_count=spec.loop_count,
expected_checksum=spec.expected_checksum,
stdin_text=spec.stdin_text,
base_loop_count=spec.loop_count,
)
def run_succeeded_for_params(process: subprocess.CompletedProcess[str], params: RunParameters) -> bool:
if normalized_stdout(process.stdout) != params.expected_checksum:
return False
if params.mode == "hot-loop":
return True
return process.returncode == 0
def ns_to_ms(value: int) -> float:
return value / 1_000_000.0
def first_available(candidates: list[str]) -> str | None:
for candidate in candidates:
found = shutil.which(candidate)
if found:
return found
return None
def format_command(command: list[str]) -> str:
return " ".join(shlex_quote(part) for part in command)
def shlex_quote(value: str) -> str:
if value and all(char.isalnum() or char in "/._:-" for char in value):
return value
return "'" + value.replace("'", "'\"'\"'") + "'"

1
benchmarks/string-eq-loop/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,71 @@
# String Equality Loop Benchmark Scaffold
Release: `exp-119`.
This benchmark compares exact ASCII string content equality plus `i32`
checksum accumulation across Slovo, C, Rust, Python, Clojure, and Common Lisp/SBCL on the same machine.
It is not a published benchmark result, performance threshold, optimizer
claim, or cross-machine comparison.
## Files
- `src/main.slo`: Slovo project benchmark fixture
- `c/string_eq_loop.c`: C comparison implementation
- `rust/string_eq_loop.rs`: Rust comparison implementation
- `python/string_eq_loop.py`: Python comparison implementation
- `clojure/string_eq_loop.clj`: Clojure comparison implementation
- `common-lisp/string_eq_loop.lisp`: Common Lisp/SBCL comparison implementation
- `run.py`: build/run/timing harness
All implementations print checksum `4600001` for loop count `1000000` and
target string `omega`. Hot-loop mode uses loop count `10000000` and checksum
`46000001`. The runner supplies the loop count and target string at runtime.
## Commands
Run from the Glagol repository root:
```bash
python3 benchmarks/string-eq-loop/run.py --list
python3 benchmarks/string-eq-loop/run.py --dry-run
python3 benchmarks/string-eq-loop/run.py --only python --repeats 3 --warmups 1
python3 benchmarks/string-eq-loop/run.py --mode hot-loop --only slovo --only c --only rust
```
To include Slovo, build or point at a Glagol binary and make sure host Clang is
available:
```bash
cargo build --manifest-path compiler/Cargo.toml --bin glagol
python3 benchmarks/string-eq-loop/run.py --glagol compiler/target/debug/glagol
```
The runner skips missing C/Rust/Slovo/Clojure/SBCL toolchains where possible.
Use `--only` multiple times to select implementations:
```bash
python3 benchmarks/string-eq-loop/run.py --only slovo --only c --only rust --only clojure --only common_lisp
```
Clojure is detected with `clojure` on PATH, `CLOJURE`, or `CLOJURE_JAR`.
Common Lisp is detected with `sbcl` on PATH, `SBCL`, or `--sbcl`.
## Comparison Method
- The runner builds each implementation once before timing. The reported
numbers measure execution only, not compile time.
- Slovo timings use `glagol build`, which currently lowers to LLVM and then
invokes host `clang -O2` with `runtime/runtime.c`.
- C timings use `clang -O2 -std=c11`.
- Rust timings use `rustc -C opt-level=3 -C debuginfo=0`.
- The measured loop keeps one fixed immutable string array in local scope,
indexes it with a loop-carried `% 5` position, and compares the selected
string against a runtime-supplied ASCII target by content equality only.
Timing is cold-process local-machine evidence only. Clojure timings include
JVM and Clojure startup, while Common Lisp timings include SBCL script
startup.
Hot-loop mode is startup-amortized local evidence. It runs a larger loop count
and reports total time plus normalized time for the base `1000000` loop count.

View File

@ -0,0 +1,11 @@
{
"benchmark": "string-eq-loop",
"source_stem": "string_eq_loop",
"loop_count": 1000000,
"expected_checksum": "4600001",
"stdin": "1000000\n",
"hot_loop_count": 10000000,
"hot_expected_checksum": "46000001",
"hot_stdin": "10000000\n",
"run_args": ["omega"]
}

View File

@ -0,0 +1,39 @@
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define LOOP_COUNT 1000000
#define EXPECTED_CHECKSUM 4600001
static int32_t configured_loop_count(void) {
int32_t value = LOOP_COUNT;
if (scanf("%d", &value) != 1 || value <= 0) {
return LOOP_COUNT;
}
return value;
}
static const char *configured_target(int argc, char **argv) {
return argc > 1 ? argv[1] : "omega";
}
static int32_t string_eq_loop(int32_t limit, const char *target) {
static const char *words[5] = {"alpha", "omega", "delta", "omega", "sigma"};
int32_t i = 0;
int32_t acc = 1;
while (i < limit) {
const char *current = words[i % 5];
acc = strcmp(current, target) == 0 ? acc + 7 : acc + 3;
acc = acc > 1000000000 ? acc - 1000000000 : acc;
i = i + 1;
}
return acc;
}
int main(int argc, char **argv) {
int32_t result = string_eq_loop(configured_loop_count(), configured_target(argc, argv));
printf("%d\n", result);
return result == EXPECTED_CHECKSUM ? 0 : 1;
}

View File

@ -0,0 +1,35 @@
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(def loop-count 1000000)
(def expected-checksum 4600001)
(def words ["alpha" "omega" "delta" "omega" "sigma"])
(defn configured-loop-count []
(try
(let [line (read-line)
value (Integer/parseInt (.trim ^String line))]
(if (pos? value) value loop-count))
(catch Exception _
loop-count)))
(defn configured-target []
(or (first *command-line-args*) "omega"))
(defn string-eq-loop [limit target]
(loop [i 0
acc 1]
(if (< i limit)
(let [current (nth words (rem i 5))
next (if (= ^String current ^String target)
(+ acc 7)
(+ acc 3))
bounded (if (> next 1000000000)
(- next 1000000000)
next)]
(recur (inc i) bounded))
acc)))
(let [result (string-eq-loop (configured-loop-count) (configured-target))]
(println result)
(System/exit (if (= result expected-checksum) 0 1)))

View File

@ -0,0 +1,43 @@
(declaim (optimize (speed 3) (safety 0) (debug 0)))
(defconstant +loop-count+ 1000000)
(defconstant +expected-checksum+ 4600001)
(defparameter +words+ #("alpha" "omega" "delta" "omega" "sigma"))
(declaim (ftype (function () fixnum) configured-loop-count))
(defun configured-loop-count ()
(handler-case
(let ((line (read-line *standard-input* nil nil)))
(if line
(let ((value (parse-integer line :junk-allowed t)))
(if (> value 0) value +loop-count+))
+loop-count+))
(error () +loop-count+)))
(declaim (ftype (function () string) configured-target))
(defun configured-target ()
(or (second sb-ext:*posix-argv*) "omega"))
(declaim (ftype (function (fixnum string) fixnum) string-eq-loop))
(defun string-eq-loop (limit target)
(declare (type fixnum limit)
(type string target))
(loop with i of-type fixnum = 0
with acc of-type fixnum = 1
while (< i limit)
do (let* ((current (svref +words+ (rem i 5)))
(next (if (string= current target)
(+ acc 7)
(+ acc 3)))
(bounded (if (> next 1000000000)
(- next 1000000000)
next)))
(declare (type string current)
(type fixnum next bounded))
(setf acc bounded
i (+ i 1)))
finally (return acc)))
(let ((result (string-eq-loop (configured-loop-count) (configured-target))))
(format t "~D~%" result)
(sb-ext:exit :code (if (= result +expected-checksum+) 0 1)))

View File

@ -0,0 +1,42 @@
import sys
LOOP_COUNT = 1_000_000
EXPECTED_CHECKSUM = 4_600_001
WORDS = ["alpha", "omega", "delta", "omega", "sigma"]
def configured_loop_count() -> int:
try:
value = int(input().strip())
except (EOFError, ValueError):
return LOOP_COUNT
return value if value > 0 else LOOP_COUNT
def configured_target() -> str:
return sys.argv[1] if len(sys.argv) > 1 else "omega"
def string_eq_loop(limit: int, target: str) -> int:
i = 0
acc = 1
while i < limit:
current = WORDS[i % 5]
acc = acc + 7 if current == target else acc + 3
acc = acc - 1_000_000_000 if acc > 1_000_000_000 else acc
i += 1
return acc
def main() -> int:
result = string_eq_loop(configured_loop_count(), configured_target())
print(result)
return 0 if result == EXPECTED_CHECKSUM else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Run the local string-eq-loop benchmark scaffold."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from runner import main
if __name__ == "__main__":
raise SystemExit(main(Path(__file__).resolve().parent, sys.argv[1:]))

View File

@ -0,0 +1,48 @@
const LOOP_COUNT: i32 = 1_000_000;
const EXPECTED_CHECKSUM: i32 = 4_600_001;
const WORDS: [&str; 5] = ["alpha", "omega", "delta", "omega", "sigma"];
fn configured_loop_count() -> i32 {
let mut input = String::new();
if std::io::stdin().read_line(&mut input).is_err() {
return LOOP_COUNT;
}
input
.trim()
.parse::<i32>()
.ok()
.filter(|value| *value > 0)
.unwrap_or(LOOP_COUNT)
}
fn configured_target() -> String {
std::env::args()
.nth(1)
.unwrap_or_else(|| "omega".to_string())
}
fn string_eq_loop(limit: i32, target: &str) -> i32 {
let mut i = 0;
let mut acc = 1;
while i < limit {
let current = WORDS[(i % 5) as usize];
acc = if current == target { acc + 7 } else { acc + 3 };
acc = if acc > 1_000_000_000 {
acc - 1_000_000_000
} else {
acc
};
i += 1;
}
acc
}
fn main() {
let target = configured_target();
let result = string_eq_loop(configured_loop_count(), &target);
println!("{}", result);
std::process::exit(if result == EXPECTED_CHECKSUM { 0 } else { 1 });
}

View File

@ -0,0 +1,4 @@
[project]
name = "string-eq-loop"
source_root = "src"
entry = "main"

View File

@ -0,0 +1,65 @@
; Benchmark scaffold fixture for local-machine string-equality timing comparisons only.
; Keep LOOP_COUNT and EXPECTED_CHECKSUM aligned with the C/Rust/Python fixtures.
; The runner supplies the target through argv and the loop count through stdin
; or argv so the equality path stays runtime-configured.
(module main)
(fn loop_count () -> i32
1000000)
(fn expected_checksum () -> i32
4600001)
(fn parse_stdin_loop_count () -> (result i32 i32)
(let input (result string i32) (std.io.read_stdin_result))
(match input
((ok text)
(std.string.parse_i32_result text))
((err code)
(err i32 i32 code))))
(fn parse_arg_loop_count () -> (result i32 i32)
(std.string.parse_i32_result (std.process.arg 2)))
(fn configured_stdin_loop_count () -> i32
(let parsed_stdin (result i32 i32) (parse_stdin_loop_count))
(if (is_ok parsed_stdin)
(unwrap_ok parsed_stdin)
(loop_count)))
(fn configured_loop_count () -> i32
(let parsed_arg (result i32 i32) (parse_arg_loop_count))
(if (is_ok parsed_arg)
(unwrap_ok parsed_arg)
(configured_stdin_loop_count)))
(fn target_text () -> string
(std.process.arg 1))
(fn words () -> (array string 5)
(array string "alpha" "omega" "delta" "omega" "sigma"))
(fn string_eq_loop ((limit i32) (target string)) -> i32
(let values (array string 5) (words))
(var i i32 0)
(var acc i32 1)
(while (< i limit)
(set acc (if (= (index values (% i 5)) target)
(+ acc 7)
(+ acc 3)))
(set acc (if (> acc 1000000000)
(- acc 1000000000)
acc))
(set i (+ i 1)))
acc)
(fn main () -> i32
(let result i32 (string_eq_loop (configured_loop_count) (target_text)))
(std.io.print_i32 result)
(if (= result (expected_checksum))
0
1))
(test "string equality loop checksum is deterministic"
(= (string_eq_loop (loop_count) "omega") (expected_checksum)))

View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,73 @@
# Vec I32 Index Loop Benchmark Scaffold
Release: `exp-123`.
This benchmark compares runtime-owned `(vec i32)` indexing plus scalar
accumulation across Slovo, C, Rust, Python, Clojure, and Common Lisp/SBCL on
the same machine.
It is not a published benchmark result, performance threshold, optimizer
claim, or cross-machine comparison.
## Files
- `src/main.slo`: Slovo project benchmark fixture
- `c/vec_i32_index_loop.c`: C comparison implementation
- `rust/vec_i32_index_loop.rs`: Rust comparison implementation
- `python/vec_i32_index_loop.py`: Python comparison implementation
- `clojure/vec_i32_index_loop.clj`: Clojure comparison implementation
- `common-lisp/vec_i32_index_loop.lisp`: Common Lisp/SBCL comparison implementation
- `run.py`: build/run/timing harness
All implementations print checksum `3875007` for loop count `1000000`.
Hot-loop mode uses loop count `10000000` and checksum `38750007`. The runner
supplies the loop count at runtime so native compilers cannot fold the loop
into a constant answer.
## Commands
Run from the Glagol repository root:
```bash
python3 benchmarks/vec-i32-index-loop/run.py --list
python3 benchmarks/vec-i32-index-loop/run.py --dry-run
python3 benchmarks/vec-i32-index-loop/run.py --only python --repeats 3 --warmups 1
python3 benchmarks/vec-i32-index-loop/run.py --mode hot-loop --only slovo --only c --only rust
```
To include Slovo, build or point at a Glagol binary and make sure host Clang is
available:
```bash
cargo build --manifest-path compiler/Cargo.toml --bin glagol
python3 benchmarks/vec-i32-index-loop/run.py --glagol compiler/target/debug/glagol
```
The runner skips missing C/Rust/Slovo/Clojure/SBCL toolchains where possible.
Use `--only` multiple times to select implementations:
```bash
python3 benchmarks/vec-i32-index-loop/run.py --only slovo --only c --only rust --only clojure --only common_lisp
```
Clojure is detected with `clojure` on PATH, `CLOJURE`, or `CLOJURE_JAR`.
Common Lisp is detected with `sbcl` on PATH, `SBCL`, or `--sbcl`.
## Comparison Method
- The runner builds each implementation once before timing. The reported
numbers measure execution only, not compile time.
- Slovo timings use `glagol build`, which currently lowers to LLVM and then
invokes host `clang -O2` with `runtime/runtime.c`.
- C timings use `clang -O2 -std=c11`.
- Rust timings use `rustc -C opt-level=3 -C debuginfo=0`.
- The measured loop keeps one immutable runtime-owned `i32` vector in local
scope, indexes it with a loop-carried `% 8` position, and accumulates the
selected value into an `i32` checksum.
Timing is cold-process local-machine evidence only. Clojure timings include
JVM and Clojure startup, while Common Lisp timings include SBCL script
startup.
Hot-loop mode is startup-amortized local evidence. It runs a larger loop count
and reports total time plus normalized time for the base `1000000` loop count.

View File

@ -0,0 +1,10 @@
{
"benchmark": "vec-i32-index-loop",
"source_stem": "vec_i32_index_loop",
"loop_count": 1000000,
"expected_checksum": "3875007",
"stdin": "1000000\n",
"hot_loop_count": 10000000,
"hot_expected_checksum": "38750007",
"hot_stdin": "10000000\n"
}

View File

@ -0,0 +1,60 @@
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define LOOP_COUNT 1000000
#define EXPECTED_CHECKSUM 3875007
typedef struct {
int32_t len;
int32_t *data;
} vec_i32;
static int32_t configured_loop_count(void) {
int32_t value = LOOP_COUNT;
if (scanf("%d", &value) != 1 || value <= 0) {
return LOOP_COUNT;
}
return value;
}
static vec_i32 make_digits(void) {
vec_i32 values;
values.len = 8;
values.data = malloc(sizeof(int32_t) * (size_t)values.len);
if (values.data == NULL) {
fputs("allocation failed\n", stderr);
exit(2);
}
values.data[0] = 3;
values.data[1] = 1;
values.data[2] = 4;
values.data[3] = 1;
values.data[4] = 5;
values.data[5] = 9;
values.data[6] = 2;
values.data[7] = 6;
return values;
}
static int32_t vec_i32_index_loop(int32_t limit) {
vec_i32 digits = make_digits();
int32_t i = 0;
int32_t acc = 7;
while (i < limit) {
acc = acc + digits.data[i % digits.len];
acc = acc > 1000000000 ? acc - 1000000000 : acc;
i = i + 1;
}
free(digits.data);
return acc;
}
int main(void) {
int32_t result = vec_i32_index_loop(configured_loop_count());
printf("%d\n", result);
return result == EXPECTED_CHECKSUM ? 0 : 1;
}

View File

@ -0,0 +1,28 @@
(set! *warn-on-reflection* true)
(set! *unchecked-math* :warn-on-boxed)
(def loop-count 1000000)
(def expected-checksum 3875007)
(defn configured-loop-count []
(try
(let [line (read-line)
value (Integer/parseInt (.trim ^String line))]
(if (pos? value) value loop-count))
(catch Exception _
loop-count)))
(defn vec-i32-index-loop [limit]
(let [digits [3 1 4 1 5 9 2 6]]
(loop [i 0
acc 7]
(if (< i limit)
(let [next (+ acc (nth digits (rem i 8)))
bounded (if (> next 1000000000)
(- next 1000000000)
next)]
(recur (inc i) bounded))
acc))))
(let [result (vec-i32-index-loop (configured-loop-count))]
(println result)
(System/exit (if (= result expected-checksum) 0 1)))

View File

@ -0,0 +1,33 @@
(declaim (optimize (speed 3) (safety 0) (debug 0)))
(defconstant +loop-count+ 1000000)
(defconstant +expected-checksum+ 3875007)
(declaim (ftype (function () fixnum) configured-loop-count))
(defun configured-loop-count ()
(handler-case
(let ((line (read-line *standard-input* nil nil)))
(if line
(let ((value (parse-integer line :junk-allowed t)))
(if (> value 0) value +loop-count+))
+loop-count+))
(error () +loop-count+)))
(declaim (ftype (function (fixnum) fixnum) vec-i32-index-loop))
(defun vec-i32-index-loop (limit)
(declare (type fixnum limit))
(let ((digits #(3 1 4 1 5 9 2 6)))
(loop with i of-type fixnum = 0
with acc of-type fixnum = 7
while (< i limit)
do (let* ((next (+ acc (svref digits (rem i 8))))
(bounded (if (> next 1000000000)
(- next 1000000000)
next)))
(declare (type fixnum next bounded))
(setf acc bounded
i (+ i 1)))
finally (return acc))))
(let ((result (vec-i32-index-loop (configured-loop-count))))
(format t "~D~%" result)
(sb-ext:exit :code (if (= result +expected-checksum+) 0 1)))

View File

@ -0,0 +1,34 @@
LOOP_COUNT = 1_000_000
EXPECTED_CHECKSUM = 3_875_007
def configured_loop_count() -> int:
try:
value = int(input().strip())
except (EOFError, ValueError):
return LOOP_COUNT
return value if value > 0 else LOOP_COUNT
def vec_i32_index_loop(limit: int) -> int:
digits = [3, 1, 4, 1, 5, 9, 2, 6]
i = 0
acc = 7
while i < limit:
acc += digits[i % len(digits)]
acc = acc - 1_000_000_000 if acc > 1_000_000_000 else acc
i += 1
return acc
def main() -> int:
result = vec_i32_index_loop(configured_loop_count())
print(result)
return 0 if result == EXPECTED_CHECKSUM else 1
if __name__ == "__main__":
raise SystemExit(main())

View File

@ -0,0 +1,13 @@
#!/usr/bin/env python3
"""Run the local vec-i32-index-loop benchmark scaffold."""
from pathlib import Path
import sys
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from runner import main
if __name__ == "__main__":
raise SystemExit(main(Path(__file__).resolve().parent, sys.argv[1:]))

View File

@ -0,0 +1,40 @@
const LOOP_COUNT: i32 = 1_000_000;
const EXPECTED_CHECKSUM: i32 = 3_875_007;
fn configured_loop_count() -> i32 {
let mut input = String::new();
if std::io::stdin().read_line(&mut input).is_err() {
return LOOP_COUNT;
}
input
.trim()
.parse::<i32>()
.ok()
.filter(|value| *value > 0)
.unwrap_or(LOOP_COUNT)
}
fn vec_i32_index_loop(limit: i32) -> i32 {
let digits = vec![3, 1, 4, 1, 5, 9, 2, 6];
let mut i = 0;
let mut acc = 7;
while i < limit {
acc += digits[(i % digits.len() as i32) as usize];
acc = if acc > 1_000_000_000 {
acc - 1_000_000_000
} else {
acc
};
i += 1;
}
acc
}
fn main() {
let result = vec_i32_index_loop(configured_loop_count());
println!("{}", result);
std::process::exit(if result == EXPECTED_CHECKSUM { 0 } else { 1 });
}

View File

@ -0,0 +1,4 @@
[project]
name = "vec-i32-index-loop"
source_root = "src"
entry = "main"

View File

@ -0,0 +1,62 @@
; Benchmark scaffold fixture for local-machine vec-i32-index timing comparisons only.
; Keep LOOP_COUNT and EXPECTED_CHECKSUM aligned with the C/Rust/Python fixtures.
; The runner supplies the loop count through stdin or argv so native compilers
; cannot fold the benchmark loop into a constant answer.
(module main)
(import std.vec_i32 (empty append2 append3 at))
(fn loop_count () -> i32
1000000)
(fn expected_checksum () -> i32
3875007)
(fn parse_stdin_loop_count () -> (result i32 i32)
(let input (result string i32) (std.io.read_stdin_result))
(match input
((ok text)
(std.string.parse_i32_result text))
((err code)
(err i32 i32 code))))
(fn parse_arg_loop_count () -> (result i32 i32)
(std.string.parse_i32_result (std.process.arg 1)))
(fn configured_stdin_loop_count () -> i32
(let parsed_stdin (result i32 i32) (parse_stdin_loop_count))
(if (is_ok parsed_stdin)
(unwrap_ok parsed_stdin)
(loop_count)))
(fn configured_loop_count () -> i32
(let parsed_arg (result i32 i32) (parse_arg_loop_count))
(if (is_ok parsed_arg)
(unwrap_ok parsed_arg)
(configured_stdin_loop_count)))
(fn values () -> (vec i32)
(append2 (append3 (append3 (empty) 3 1 4) 1 5 9) 2 6))
(fn vec_i32_index_loop ((limit i32)) -> i32
(let digits (vec i32) (values))
(var i i32 0)
(var acc i32 7)
(while (< i limit)
(set acc (+ acc (at digits (% i 8))))
(set acc (if (> acc 1000000000)
(- acc 1000000000)
acc))
(set i (+ i 1)))
acc)
(fn main () -> i32
(let result i32 (vec_i32_index_loop (configured_loop_count)))
(std.io.print_i32 result)
(if (= result (expected_checksum))
0
1))
(test "vec i32 index loop checksum is deterministic"
(= (vec_i32_index_loop (loop_count)) (expected_checksum)))

View File

@ -0,0 +1 @@
build/

View File

@ -0,0 +1,73 @@
# Vec String Equality Loop Benchmark Scaffold
Release: `exp-123`.
This benchmark compares runtime-owned `(vec string)` indexing plus exact ASCII
string content equality and `i32` checksum accumulation across Slovo, C, Rust,
Python, Clojure, and Common Lisp/SBCL on the same machine.
It is not a published benchmark result, performance threshold, optimizer
claim, or cross-machine comparison.
## Files
- `src/main.slo`: Slovo project benchmark fixture
- `c/vec_string_eq_loop.c`: C comparison implementation
- `rust/vec_string_eq_loop.rs`: Rust comparison implementation
- `python/vec_string_eq_loop.py`: Python comparison implementation
- `clojure/vec_string_eq_loop.clj`: Clojure comparison implementation
- `common-lisp/vec_string_eq_loop.lisp`: Common Lisp/SBCL comparison implementation
- `run.py`: build/run/timing harness
All implementations print checksum `4600001` for loop count `1000000` and
target string `omega`. Hot-loop mode uses loop count `10000000` and checksum
`46000001`. The runner supplies the loop count and target string at runtime.
## Commands
Run from the Glagol repository root:
```bash
python3 benchmarks/vec-string-eq-loop/run.py --list
python3 benchmarks/vec-string-eq-loop/run.py --dry-run
python3 benchmarks/vec-string-eq-loop/run.py --only python --repeats 3 --warmups 1
python3 benchmarks/vec-string-eq-loop/run.py --mode hot-loop --only slovo --only c --only rust
```
To include Slovo, build or point at a Glagol binary and make sure host Clang is
available:
```bash
cargo build --manifest-path compiler/Cargo.toml --bin glagol
python3 benchmarks/vec-string-eq-loop/run.py --glagol compiler/target/debug/glagol
```
The runner skips missing C/Rust/Slovo/Clojure/SBCL toolchains where possible.
Use `--only` multiple times to select implementations:
```bash
python3 benchmarks/vec-string-eq-loop/run.py --only slovo --only c --only rust --only clojure --only common_lisp
```
Clojure is detected with `clojure` on PATH, `CLOJURE`, or `CLOJURE_JAR`.
Common Lisp is detected with `sbcl` on PATH, `SBCL`, or `--sbcl`.
## Comparison Method
- The runner builds each implementation once before timing. The reported
numbers measure execution only, not compile time.
- Slovo timings use `glagol build`, which currently lowers to LLVM and then
invokes host `clang -O2` with `runtime/runtime.c`.
- C timings use `clang -O2 -std=c11`.
- Rust timings use `rustc -C opt-level=3 -C debuginfo=0`.
- The measured loop keeps one immutable runtime-owned string vector in local
scope, indexes it with a loop-carried `% 5` position, and compares the
selected string against a runtime-supplied ASCII target by content equality
only.
Timing is cold-process local-machine evidence only. Clojure timings include
JVM and Clojure startup, while Common Lisp timings include SBCL script
startup.
Hot-loop mode is startup-amortized local evidence. It runs a larger loop count
and reports total time plus normalized time for the base `1000000` loop count.

View File

@ -0,0 +1,11 @@
{
"benchmark": "vec-string-eq-loop",
"source_stem": "vec_string_eq_loop",
"loop_count": 1000000,
"expected_checksum": "4600001",
"stdin": "1000000\n",
"hot_loop_count": 10000000,
"hot_expected_checksum": "46000001",
"hot_stdin": "10000000\n",
"run_args": ["omega"]
}

Some files were not shown because too many files have changed in this diff Show More