diff --git a/src/aig/aig/aigTable.c b/src/aig/aig/aigTable.c index 6c2463b4d..a0818b3a8 100644 --- a/src/aig/aig/aigTable.c +++ b/src/aig/aig/aigTable.c @@ -36,14 +36,25 @@ ABC_NAMESPACE_IMPL_START // terms multiply a 0/1 flag by a small constant. Doing those two in unsigned // arithmetic is defined for every Id and keeps the result a function of the // operands alone. -static unsigned long Aig_Hash( Aig_Obj_t * pObj, int TableSize ) +// +// The mixed key is reduced to a bucket with two multiplies rather than with a +// remainder, which is a hardware divide by a runtime value on the hottest path +// this package has. The key is multiplied by an odd 64-bit constant and the +// high half of the product taken, which every input bit has had an effect on; +// that is then mapped onto the table by multiplying it by the table size and +// taking the high half again. Neither step needs the size to be a power of +// two: nTableSize is a public field and a client may size the table itself. +#define AIG_HASH_MULT ABC_CONST(0x9E3779B97F4A7C15) + +static unsigned Aig_Hash( Aig_Obj_t * pObj, int TableSize ) { - unsigned long Key = Aig_ObjIsExor(pObj) * 1699; + unsigned Key = Aig_ObjIsExor(pObj) * 1699, Hash; Key ^= (unsigned)Aig_ObjFanin0(pObj)->Id * 7937u; Key ^= (unsigned)Aig_ObjFanin1(pObj)->Id * 2971u; Key ^= Aig_ObjFaninC0(pObj) * 911; Key ^= Aig_ObjFaninC1(pObj) * 353; - return Key % TableSize; + Hash = (unsigned)(((word)Key * AIG_HASH_MULT) >> 32); + return (unsigned)(((word)Hash * (word)(unsigned)TableSize) >> 32); } // returns the place where this node is stored (or should be stored) @@ -76,38 +87,34 @@ static Aig_Obj_t ** Aig_TableFind( Aig_Man_t * p, Aig_Obj_t * pObj ) ***********************************************************************/ void Aig_TableResize( Aig_Man_t * p ) { - Aig_Obj_t * pEntry, * pNext; - Aig_Obj_t ** pTableOld, ** ppPlace; - int nTableSizeOld, Counter, i; - abctime clk; + Aig_Obj_t * pEntry; + Aig_Obj_t ** ppPlace; + int Counter, i; assert( p->pTable != NULL ); -clk = Abc_Clock(); - // save the old table - pTableOld = p->pTable; - nTableSizeOld = p->nTableSize; - // get the new table - p->nTableSize = Abc_PrimeCudd( 2 * Aig_ManNodeNum(p) ); + // The table is rebuilt from vObjs rather than from the old buckets, so + // the old array can be freed before the new one is allocated: the + // resize transient is max(old, new) instead of their sum, and the + // sweep reads the nodes sequentially where chain-chasing did not. + // Membership is unchanged -- the table holds exactly the AND and EXOR + // nodes, which is the invariant the Counter assert below has always + // stated, and the hash is recomputed from each node's fanins either + // way. Stale pNext values from the freed chains are never read: a + // node's pNext is only walked once it is in the new table, and it is + // nulled at insertion. + ABC_FREE( p->pTable ); + p->nTableSize = Abc_PrimeCudd( 2 * Aig_ManNodeNum(p) ); p->pTable = ABC_ALLOC( Aig_Obj_t *, p->nTableSize ); memset( p->pTable, 0, sizeof(Aig_Obj_t *) * p->nTableSize ); - // rehash the entries from the old table Counter = 0; - for ( i = 0; i < nTableSizeOld; i++ ) - for ( pEntry = pTableOld[i], pNext = pEntry? pEntry->pNext : NULL; - pEntry; pEntry = pNext, pNext = pEntry? pEntry->pNext : NULL ) + Aig_ManForEachNode( p, pEntry, i ) { - // get the place where this entry goes in the table ppPlace = Aig_TableFind( p, pEntry ); assert( *ppPlace == NULL ); // should not be there - // add the entry to the list *ppPlace = pEntry; pEntry->pNext = NULL; Counter++; } assert( Counter == Aig_ManNodeNum(p) ); -// printf( "Increasing the structural table size from %6d to %6d. ", nTableSizeOld, p->nTableSize ); -// ABC_PRT( "Time", Abc_Clock() - clk ); - // replace the table and the parameters - ABC_FREE( pTableOld ); } /**Function************************************************************* diff --git a/src/aig/gia/giaHash.c b/src/aig/gia/giaHash.c index 063cfd31a..e796e0bde 100644 --- a/src/aig/gia/giaHash.c +++ b/src/aig/gia/giaHash.c @@ -31,6 +31,12 @@ ABC_NAMESPACE_IMPL_START /// FUNCTION DEFINITIONS /// //////////////////////////////////////////////////////////////////////// +// The mixed key is reduced to a bucket with two multiplies rather than with a +// remainder, for the reason given over Aig_Hash() in aigTable.c: a remainder +// by a runtime table size is a hardware divide on every lookup and every +// insert, and this reduction needs no power-of-two table to avoid it. +#define GIA_HASH_MULT ABC_CONST(0x9E3779B97F4A7C15) + /**Function************************************************************* Synopsis [Returns the place where this node is stored (or should be stored).] @@ -44,12 +50,13 @@ ABC_NAMESPACE_IMPL_START ***********************************************************************/ static inline int Gia_ManHashOne( int iLit0, int iLit1, int iLitC, int TableSize ) { - unsigned Key = iLitC * 2011; + unsigned Key = iLitC * 2011, Hash; Key += Abc_Lit2Var(iLit0) * 7937; Key += Abc_Lit2Var(iLit1) * 2971; Key += Abc_LitIsCompl(iLit0) * 911; Key += Abc_LitIsCompl(iLit1) * 353; - return (int)(Key % TableSize); + Hash = (unsigned)(((word)Key * GIA_HASH_MULT) >> 32); + return (int)(((word)Hash * (word)(unsigned)TableSize) >> 32); } static inline int * Gia_ManHashFind( Gia_Man_t * p, int iLit0, int iLit1, int iLitC ) {