You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Both trees mark Clause with #[repr(align(64))], but inline literal capacity and field order diverge. v0.3.2 still uses SmallVec<[Lit; 4]> with metadata-first layout; main reorders fields and uses SmallVec<[Lit; 8]> so typical learned clauses stay in one cache line with no heap spill.
Layout comparison
v0.3.2 (oxiz-sat/src/clause.rs)
#[repr(align(64))]pubstructClause{pubactivity:f64,// metadata firstpublearned:bool,publbd:u32,pubdeleted:bool,publits:SmallVec<[Lit;4]>,// spills at ≥5 litspubtier:ClauseTier,pubusage_count:u32,}
Comment says metadata is kept hot at the front so propagation touches one line before reading literals — but any clause with 5+ lits pays a heap indirection on every watch/BCP touch of lits.
Layout probe (type layout is compile-time; same at all opt levels):
inline N
SmallVec size
Clause size
4
24B
48B
5–6
32B
56B
7–8
40B
64B (1 cache line)
9–10
48B
64B
[Lit;7] and [Lit;8] are the same SmallVec footprint (padding); main takes the free 8th slot.
Main also has assert!(size_of::<Clause>() <= 64) so the invariant can’t regress silently.
Why it matters
Structured instances learn longer clauses than random-3-SAT. With [Lit;4], most 5–8 lit clauses heap-allocate → ~2 cache misses per clause touch vs 1 when inline.
Main measurements (00bd400d):
instance
before ([Lit;4])
after ([Lit;7] then 8)
Urquhart-s3-b8
3.5s
0.67s (−81%, 5.2×)
longmult15
13.1s
10.4s (−21%)
uuf200 corpus
35.3s
36.2s (+2.5%, short clauses)
Correct on 75 cadical/test/cnf instances; sat lib tests green.
This is pure layout — no API change — and is independent of lucky / lazy-HBR / opt-level gaps (#35, #38, #37).
Commits on main
00bd400d perf(sat): SmallVec<[Lit;7]> + field reorder — eliminates heap spills
5d2ae229 perf(sat): use SmallVec<[Lit;8]> — free over [Lit;7] (same 64B struct)
Suggested direction
Put lits: SmallVec<[Lit; 8]>first in Clause
Keep #[repr(align(64))] and add size_of::<Clause>() <= 64 test
Re-bench Urquhart / longmult / industrial CNF (and anything from [perf] sat #35)
No semantic change expected — only cache behaviour.
Summary
Both trees mark
Clausewith#[repr(align(64))], but inline literal capacity and field order diverge. v0.3.2 still usesSmallVec<[Lit; 4]>with metadata-first layout; main reorders fields and usesSmallVec<[Lit; 8]>so typical learned clauses stay in one cache line with no heap spill.Layout comparison
v0.3.2 (
oxiz-sat/src/clause.rs)Comment says metadata is kept hot at the front so propagation touches one line before reading literals — but any clause with 5+ lits pays a heap indirection on every watch/BCP touch of
lits.main (
00bd400d,5d2ae229)Layout probe (type layout is compile-time; same at all opt levels):
[Lit;7]and[Lit;8]are the same SmallVec footprint (padding); main takes the free 8th slot.Main also has
assert!(size_of::<Clause>() <= 64)so the invariant can’t regress silently.Why it matters
Structured instances learn longer clauses than random-3-SAT. With
[Lit;4], most 5–8 lit clauses heap-allocate → ~2 cache misses per clause touch vs 1 when inline.Main measurements (
00bd400d):[Lit;4])[Lit;7]then 8)Correct on 75 cadical/test/cnf instances; sat lib tests green.
This is pure layout — no API change — and is independent of lucky / lazy-HBR / opt-level gaps (#35, #38, #37).
Commits on main
00bd400dperf(sat): SmallVec<[Lit;7]> + field reorder — eliminates heap spills5d2ae229perf(sat): use SmallVec<[Lit;8]> — free over [Lit;7] (same 64B struct)Suggested direction
lits: SmallVec<[Lit; 8]>first inClause#[repr(align(64))]and addsize_of::<Clause>() <= 64testNo semantic change expected — only cache behaviour.