You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							1053 lines
						
					
					
						
							27 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							1053 lines
						
					
					
						
							27 KiB
						
					
					
				| /**CFile*********************************************************************** | |
|  | |
|   FileName    [cuddCache.c] | |
|  | |
|   PackageName [cudd] | |
|  | |
|   Synopsis    [Functions for cache insertion and lookup.] | |
|  | |
|   Description [Internal procedures included in this module: | |
| 		<ul> | |
| 		<li> cuddInitCache() | |
| 		<li> cuddCacheInsert() | |
| 		<li> cuddCacheInsert2() | |
| 		<li> cuddCacheLookup() | |
| 		<li> cuddCacheLookupZdd() | |
| 		<li> cuddCacheLookup2() | |
| 		<li> cuddCacheLookup2Zdd() | |
| 		<li> cuddConstantLookup() | |
| 		<li> cuddCacheProfile() | |
| 		<li> cuddCacheResize() | |
| 		<li> cuddCacheFlush() | |
| 		<li> cuddComputeFloorLog2() | |
| 		</ul> | |
| 	    Static procedures included in this module: | |
| 		<ul> | |
| 		</ul> ] | |
|  | |
|   SeeAlso     [] | |
|  | |
|   Author      [Fabio Somenzi] | |
|  | |
|   Copyright   [Copyright (c) 1995-2012, Regents of the University of Colorado | |
|  | |
|   All rights reserved. | |
|  | |
|   Redistribution and use in source and binary forms, with or without | |
|   modification, are permitted provided that the following conditions | |
|   are met: | |
|  | |
|   Redistributions of source code must retain the above copyright | |
|   notice, this list of conditions and the following disclaimer. | |
|  | |
|   Redistributions in binary form must reproduce the above copyright | |
|   notice, this list of conditions and the following disclaimer in the | |
|   documentation and/or other materials provided with the distribution. | |
|  | |
|   Neither the name of the University of Colorado nor the names of its | |
|   contributors may be used to endorse or promote products derived from | |
|   this software without specific prior written permission. | |
|  | |
|   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
|   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
|   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
|   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
|   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
|   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
|   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
|   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |
|   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
|   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |
|   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
|   POSSIBILITY OF SUCH DAMAGE.] | |
|  | |
| ******************************************************************************/ | |
| 
 | |
| #include "util.h" | |
| #include "cuddInt.h" | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Constant declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| #ifdef DD_CACHE_PROFILE | |
| #define DD_HYSTO_BINS 8 | |
| #endif | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Stucture declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Type declarations                                                         */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Variable declarations                                                     */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| #ifndef lint | |
| static char rcsid[] DD_UNUSED = "$Id: cuddCache.c,v 1.36 2012/02/05 01:07:18 fabio Exp $"; | |
| #endif | |
|  | |
| /*---------------------------------------------------------------------------*/ | |
| /* Macro declarations                                                        */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /**AutomaticStart*************************************************************/ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Static function prototypes                                                */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /**AutomaticEnd***************************************************************/ | |
| 
 | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of exported functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of internal functions                                          */ | |
| /*---------------------------------------------------------------------------*/ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Initializes the computed table.] | |
|  | |
|   Description [Initializes the computed table. It is called by | |
|   Cudd_Init. Returns 1 in case of success; 0 otherwise.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [Cudd_Init] | |
|  | |
| ******************************************************************************/ | |
| int | |
| cuddInitCache( | |
|   DdManager * unique /* unique table */, | |
|   unsigned int cacheSize /* initial size of the cache */, | |
|   unsigned int maxCacheSize /* cache size beyond which no resizing occurs */) | |
| { | |
|     int i; | |
|     unsigned int logSize; | |
| #ifndef DD_CACHE_PROFILE | |
|     DdNodePtr *mem; | |
|     ptruint offset; | |
| #endif | |
|  | |
|     /* Round cacheSize to largest power of 2 not greater than the requested | |
|     ** initial cache size. */ | |
|     logSize = cuddComputeFloorLog2(ddMax(cacheSize,unique->slots/2)); | |
|     cacheSize = 1 << logSize; | |
|     unique->acache = ALLOC(DdCache,cacheSize+1); | |
|     if (unique->acache == NULL) { | |
| 	unique->errorCode = CUDD_MEMORY_OUT; | |
| 	return(0); | |
|     } | |
|     /* If the size of the cache entry is a power of 2, we want to | |
|     ** enforce alignment to that power of two. This happens when | |
|     ** DD_CACHE_PROFILE is not defined. */ | |
| #ifdef DD_CACHE_PROFILE | |
|     unique->cache = unique->acache; | |
|     unique->memused += (cacheSize) * sizeof(DdCache); | |
| #else | |
|     mem = (DdNodePtr *) unique->acache; | |
|     offset = (ptruint) mem & (sizeof(DdCache) - 1); | |
|     mem += (sizeof(DdCache) - offset) / sizeof(DdNodePtr); | |
|     unique->cache = (DdCache *) mem; | |
|     assert(((ptruint) unique->cache & (sizeof(DdCache) - 1)) == 0); | |
|     unique->memused += (cacheSize+1) * sizeof(DdCache); | |
| #endif | |
|     unique->cacheSlots = cacheSize; | |
|     unique->cacheShift = sizeof(int) * 8 - logSize; | |
|     unique->maxCacheHard = maxCacheSize; | |
|     /* If cacheSlack is non-negative, we can resize. */ | |
|     unique->cacheSlack = (int) ddMin(maxCacheSize, | |
| 	DD_MAX_CACHE_TO_SLOTS_RATIO*unique->slots) - | |
| 	2 * (int) cacheSize; | |
|     Cudd_SetMinHit(unique,DD_MIN_HIT); | |
|     /* Initialize to avoid division by 0 and immediate resizing. */ | |
|     unique->cacheMisses = (double) (int) (cacheSize * unique->minHit + 1); | |
|     unique->cacheHits = 0; | |
|     unique->totCachehits = 0; | |
|     /* The sum of cacheMisses and totCacheMisses is always correct, | |
|     ** even though cacheMisses is larger than it should for the reasons | |
|     ** explained above. */ | |
|     unique->totCacheMisses = -unique->cacheMisses; | |
|     unique->cachecollisions = 0; | |
|     unique->cacheinserts = 0; | |
|     unique->cacheLastInserts = 0; | |
|     unique->cachedeletions = 0; | |
| 
 | |
|     /* Initialize the cache */ | |
|     for (i = 0; (unsigned) i < cacheSize; i++) { | |
| 	unique->cache[i].h = 0; /* unused slots */ | |
| 	unique->cache[i].data = NULL; /* invalid entry */ | |
| #ifdef DD_CACHE_PROFILE | |
| 	unique->cache[i].count = 0; | |
| #endif | |
|     } | |
| 
 | |
|     return(1); | |
| 
 | |
| } /* end of cuddInitCache */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Inserts a result in the cache for a function with three | |
|   operands.] | |
|  | |
|   Description [Inserts a result in the cache for a function with three | |
|   operands.  The operator tag (see cuddInt.h for details) is split and stored | |
|   into unused bits of the first two pointers.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheInsert2 cuddCacheInsert1] | |
|  | |
| ******************************************************************************/ | |
| void | |
| cuddCacheInsert( | |
|   DdManager * table, | |
|   ptruint op, | |
|   DdNode * f, | |
|   DdNode * g, | |
|   DdNode * h, | |
|   DdNode * data) | |
| { | |
|     int posn; | |
|     register DdCache *entry; | |
|     ptruint uf, ug, uh; | |
| 
 | |
|     uf = (ptruint) f | (op & 0xe); | |
|     ug = (ptruint) g | (op >> 4); | |
|     uh = (ptruint) h; | |
| 
 | |
|     posn = ddCHash2(uh,uf,ug,table->cacheShift); | |
|     entry = &table->cache[posn]; | |
| 
 | |
|     table->cachecollisions += entry->data != NULL; | |
|     table->cacheinserts++; | |
| 
 | |
|     entry->f    = (DdNode *) uf; | |
|     entry->g    = (DdNode *) ug; | |
|     entry->h    = uh; | |
|     entry->data = data; | |
| #ifdef DD_CACHE_PROFILE | |
|     entry->count++; | |
| #endif | |
|  | |
| } /* end of cuddCacheInsert */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Inserts a result in the cache for a function with two | |
|   operands.] | |
|  | |
|   Description [] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheInsert cuddCacheInsert1] | |
|  | |
| ******************************************************************************/ | |
| void | |
| cuddCacheInsert2( | |
|   DdManager * table, | |
|   DD_CTFP op, | |
|   DdNode * f, | |
|   DdNode * g, | |
|   DdNode * data) | |
| { | |
|     int posn; | |
|     register DdCache *entry; | |
| 
 | |
|     posn = ddCHash2(op,f,g,table->cacheShift); | |
|     entry = &table->cache[posn]; | |
| 
 | |
|     if (entry->data != NULL) { | |
| 	table->cachecollisions++; | |
|     } | |
|     table->cacheinserts++; | |
| 
 | |
|     entry->f = f; | |
|     entry->g = g; | |
|     entry->h = (ptruint) op; | |
|     entry->data = data; | |
| #ifdef DD_CACHE_PROFILE | |
|     entry->count++; | |
| #endif | |
|  | |
| } /* end of cuddCacheInsert2 */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Inserts a result in the cache for a function with two | |
|   operands.] | |
|  | |
|   Description [] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheInsert cuddCacheInsert2] | |
|  | |
| ******************************************************************************/ | |
| void | |
| cuddCacheInsert1( | |
|   DdManager * table, | |
|   DD_CTFP1 op, | |
|   DdNode * f, | |
|   DdNode * data) | |
| { | |
|     int posn; | |
|     register DdCache *entry; | |
| 
 | |
|     posn = ddCHash2(op,f,f,table->cacheShift); | |
|     entry = &table->cache[posn]; | |
| 
 | |
|     if (entry->data != NULL) { | |
| 	table->cachecollisions++; | |
|     } | |
|     table->cacheinserts++; | |
| 
 | |
|     entry->f = f; | |
|     entry->g = f; | |
|     entry->h = (ptruint) op; | |
|     entry->data = data; | |
| #ifdef DD_CACHE_PROFILE | |
|     entry->count++; | |
| #endif | |
|  | |
| } /* end of cuddCacheInsert1 */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Looks up in the cache for the result of op applied to f, | |
|   g, and h.] | |
|  | |
|   Description [Returns the result if found; it returns NULL if no | |
|   result is found.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheLookup2 cuddCacheLookup1] | |
|  | |
| ******************************************************************************/ | |
| DdNode * | |
| cuddCacheLookup( | |
|   DdManager * table, | |
|   ptruint op, | |
|   DdNode * f, | |
|   DdNode * g, | |
|   DdNode * h) | |
| { | |
|     int posn; | |
|     DdCache *en,*cache; | |
|     DdNode *data; | |
|     ptruint uf, ug, uh; | |
| 
 | |
|     uf = (ptruint) f | (op & 0xe); | |
|     ug = (ptruint) g | (op >> 4); | |
|     uh = (ptruint) h; | |
| 
 | |
|     cache = table->cache; | |
| #ifdef DD_DEBUG | |
|     if (cache == NULL) { | |
| 	return(NULL); | |
|     } | |
| #endif | |
|  | |
|     posn = ddCHash2(uh,uf,ug,table->cacheShift); | |
|     en = &cache[posn]; | |
|     if (en->data != NULL && en->f==(DdNodePtr)uf && en->g==(DdNodePtr)ug && | |
| 	en->h==uh) { | |
| 	data = Cudd_Regular(en->data); | |
| 	table->cacheHits++; | |
| 	if (data->ref == 0) { | |
| 	    cuddReclaim(table,data); | |
| 	} | |
| 	return(en->data); | |
|     } | |
| 
 | |
|     /* Cache miss: decide whether to resize. */ | |
|     table->cacheMisses++; | |
| 
 | |
|     if (table->cacheSlack >= 0 && | |
| 	table->cacheHits > table->cacheMisses * table->minHit) { | |
| 	cuddCacheResize(table); | |
|     } | |
| 
 | |
|     return(NULL); | |
| 
 | |
| } /* end of cuddCacheLookup */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Looks up in the cache for the result of op applied to f, | |
|   g, and h.] | |
|  | |
|   Description [Returns the result if found; it returns NULL if no | |
|   result is found.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheLookup2Zdd cuddCacheLookup1Zdd] | |
|  | |
| ******************************************************************************/ | |
| DdNode * | |
| cuddCacheLookupZdd( | |
|   DdManager * table, | |
|   ptruint op, | |
|   DdNode * f, | |
|   DdNode * g, | |
|   DdNode * h) | |
| { | |
|     int posn; | |
|     DdCache *en,*cache; | |
|     DdNode *data; | |
|     ptruint uf, ug, uh; | |
| 
 | |
|     uf = (ptruint) f | (op & 0xe); | |
|     ug = (ptruint) g | (op >> 4); | |
|     uh = (ptruint) h; | |
| 
 | |
|     cache = table->cache; | |
| #ifdef DD_DEBUG | |
|     if (cache == NULL) { | |
| 	return(NULL); | |
|     } | |
| #endif | |
|  | |
|     posn = ddCHash2(uh,uf,ug,table->cacheShift); | |
|     en = &cache[posn]; | |
|     if (en->data != NULL && en->f==(DdNodePtr)uf && en->g==(DdNodePtr)ug && | |
| 	en->h==uh) { | |
| 	data = Cudd_Regular(en->data); | |
| 	table->cacheHits++; | |
| 	if (data->ref == 0) { | |
| 	    cuddReclaimZdd(table,data); | |
| 	} | |
| 	return(en->data); | |
|     } | |
| 
 | |
|     /* Cache miss: decide whether to resize. */ | |
|     table->cacheMisses++; | |
| 
 | |
|     if (table->cacheSlack >= 0 && | |
| 	table->cacheHits > table->cacheMisses * table->minHit) { | |
| 	cuddCacheResize(table); | |
|     } | |
| 
 | |
|     return(NULL); | |
| 
 | |
| } /* end of cuddCacheLookupZdd */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Looks up in the cache for the result of op applied to f | |
|   and g.] | |
|  | |
|   Description [Returns the result if found; it returns NULL if no | |
|   result is found.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheLookup cuddCacheLookup1] | |
|  | |
| ******************************************************************************/ | |
| DdNode * | |
| cuddCacheLookup2( | |
|   DdManager * table, | |
|   DD_CTFP op, | |
|   DdNode * f, | |
|   DdNode * g) | |
| { | |
|     int posn; | |
|     DdCache *en,*cache; | |
|     DdNode *data; | |
| 
 | |
|     cache = table->cache; | |
| #ifdef DD_DEBUG | |
|     if (cache == NULL) { | |
| 	return(NULL); | |
|     } | |
| #endif | |
|  | |
|     posn = ddCHash2(op,f,g,table->cacheShift); | |
|     en = &cache[posn]; | |
|     if (en->data != NULL && en->f==f && en->g==g && en->h==(ptruint)op) { | |
| 	data = Cudd_Regular(en->data); | |
| 	table->cacheHits++; | |
| 	if (data->ref == 0) { | |
| 	    cuddReclaim(table,data); | |
| 	} | |
| 	return(en->data); | |
|     } | |
| 
 | |
|     /* Cache miss: decide whether to resize. */ | |
|     table->cacheMisses++; | |
| 
 | |
|     if (table->cacheSlack >= 0 && | |
| 	table->cacheHits > table->cacheMisses * table->minHit) { | |
| 	cuddCacheResize(table); | |
|     } | |
| 
 | |
|     return(NULL); | |
| 
 | |
| } /* end of cuddCacheLookup2 */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis [Looks up in the cache for the result of op applied to f.] | |
|  | |
|   Description [Returns the result if found; it returns NULL if no | |
|   result is found.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheLookup cuddCacheLookup2] | |
|  | |
| ******************************************************************************/ | |
| DdNode * | |
| cuddCacheLookup1( | |
|   DdManager * table, | |
|   DD_CTFP1 op, | |
|   DdNode * f) | |
| { | |
|     int posn; | |
|     DdCache *en,*cache; | |
|     DdNode *data; | |
| 
 | |
|     cache = table->cache; | |
| #ifdef DD_DEBUG | |
|     if (cache == NULL) { | |
| 	return(NULL); | |
|     } | |
| #endif | |
|  | |
|     posn = ddCHash2(op,f,f,table->cacheShift); | |
|     en = &cache[posn]; | |
|     if (en->data != NULL && en->f==f && en->h==(ptruint)op) { | |
| 	data = Cudd_Regular(en->data); | |
| 	table->cacheHits++; | |
| 	if (data->ref == 0) { | |
| 	    cuddReclaim(table,data); | |
| 	} | |
| 	return(en->data); | |
|     } | |
| 
 | |
|     /* Cache miss: decide whether to resize. */ | |
|     table->cacheMisses++; | |
| 
 | |
|     if (table->cacheSlack >= 0 && | |
| 	table->cacheHits > table->cacheMisses * table->minHit) { | |
| 	cuddCacheResize(table); | |
|     } | |
| 
 | |
|     return(NULL); | |
| 
 | |
| } /* end of cuddCacheLookup1 */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis [Looks up in the cache for the result of op applied to f | |
|   and g.] | |
|  | |
|   Description [Returns the result if found; it returns NULL if no | |
|   result is found.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheLookupZdd cuddCacheLookup1Zdd] | |
|  | |
| ******************************************************************************/ | |
| DdNode * | |
| cuddCacheLookup2Zdd( | |
|   DdManager * table, | |
|   DD_CTFP op, | |
|   DdNode * f, | |
|   DdNode * g) | |
| { | |
|     int posn; | |
|     DdCache *en,*cache; | |
|     DdNode *data; | |
| 
 | |
|     cache = table->cache; | |
| #ifdef DD_DEBUG | |
|     if (cache == NULL) { | |
| 	return(NULL); | |
|     } | |
| #endif | |
|  | |
|     posn = ddCHash2(op,f,g,table->cacheShift); | |
|     en = &cache[posn]; | |
|     if (en->data != NULL && en->f==f && en->g==g && en->h==(ptruint)op) { | |
| 	data = Cudd_Regular(en->data); | |
| 	table->cacheHits++; | |
| 	if (data->ref == 0) { | |
| 	    cuddReclaimZdd(table,data); | |
| 	} | |
| 	return(en->data); | |
|     } | |
| 
 | |
|     /* Cache miss: decide whether to resize. */ | |
|     table->cacheMisses++; | |
| 
 | |
|     if (table->cacheSlack >= 0 && | |
| 	table->cacheHits > table->cacheMisses * table->minHit) { | |
| 	cuddCacheResize(table); | |
|     } | |
| 
 | |
|     return(NULL); | |
| 
 | |
| } /* end of cuddCacheLookup2Zdd */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis [Looks up in the cache for the result of op applied to f.] | |
|  | |
|   Description [Returns the result if found; it returns NULL if no | |
|   result is found.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheLookupZdd cuddCacheLookup2Zdd] | |
|  | |
| ******************************************************************************/ | |
| DdNode * | |
| cuddCacheLookup1Zdd( | |
|   DdManager * table, | |
|   DD_CTFP1 op, | |
|   DdNode * f) | |
| { | |
|     int posn; | |
|     DdCache *en,*cache; | |
|     DdNode *data; | |
| 
 | |
|     cache = table->cache; | |
| #ifdef DD_DEBUG | |
|     if (cache == NULL) { | |
| 	return(NULL); | |
|     } | |
| #endif | |
|  | |
|     posn = ddCHash2(op,f,f,table->cacheShift); | |
|     en = &cache[posn]; | |
|     if (en->data != NULL && en->f==f && en->h==(ptruint)op) { | |
| 	data = Cudd_Regular(en->data); | |
| 	table->cacheHits++; | |
| 	if (data->ref == 0) { | |
| 	    cuddReclaimZdd(table,data); | |
| 	} | |
| 	return(en->data); | |
|     } | |
| 
 | |
|     /* Cache miss: decide whether to resize. */ | |
|     table->cacheMisses++; | |
| 
 | |
|     if (table->cacheSlack >= 0  && | |
| 	table->cacheHits > table->cacheMisses * table->minHit) { | |
| 	cuddCacheResize(table); | |
|     } | |
| 
 | |
|     return(NULL); | |
| 
 | |
| } /* end of cuddCacheLookup1Zdd */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis [Looks up in the cache for the result of op applied to f, | |
|   g, and h.] | |
|  | |
|   Description [Looks up in the cache for the result of op applied to f, | |
|   g, and h. Assumes that the calling procedure (e.g., | |
|   Cudd_bddIteConstant) is only interested in whether the result is | |
|   constant or not. Returns the result if found (possibly | |
|   DD_NON_CONSTANT); otherwise it returns NULL.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [cuddCacheLookup] | |
|  | |
| ******************************************************************************/ | |
| DdNode * | |
| cuddConstantLookup( | |
|   DdManager * table, | |
|   ptruint op, | |
|   DdNode * f, | |
|   DdNode * g, | |
|   DdNode * h) | |
| { | |
|     int posn; | |
|     DdCache *en,*cache; | |
|     ptruint uf, ug, uh; | |
| 
 | |
|     uf = (ptruint) f | (op & 0xe); | |
|     ug = (ptruint) g | (op >> 4); | |
|     uh = (ptruint) h; | |
| 
 | |
|     cache = table->cache; | |
| #ifdef DD_DEBUG | |
|     if (cache == NULL) { | |
| 	return(NULL); | |
|     } | |
| #endif | |
|     posn = ddCHash2(uh,uf,ug,table->cacheShift); | |
|     en = &cache[posn]; | |
| 
 | |
|     /* We do not reclaim here because the result should not be | |
|      * referenced, but only tested for being a constant. | |
|      */ | |
|     if (en->data != NULL && | |
| 	en->f == (DdNodePtr)uf && en->g == (DdNodePtr)ug && en->h == uh) { | |
| 	table->cacheHits++; | |
| 	return(en->data); | |
|     } | |
| 
 | |
|     /* Cache miss: decide whether to resize. */ | |
|     table->cacheMisses++; | |
| 
 | |
|     if (table->cacheSlack >= 0 && | |
| 	table->cacheHits > table->cacheMisses * table->minHit) { | |
| 	cuddCacheResize(table); | |
|     } | |
| 
 | |
|     return(NULL); | |
| 
 | |
| } /* end of cuddConstantLookup */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Computes and prints a profile of the cache usage.] | |
|  | |
|   Description [Computes and prints a profile of the cache usage. | |
|   Returns 1 if successful; 0 otherwise.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [] | |
|  | |
| ******************************************************************************/ | |
| int | |
| cuddCacheProfile( | |
|   DdManager * table, | |
|   FILE * fp) | |
| { | |
|     DdCache *cache = table->cache; | |
|     int slots = table->cacheSlots; | |
|     int nzeroes = 0; | |
|     int i, retval; | |
|     double exUsed; | |
| 
 | |
| #ifdef DD_CACHE_PROFILE | |
|     double count, mean, meansq, stddev, expected; | |
|     long max, min; | |
|     int imax, imin; | |
|     double *hystogramQ, *hystogramR; /* histograms by quotient and remainder */ | |
|     int nbins = DD_HYSTO_BINS; | |
|     int bin; | |
|     long thiscount; | |
|     double totalcount, exStddev; | |
| 
 | |
|     meansq = mean = expected = 0.0; | |
|     max = min = (long) cache[0].count; | |
|     imax = imin = 0; | |
|     totalcount = 0.0; | |
| 
 | |
|     hystogramQ = ALLOC(double, nbins); | |
|     if (hystogramQ == NULL) { | |
| 	table->errorCode = CUDD_MEMORY_OUT; | |
| 	return(0); | |
|     } | |
|     hystogramR = ALLOC(double, nbins); | |
|     if (hystogramR == NULL) { | |
| 	FREE(hystogramQ); | |
| 	table->errorCode = CUDD_MEMORY_OUT; | |
| 	return(0); | |
|     } | |
|     for (i = 0; i < nbins; i++) { | |
| 	hystogramQ[i] = 0; | |
| 	hystogramR[i] = 0; | |
|     } | |
| 
 | |
|     for (i = 0; i < slots; i++) { | |
| 	thiscount = (long) cache[i].count; | |
| 	if (thiscount > max) { | |
| 	    max = thiscount; | |
| 	    imax = i; | |
| 	} | |
| 	if (thiscount < min) { | |
| 	    min = thiscount; | |
| 	    imin = i; | |
| 	} | |
| 	if (thiscount == 0) { | |
| 	    nzeroes++; | |
| 	} | |
| 	count = (double) thiscount; | |
| 	mean += count; | |
| 	meansq += count * count; | |
| 	totalcount += count; | |
| 	expected += count * (double) i; | |
| 	bin = (i * nbins) / slots; | |
| 	hystogramQ[bin] += (double) thiscount; | |
| 	bin = i % nbins; | |
| 	hystogramR[bin] += (double) thiscount; | |
|     } | |
|     mean /= (double) slots; | |
|     meansq /= (double) slots; | |
| 
 | |
|     /* Compute the standard deviation from both the data and the | |
|     ** theoretical model for a random distribution. */ | |
|     stddev = sqrt(meansq - mean*mean); | |
|     exStddev = sqrt((1 - 1/(double) slots) * totalcount / (double) slots); | |
| 
 | |
|     retval = fprintf(fp,"Cache average accesses = %g\n",  mean); | |
|     if (retval == EOF) return(0); | |
|     retval = fprintf(fp,"Cache access standard deviation = %g ", stddev); | |
|     if (retval == EOF) return(0); | |
|     retval = fprintf(fp,"(expected = %g)\n", exStddev); | |
|     if (retval == EOF) return(0); | |
|     retval = fprintf(fp,"Cache max accesses = %ld for slot %d\n", max, imax); | |
|     if (retval == EOF) return(0); | |
|     retval = fprintf(fp,"Cache min accesses = %ld for slot %d\n", min, imin); | |
|     if (retval == EOF) return(0); | |
|     exUsed = 100.0 * (1.0 - exp(-totalcount / (double) slots)); | |
|     retval = fprintf(fp,"Cache used slots = %.2f%% (expected %.2f%%)\n", | |
| 		     100.0 - (double) nzeroes * 100.0 / (double) slots, | |
| 		     exUsed); | |
|     if (retval == EOF) return(0); | |
| 
 | |
|     if (totalcount > 0) { | |
| 	expected /= totalcount; | |
| 	retval = fprintf(fp,"Cache access hystogram for %d bins", nbins); | |
| 	if (retval == EOF) return(0); | |
| 	retval = fprintf(fp," (expected bin value = %g)\nBy quotient:", | |
| 			 expected); | |
| 	if (retval == EOF) return(0); | |
| 	for (i = nbins - 1; i>=0; i--) { | |
| 	    retval = fprintf(fp," %.0f", hystogramQ[i]); | |
| 	    if (retval == EOF) return(0); | |
| 	} | |
| 	retval = fprintf(fp,"\nBy residue: "); | |
| 	if (retval == EOF) return(0); | |
| 	for (i = nbins - 1; i>=0; i--) { | |
| 	    retval = fprintf(fp," %.0f", hystogramR[i]); | |
| 	    if (retval == EOF) return(0); | |
| 	} | |
| 	retval = fprintf(fp,"\n"); | |
| 	if (retval == EOF) return(0); | |
|     } | |
| 
 | |
|     FREE(hystogramQ); | |
|     FREE(hystogramR); | |
| #else | |
|     for (i = 0; i < slots; i++) { | |
| 	nzeroes += cache[i].h == 0; | |
|     } | |
|     exUsed = 100.0 * | |
| 	(1.0 - exp(-(table->cacheinserts - table->cacheLastInserts) / | |
| 		   (double) slots)); | |
|     retval = fprintf(fp,"Cache used slots = %.2f%% (expected %.2f%%)\n", | |
| 		     100.0 - (double) nzeroes * 100.0 / (double) slots, | |
| 		     exUsed); | |
|     if (retval == EOF) return(0); | |
| #endif | |
|     return(1); | |
| 
 | |
| } /* end of cuddCacheProfile */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Resizes the cache.] | |
|  | |
|   Description [] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [] | |
|  | |
| ******************************************************************************/ | |
| void | |
| cuddCacheResize( | |
|   DdManager * table) | |
| { | |
|     DdCache *cache, *oldcache, *oldacache, *entry, *old; | |
|     int i; | |
|     int posn, shift; | |
|     unsigned int slots, oldslots; | |
|     double offset; | |
|     int moved = 0; | |
|     extern DD_OOMFP MMoutOfMemory; | |
|     DD_OOMFP saveHandler; | |
| #ifndef DD_CACHE_PROFILE | |
|     ptruint misalignment; | |
|     DdNodePtr *mem; | |
| #endif | |
|  | |
|     oldcache = table->cache; | |
|     oldacache = table->acache; | |
|     oldslots = table->cacheSlots; | |
|     slots = table->cacheSlots = oldslots << 1; | |
| 
 | |
| #ifdef DD_VERBOSE | |
|     (void) fprintf(table->err,"Resizing the cache from %d to %d entries\n", | |
| 		   oldslots, slots); | |
|     (void) fprintf(table->err, | |
| 		   "\thits = %g\tmisses = %g\thit ratio = %5.3f\n", | |
| 		   table->cacheHits, table->cacheMisses, | |
| 		   table->cacheHits / (table->cacheHits + table->cacheMisses)); | |
| #endif | |
|  | |
|     saveHandler = MMoutOfMemory; | |
|     MMoutOfMemory = Cudd_OutOfMem; | |
|     table->acache = cache = ALLOC(DdCache,slots+1); | |
|     MMoutOfMemory = saveHandler; | |
|     /* If we fail to allocate the new table we just give up. */ | |
|     if (cache == NULL) { | |
| #ifdef DD_VERBOSE | |
| 	(void) fprintf(table->err,"Resizing failed. Giving up.\n"); | |
| #endif | |
| 	table->cacheSlots = oldslots; | |
| 	table->acache = oldacache; | |
| 	/* Do not try to resize again. */ | |
| 	table->maxCacheHard = oldslots - 1; | |
| 	table->cacheSlack = - (int) (oldslots + 1); | |
| 	return; | |
|     } | |
|     /* If the size of the cache entry is a power of 2, we want to | |
|     ** enforce alignment to that power of two. This happens when | |
|     ** DD_CACHE_PROFILE is not defined. */ | |
| #ifdef DD_CACHE_PROFILE | |
|     table->cache = cache; | |
| #else | |
|     mem = (DdNodePtr *) cache; | |
|     misalignment = (ptruint) mem & (sizeof(DdCache) - 1); | |
|     mem += (sizeof(DdCache) - misalignment) / sizeof(DdNodePtr); | |
|     table->cache = cache = (DdCache *) mem; | |
|     assert(((ptruint) table->cache & (sizeof(DdCache) - 1)) == 0); | |
| #endif | |
|     shift = --(table->cacheShift); | |
|     table->memused += (slots - oldslots) * sizeof(DdCache); | |
|     table->cacheSlack -= slots; /* need these many slots to double again */ | |
| 
 | |
|     /* Clear new cache. */ | |
|     for (i = 0; (unsigned) i < slots; i++) { | |
| 	cache[i].data = NULL; | |
| 	cache[i].h = 0; | |
| #ifdef DD_CACHE_PROFILE | |
| 	cache[i].count = 0; | |
| #endif | |
|     } | |
| 
 | |
|     /* Copy from old cache to new one. */ | |
|     for (i = 0; (unsigned) i < oldslots; i++) { | |
| 	old = &oldcache[i]; | |
| 	if (old->data != NULL) { | |
| 	    posn = ddCHash2(old->h,old->f,old->g,shift); | |
| 	    entry = &cache[posn]; | |
| 	    entry->f = old->f; | |
| 	    entry->g = old->g; | |
| 	    entry->h = old->h; | |
| 	    entry->data = old->data; | |
| #ifdef DD_CACHE_PROFILE | |
| 	    entry->count = 1; | |
| #endif | |
| 	    moved++; | |
| 	} | |
|     } | |
| 
 | |
|     FREE(oldacache); | |
| 
 | |
|     /* Reinitialize measurements so as to avoid division by 0 and | |
|     ** immediate resizing. | |
|     */ | |
|     offset = (double) (int) (slots * table->minHit + 1); | |
|     table->totCacheMisses += table->cacheMisses - offset; | |
|     table->cacheMisses = offset; | |
|     table->totCachehits += table->cacheHits; | |
|     table->cacheHits = 0; | |
|     table->cacheLastInserts = table->cacheinserts - (double) moved; | |
| 
 | |
| } /* end of cuddCacheResize */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Flushes the cache.] | |
|  | |
|   Description [] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [] | |
|  | |
| ******************************************************************************/ | |
| void | |
| cuddCacheFlush( | |
|   DdManager * table) | |
| { | |
|     int i, slots; | |
|     DdCache *cache; | |
| 
 | |
|     slots = table->cacheSlots; | |
|     cache = table->cache; | |
|     for (i = 0; i < slots; i++) { | |
| 	table->cachedeletions += cache[i].data != NULL; | |
| 	cache[i].data = NULL; | |
|     } | |
|     table->cacheLastInserts = table->cacheinserts; | |
| 
 | |
|     return; | |
| 
 | |
| } /* end of cuddCacheFlush */ | |
| 
 | |
| 
 | |
| /**Function******************************************************************** | |
|  | |
|   Synopsis    [Returns the floor of the logarithm to the base 2.] | |
|  | |
|   Description [Returns the floor of the logarithm to the base 2. | |
|   The input value is assumed to be greater than 0.] | |
|  | |
|   SideEffects [None] | |
|  | |
|   SeeAlso     [] | |
|  | |
| ******************************************************************************/ | |
| int | |
| cuddComputeFloorLog2( | |
|   unsigned int value) | |
| { | |
|     int floorLog = 0; | |
| #ifdef DD_DEBUG | |
|     assert(value > 0); | |
| #endif | |
|     while (value > 1) { | |
| 	floorLog++; | |
| 	value >>= 1; | |
|     } | |
|     return(floorLog); | |
| 
 | |
| } /* end of cuddComputeFloorLog2 */ | |
| 
 | |
| /*---------------------------------------------------------------------------*/ | |
| /* Definition of static functions                                            */ | |
| /*---------------------------------------------------------------------------*/
 |