Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Whenever you are asked to edit AGENTS.md, do not take these as litteral, step-by

- Run tests after code changes that can affect behavior. Do not ask the user to run tests.
- Do not run tests for instruction-only edits, documentation-only edits, or git-only changes (branch/worktree/checkout/reset).
- When running tests, run `cargo test --no-run` in the same profile (debug/release) first.
- When running tests, use release profile and run `cargo test --release --no-run` first.
- The user does not care about phrasing or framing; do not propose rephrasings or reframings for solutions or fixes, and describe them objectively.
- Never propose fixes that change semantics; correctness is defined by the current semantics, and a semantic change is not an acceptable bug fix unless the user explicitly requests it.
- Treat semantics-breaking changes as catastrophic to correctness; even when explicitly requested, treat them as severe, warn strongly, and resist by default.
Expand All @@ -60,6 +60,8 @@ Whenever you are asked to edit AGENTS.md, do not take these as litteral, step-by
- When the user has already requested a task, proceed without asking for permission or prompting for confirmation; avoid “say the word” or similar re-ask phrasing.
- When objective standards in this file or the system/developer instructions already determine the correct action, follow them without asking the user for preference or permission; only ask when those standards are insufficient to decide.
- User preference is not the deciding factor; follow objective standards relevant to the task to determine the best action. Do not ask for preference when standards already decide.
- stop giving me unprompted status updates unless you litterally have no pending work.
- If yoiu do have pending work, do NOT give a status update, just do the pending work.

## Debugging and Diagnosis Protocol

Expand All @@ -78,6 +80,9 @@ Whenever you are asked to edit AGENTS.md, do not take these as litteral, step-by
- Refactors must be deletion-first: remove the obsolete implementation as soon as a replacement is chosen.
- Never keep parallel implementations for the same behavior.
- Do not perform incremental replacements; complete each replacement in a single change set.
- Never maintain backward-compatibility paths, adapters, or compatibility shims.
- Never keep legacy execution features once a replacement exists.
- Always apply destructive updates during architecture replacement: remove the old implementation and route all execution through the new one.

## PRIMARY EDICT: Tests Must Verify Correct Behavior

Expand Down Expand Up @@ -184,12 +189,12 @@ Layout rule: avoid multi-column exposition; keep text single-column and give dia

## CRITICAL: Always Use Timeouts When Running Tests

**NEVER run tests without a timeout.** If tests don't ALL finish in less than 30 seconds, there's an infinite loop bug.
**NEVER run tests without a timeout.** If tests don't ALL finish in less than 60 seconds, there's an infinite loop bug.

**Always use:**
```bash
cargo test --no-run 2>&1
timeout 30 cargo test 2>&1
cargo test --release --no-run 2>&1
timeout 60 cargo test --release 2>&1
```

**Never use:**
Expand All @@ -203,7 +208,7 @@ This applies to ALL test commands - full suite, filtered tests, individual tests

Baseline to preserve during the parallelism port (I ran this myself on commit `8eab6f01f1be85aa5a5790afb84a604e6948b336`):
- Built release tests with `cargo test --release --no-run`.
- Ran `timeout 30 cargo test --release --lib engine::tests::program_synth_flip_query_emits_answer -- --exact --nocapture`.
- Ran `timeout 60 cargo test --release --lib engine::tests::program_synth_flip_query_emits_answer -- --exact --nocapture`.
- Result: pass.

## TDD Test Coverage Requirements
Expand Down
27 changes: 23 additions & 4 deletions benches/perf_corpus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@

use criterion::{black_box, criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
use rwlog::perf_corpus::{
apply_filters, case_bench_id, prepare_case, run_prepared, sort_cases, summary_string,
validate_cases, CorpusCase, CorpusFilters, RunMode,
apply_filters, case_bench_id, compile_prepared, prepare_case, run_compiled, run_prepared,
sort_cases, summary_string, validate_cases, CorpusCase, CorpusFilters, RunMode,
};
use std::sync::OnceLock;

Expand Down Expand Up @@ -55,8 +55,8 @@ fn bench_corpus_execute(c: &mut Criterion) {
case,
|b, case| {
b.iter_batched(
|| prepare_case(case),
|prepared| black_box(run_prepared(case, prepared)),
|| compile_prepared(prepare_case(case)),
|engine| black_box(run_compiled(case, engine)),
BatchSize::SmallInput,
);
},
Expand All @@ -80,6 +80,24 @@ fn bench_corpus_parse(c: &mut Criterion) {
group.finish();
}

fn bench_corpus_compile(c: &mut Criterion) {
let mut group = c.benchmark_group("corpus_compile");
for case in selected_cases() {
group.bench_with_input(
BenchmarkId::new(mode_suffix(case.mode), case_bench_id(case)),
case,
|b, case| {
b.iter_batched(
|| prepare_case(case),
|prepared| black_box(compile_prepared(prepared)),
BatchSize::SmallInput,
);
},
);
}
group.finish();
}

fn bench_corpus_end_to_end(c: &mut Criterion) {
for mode in [RunMode::FirstAnswer, RunMode::FirstN(1), RunMode::Exhaust] {
let mode_name = mode_suffix(mode);
Expand Down Expand Up @@ -107,6 +125,7 @@ criterion_group!(
perf_corpus_benches,
bench_corpus_execute,
bench_corpus_parse,
bench_corpus_compile,
bench_corpus_end_to_end
);
criterion_main!(perf_corpus_benches);
12 changes: 7 additions & 5 deletions src/bin/perf_corpus_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;

use rwlog::perf_corpus::{
apply_filters, environment_fingerprint, load_cases, prepare_case, run_prepared, sort_cases,
CorpusCase, CorpusFilters, EnvironmentFingerprint, TierFilter,
apply_filters, compile_prepared, environment_fingerprint, load_cases, prepare_case,
run_compiled, sort_cases, CorpusCase, CorpusFilters, EnvironmentFingerprint, TierFilter,
};
use serde::Deserialize;
use serde::Serialize;
Expand Down Expand Up @@ -153,16 +153,18 @@ fn run_samples_adaptive(
let max_samples = max_samples.max(min_samples);
for _ in 0..cfg.warmup {
let prepared = prepare_case(case);
let _ = run_prepared(case, prepared);
let engine = compile_prepared(prepared);
let _ = run_compiled(case, engine);
}

let mut out = Vec::with_capacity(max_samples);
let mut remaining = min_samples;
while out.len() < max_samples {
for _ in 0..remaining {
let start = Instant::now();
let prepared = prepare_case(case);
let _ = run_prepared(case, prepared);
let engine = compile_prepared(prepared);
let start = Instant::now();
let _ = run_compiled(case, engine);
out.push(start.elapsed().as_secs_f64() * 1_000_000.0);
}
if out.len() >= max_samples {
Expand Down
12 changes: 7 additions & 5 deletions src/bin/perf_corpus_recommend_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;

use rwlog::perf_corpus::{
apply_filters, environment_fingerprint, load_cases, prepare_case, run_prepared, sort_cases,
CorpusCase, CorpusFilters, EnvironmentFingerprint, TierFilter,
apply_filters, compile_prepared, environment_fingerprint, load_cases, prepare_case,
run_compiled, sort_cases, CorpusCase, CorpusFilters, EnvironmentFingerprint, TierFilter,
};
use serde::Deserialize;
use serde::Serialize;
Expand Down Expand Up @@ -88,14 +88,16 @@ fn quick_cases(filter: Option<&str>) -> Vec<CorpusCase> {
fn run_samples(case: &CorpusCase, samples: usize, warmup: usize) -> Vec<f64> {
for _ in 0..warmup {
let prepared = prepare_case(case);
let _ = run_prepared(case, prepared);
let engine = compile_prepared(prepared);
let _ = run_compiled(case, engine);
}

let mut out = Vec::with_capacity(samples);
for _ in 0..samples {
let start = Instant::now();
let prepared = prepare_case(case);
let _ = run_prepared(case, prepared);
let engine = compile_prepared(prepared);
let start = Instant::now();
let _ = run_compiled(case, engine);
out.push(start.elapsed().as_secs_f64() * 1_000_000.0);
}
out.sort_by(|a, b| a.partial_cmp(b).expect("finite float"));
Expand Down
23 changes: 19 additions & 4 deletions src/bin/perf_corpus_run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;

use rwlog::perf_corpus::{
apply_filters, environment_fingerprint, load_cases, prepare_case, run_prepared_with_stats,
sort_cases, CorpusCase, CorpusFilters, EnvironmentFingerprint, ExecutionCounters,
apply_filters, compile_prepared, environment_fingerprint, load_cases, prepare_case,
run_compiled_with_stats, run_prepared_with_stats, sort_cases, CorpusCase, CorpusFilters,
EnvironmentFingerprint, ExecutionCounters,
};
use serde::Serialize;
use std::collections::BTreeMap;
Expand All @@ -12,6 +13,7 @@ use std::time::Instant;
#[derive(Clone, Copy, Debug)]
enum Phase {
Parse,
Compile,
Execute,
EndToEnd,
}
Expand All @@ -20,9 +22,10 @@ impl Phase {
fn from_arg(s: &str) -> Self {
match s {
"parse" => Phase::Parse,
"compile" => Phase::Compile,
"execute" => Phase::Execute,
"end_to_end" => Phase::EndToEnd,
other => panic!("--phase must be parse|execute|end_to_end, got '{other}'"),
other => panic!("--phase must be parse|compile|execute|end_to_end, got '{other}'"),
}
}
}
Expand All @@ -31,6 +34,7 @@ impl Phase {
fn as_str(self) -> &'static str {
match self {
Phase::Parse => "parse",
Phase::Compile => "compile",
Phase::Execute => "execute",
Phase::EndToEnd => "end_to_end",
}
Expand Down Expand Up @@ -303,10 +307,21 @@ fn main() {
std::hint::black_box(prepared);
acc.push_zeros();
}
Phase::Compile => {
let prepared = prepare_case(case);
let mid = Instant::now();
let engine = compile_prepared(prepared);
std::hint::black_box(engine);
acc.push_zeros();
let elapsed = mid.elapsed();
samples_ns.push(elapsed.as_nanos() as u64);
continue;
}
Phase::Execute => {
let prepared = prepare_case(case);
let engine = compile_prepared(prepared);
let mid = Instant::now();
let stats = run_prepared_with_stats(case, prepared);
let stats = run_compiled_with_stats(case, engine);
last_answers = stats.answers;
acc.push(&stats.counters);
let elapsed = mid.elapsed();
Expand Down
23 changes: 1 addition & 22 deletions src/bin/size_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use rwlog::chr;
use rwlog::drop_fresh;
use rwlog::factors;
use rwlog::nf;
use rwlog::node;
use rwlog::rel;
use rwlog::work::{self, CallKey, ComposeWork, Env, FixWork, MeetWork, PipeWork, Table, Tables};
use std::mem::size_of;
Expand Down Expand Up @@ -53,16 +52,6 @@ fn main() {
println!("\n--- Rel ---");
println!(" Rel<C>: {:>4} bytes", size_of::<rel::Rel<C>>());

println!("\n--- Node ---");
println!(
" Node<C>: {:>4} bytes",
size_of::<node::Node<C>>()
);
println!(
" NodeStep<C>: {:>4} bytes",
size_of::<node::NodeStep<C>>()
);

println!("\n--- Work Enum ---");
println!(
" Work<C>: {:>4} bytes",
Expand Down Expand Up @@ -122,11 +111,6 @@ fn main() {
size_of::<Box<work::Work<C>>>(),
size_of::<work::Work<C>>()
);
println!(
" Box<Node<C>>: {:>4} bytes (heap: {})",
size_of::<Box<node::Node<C>>>(),
size_of::<node::Node<C>>()
);
println!(
" Arc<NF<C>>: {:>4} bytes (heap: ~{})",
size_of::<std::sync::Arc<nf::NF<C>>>(),
Expand Down Expand Up @@ -194,16 +178,11 @@ fn main() {

println!("\n--- Key Sizes for Allocation Buckets ---");
let work_size = size_of::<work::Work<C>>();
let node_size = size_of::<node::Node<C>>();
let nf_size = size_of::<nf::NF<C>>();
println!(" Box<Work> heap allocation: {} bytes", work_size);
println!(" Box<Node> heap allocation: {} bytes", node_size);
println!(" NF<C> (inline, not boxed): {} bytes", nf_size);
println!("\n Allocation bucket mapping:");
println!(
" 129-256 range could be: Node<C> ({}) or NF<C> ({})?",
node_size, nf_size
);
println!(" 129-256 range could be: NF<C> ({})", nf_size);
println!(
" 513-1024 range could be: Work<C> ({}) or PipeWork<C> ({})?",
work_size,
Expand Down
Loading