cnf: cache the ISOP cover of each distinct cut function - #10
Open
TrevorHansen wants to merge 1 commit into
Open
Conversation
Cnf_ComputeClauses() calls Kit_TruthIsop() twice for every non-AND cone it turns into clauses, once for the function and once for its complement, and computes both covers from scratch every time. A cover depends on nothing but the truth table and the leaf count, and the cuts of a bit-blasted circuit are a handful of adder, mux and xor functions repeated across the netlist, so nearly all of that is recomputation. Key the covers by (truth table, leaf count) in a table that lives for one Cnf_DeriveFastClauses() call, and replay a stored cover cube for cube. Mf_ManDeriveCnfs() does the same thing where the mapper has already assigned each truth function an id. The clauses come out of the same cubes in the same order, so the CNF is unchanged. Over bit-vector queries the covers are reused nearly every time: 7.0M cover requests over 42 distinct functions on one 512-bit arithmetic query, 3.2M over 26 on a second and 2.4M over 196 on a third, with the whole table a few hundred ints. CNF derivation on those three goes 2528 -> 1643 ms, 1244 -> 754 ms and 793 -> 448 ms, a 35-44% cut, and peak memory is unchanged. Cnf_ComputeClauses() keeps its signature and its uncached behaviour for the callers outside this file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cnf_ComputeClauses()callsKit_TruthIsop()twice for every non-AND cone it turns into clauses — once for the function and once for its complement — and computes both covers from scratch every time. A cover depends on nothing but the truth table and the leaf count, and the cuts of a bit-blasted circuit are a handful of adder, mux and xor functions repeated across the netlist, so nearly all of that work is recomputation. Profiling STP's CNF generation atCnf_DeriveFastputs the ISOP family —Kit_TruthIsop5_rec,Kit_TruthIsop_rec,Kit_TruthCofactor0/1,Kit_TruthVarInSupport— at 38-45% of the generation phase.This keys the covers by
(truth table, leaf count)in a table that lives for oneCnf_DeriveFastClauses()call, and replays a stored cover cube for cube.Mf_ManDeriveCnfs()does the same thing one package over, where the mapper has already assigned each truth function an id;Cnf_DeriveFasthas no such id, so the truth words themselves are the key (Vec_Mem_twith a two-word entry, the same structure the mapper hashes truth tables in).Cnf_ComputeClauses()keeps its signature and its uncached behaviour: the callers outside this file derive clauses for one cone at a time, where there is nothing to reuse.The CNF is unchanged
The clauses are pushed from the same cubes in the same order, so the output is identical by construction rather than by tolerance. Checked against an unpatched build over SMT-LIB QF_BV, comparing sha256 of the emitted CNF: 52/52 files byte-identical at
--cnf-generation-effort very-low(the setting that reachesCnf_DeriveFast), and 52/52 byte-identical atmedium, which routes throughCnf_Deriveinstead and so is a control on the shared code in this file. A 12-file subset was rerun against the final binary: 12/12 and 12/12.How much reuse there is
Instrumented counts for one CNF derivation,
very-low:rw_rule_candidate_vmcai_2022_bw512_11compose.s4._bit8_na6_nr4_pairedponylink-slaveTXlen-unsat-unrolled-nomemTwo hundred functions is the worst case seen; the table never exceeds a few hundred ints.
What it costs and saves
Release build, GCC 12, medians of five interleaved runs for the stage times and of three for the instruction counts:
rw_rule_candidate_vmcai_2022_bw512_11compose.s4._bit8_na6_nr4_pairedponylink-slaveTXlen-unsat-unrolled-nomemThe instruction counts fall further than the times because the removed work has an unusually high IPC (roughly 3, against ~1 for the run as a whole): a tight recursion over one 64-bit word is cheap per instruction next to the hash-table traffic around it. The stage times are the honest number.
The one memory row that moves is an allocator artifact, not the table. Rebuilding with the table allocated but never consulted reproduces the unpatched peak exactly, and setting the allocator's purge delay to zero makes the two builds agree to 0.1% (884.2 MB against 884.3 MB). The faster run simply reaches its peak before deferred purging has returned as much; true demand is unchanged, which the 135-to-706 ints of table already imply.
Scope
The cache is created and freed inside
Cnf_DeriveFastClauses()— nothing global, nothing that outlives a derivation.Cnf_CutCountClauses()has the same double-ISOP pattern but is only reachable from the diagnosticCnf_CountCnfSize(), and is left alone.