From 8c04e5266c1819b21e2656dd2083948ecf914082 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Tue, 19 Aug 2025 05:09:16 +0000 Subject: [PATCH] Use commutative hashing instead of expensive allocation and sorting For one of our large circuits, this improves the `OptMergePass` runtime from about 150s to about 130s. It's also simpler code. --- passes/opt/opt_merge.cc | 50 +++++++++++++++-------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) diff --git a/passes/opt/opt_merge.cc b/passes/opt/opt_merge.cc index f9ee7ba40..ba8168e74 100644 --- a/passes/opt/opt_merge.cc +++ b/passes/opt/opt_merge.cc @@ -44,17 +44,16 @@ struct OptMergeWorker CellTypes ct; int total_count; - static vector> sorted_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b) + static Hasher hash_pmux_in(const SigSpec& sig_s, const SigSpec& sig_b, Hasher h) { int s_width = GetSize(sig_s); int width = GetSize(sig_b) / s_width; - vector> sb_pairs; + hashlib::commutative_hash comm; for (int i = 0; i < s_width; i++) - sb_pairs.push_back(pair(sig_s[i], sig_b.extract(i*width, width))); + comm.eat(hash_ops>::hash({sig_s[i], sig_b.extract(i*width, width)})); - std::sort(sb_pairs.begin(), sb_pairs.end()); - return sb_pairs; + return comm.hash_into(h); } static void sort_pmux_conn(dict &conn) @@ -86,12 +85,10 @@ struct OptMergeWorker // (builtin || stdcell) && (unary || binary) && symmetrical if (cell->type.in(ID($and), ID($or), ID($xor), ID($xnor), ID($add), ID($mul), ID($logic_and), ID($logic_or), ID($_AND_), ID($_OR_), ID($_XOR_))) { - std::array inputs = { - assign_map(cell->getPort(ID::A)), - assign_map(cell->getPort(ID::B)) - }; - std::sort(inputs.begin(), inputs.end()); - h = hash_ops>::hash_into(inputs, h); + hashlib::commutative_hash comm; + comm.eat(hash_ops::hash(assign_map(cell->getPort(ID::A)))); + comm.eat(hash_ops::hash(assign_map(cell->getPort(ID::B)))); + h = comm.hash_into(h); } else if (cell->type.in(ID($reduce_xor), ID($reduce_xnor))) { SigSpec a = assign_map(cell->getPort(ID::A)); a.sort(); @@ -103,40 +100,29 @@ struct OptMergeWorker } else if (cell->type == ID($pmux)) { SigSpec sig_s = assign_map(cell->getPort(ID::S)); SigSpec sig_b = assign_map(cell->getPort(ID::B)); - for (const auto& [s_bit, b_chunk] : sorted_pmux_in(sig_s, sig_b)) { - h = s_bit.hash_into(h); - h = b_chunk.hash_into(h); - } + h = hash_pmux_in(sig_s, sig_b, h); h = assign_map(cell->getPort(ID::A)).hash_into(h); } else { - std::vector> conns; - for (const auto& conn : cell->connections()) { - conns.push_back(conn); + hashlib::commutative_hash comm; + for (const auto& [port, sig] : cell->connections()) { + if (cell->output(port)) + continue; + comm.eat(hash_ops>::hash({port, assign_map(sig)})); } - std::sort(conns.begin(), conns.end()); - for (const auto& [port, sig] : conns) { - if (!cell->output(port)) { - h = port.hash_into(h); - h = assign_map(sig).hash_into(h); - } - } - + h = comm.hash_into(h); if (RTLIL::builtin_ff_cell_types().count(cell->type)) h = initvals(cell->getPort(ID::Q)).hash_into(h); - } return h; } static Hasher hash_cell_parameters(const RTLIL::Cell *cell, Hasher h) { - using Paramvec = std::vector>; - Paramvec params; + hashlib::commutative_hash comm; for (const auto& param : cell->parameters) { - params.push_back(param); + comm.eat(hash_ops>::hash(param)); } - std::sort(params.begin(), params.end()); - return hash_ops::hash_into(params, h); + return comm.hash_into(h); } Hasher hash_cell_function(const RTLIL::Cell *cell, Hasher h) const