Another performance optimization to "extresist", this one to

avoid re-parsing a linked list from the current breakpoint
to the end to find all entries pointing to the same node.
Instead, the change to be made is saved in a hash table and
applied at the following link, so only one pass through the
linked list is required.
This commit is contained in:
R. Timothy Edwards 2026-05-21 19:55:50 -04:00
parent a062fdcfe0
commit 79e2dbdd43
3 changed files with 100 additions and 29 deletions

View File

@ -1 +1 @@
8.3.646 8.3.647

View File

@ -125,7 +125,7 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
resNode **pendingList, **doneList; resNode **pendingList, **doneList;
resResistor **resList; resResistor **resList;
{ {
int height; int count, height;
bool merged; bool merged;
TileType ttype; TileType ttype;
Breakpoint *p1, *p2, *p3; Breakpoint *p1, *p2, *p3;
@ -134,6 +134,8 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
resNode *currNode; resNode *currNode;
float rArea; float rArea;
resInfo *info = (resInfo *)TiGetClientPTR(tile); resInfo *info = (resInfo *)TiGetClientPTR(tile);
HashTable BreakTable;
HashEntry *he;
merged = FALSE; merged = FALSE;
height = TOP(tile) - BOTTOM(tile); height = TOP(tile) - BOTTOM(tile);
@ -163,21 +165,38 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
ttype = TiGetTypeExact(tile); ttype = TiGetTypeExact(tile);
/* Re-sort nodes left to right. */ /* Re-sort nodes left to right. */
count = ResSortBreaks(&info->breakList, TRUE);
ResSortBreaks(&info->breakList, TRUE); /* For long lists (defined as >= 16 entries), make a hash table of
* the node pointer conversions so that each node can be updated
* as we walk the list, instead of walking the rest of the list in
* a nested loop for each entry.
*/
if (count >= 16)
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
/* /*
* Eliminate breakpoints with the same X coordinate and merge * Eliminate breakpoints with the same X coordinate and merge
* their nodes. * their nodes.
*/ */
p2= info->breakList; p2 = info->breakList;
/* Add extra left area to leftmost node */ /* Add extra left area to leftmost node */
p2->br_this->rn_float.rn_area += height * (p2->br_loc.p_x - LEFT(tile)); p2->br_this->rn_float.rn_area += height * (p2->br_loc.p_x - LEFT(tile));
while (p2->br_next != NULL) while (p2->br_next != NULL)
{ {
/* Has the node been recorded as needing to be replaced? */
if (count >= 16)
{
while (TRUE)
{
he = HashLookOnly(&BreakTable, (char *)p2->br_this);
if (!he) break;
p2->br_this = (resNode *)HashGetValue(he);
}
}
p1 = p2; p1 = p2;
p2 = p2->br_next; p2 = p2->br_next;
if (p2->br_loc.p_x == p1->br_loc.p_x) if (p2->br_loc.p_x == p1->br_loc.p_x)
@ -215,25 +234,38 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
/* /*
* Was the node used in another info or breakpoint? * Was the node used in another info or breakpoint?
* If so, replace the old node with the new one. * If so, replace the old node with the new one.
*
* Short lists: Walk the list to the end and change
* nodes on the fly.
* Long lists: Record the change to be made in the
* hash table so that it can be executed as each list
* entry is encountered.
*/ */
if (count >= 16)
p3 = p2->br_next;
while (p3 != NULL)
{ {
if (p3->br_this == currNode) he = HashFind(&BreakTable, (char *)currNode);
p3->br_this = p2->br_this; HashSetValue(he, (char *)p2->br_this);
p3 = p3->br_next;
} }
} else
{
p3 = p2->br_next;
while (p3 != NULL)
{
if (p3->br_this == currNode)
p3->br_this = p2->br_this;
/* p3 = p3->br_next;
* If the X coordinates don't match, make a resistor between }
* the breakpoints. }
*/ }
else /*
{ * If the X coordinates don't match, make a resistor between
* the breakpoints.
*/
else
{
resistor = (resResistor *)mallocMagic((unsigned)sizeof(resResistor)); resistor = (resResistor *)mallocMagic((unsigned)sizeof(resResistor));
resistor->rr_nextResistor = (*resList); resistor->rr_nextResistor = (*resList);
resistor->rr_lastResistor = NULL; resistor->rr_lastResistor = NULL;
@ -276,6 +308,8 @@ ResCalcEastWest(tile, pendingList, doneList, resList)
} }
} }
if (count >= 16) HashKill(&BreakTable);
p2->br_this->rn_float.rn_area += height * (RIGHT(tile) - p2->br_loc.p_x); p2->br_this->rn_float.rn_area += height * (RIGHT(tile) - p2->br_loc.p_x);
freeMagic((char *)p2); freeMagic((char *)p2);
info->breakList = NULL; info->breakList = NULL;
@ -301,7 +335,7 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
resNode **pendingList, **doneList; resNode **pendingList, **doneList;
resResistor **resList; resResistor **resList;
{ {
int width; int count, width;
bool merged; bool merged;
TileType ttype; TileType ttype;
Breakpoint *p1, *p2, *p3; Breakpoint *p1, *p2, *p3;
@ -310,6 +344,8 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
resNode *currNode; resNode *currNode;
float rArea; float rArea;
resInfo *info = (resInfo *)TiGetClientPTR(tile); resInfo *info = (resInfo *)TiGetClientPTR(tile);
HashTable BreakTable;
HashEntry *he;
merged = FALSE; merged = FALSE;
width = RIGHT(tile) - LEFT(tile); width = RIGHT(tile) - LEFT(tile);
@ -329,7 +365,15 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
} }
/* Re-sort nodes south to north. */ /* Re-sort nodes south to north. */
ResSortBreaks(&info->breakList, FALSE); count = ResSortBreaks(&info->breakList, FALSE);
/* For long lists (defined as >= 16 entries), make a hash table of
* the node pointer conversions so that each node can be updated
* as we walk the list, instead of walking the rest of the list in
* a nested loop for each entry.
*/
if (count >= 16)
HashInit(&BreakTable, HT_DEFAULTSIZE, HT_CLIENTKEYS);
/* Simplified split tile handling */ /* Simplified split tile handling */
if (IsSplit(tile)) if (IsSplit(tile))
@ -353,6 +397,16 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
p2->br_this->rn_float.rn_area += width * (p2->br_loc.p_y - BOTTOM(tile)); p2->br_this->rn_float.rn_area += width * (p2->br_loc.p_y - BOTTOM(tile));
while (p2->br_next != NULL) while (p2->br_next != NULL)
{ {
/* Has the node been recorded as needing to be replaced? */
if (count >= 16)
{
while (TRUE)
{
he = HashLookOnly(&BreakTable, (char *)p2->br_this);
if (!he) break;
p2->br_this = (resNode *)HashGetValue(he);
}
}
p1 = p2; p1 = p2;
p2 = p2->br_next; p2 = p2->br_next;
if (p1->br_loc.p_y == p2->br_loc.p_y) if (p1->br_loc.p_y == p2->br_loc.p_y)
@ -390,14 +444,28 @@ ResCalcNorthSouth(tile, pendingList, doneList, resList)
/* /*
* Was the node used in another info or breakpoint? * Was the node used in another info or breakpoint?
* If so, replace the old node with the new one. * If so, replace the old node with the new one.
*
* Short lists: Walk the list to the end and change
* nodes on the fly.
* Long lists: Record the change to be made in the
* hash table so that it can be executed as each list
* entry is encountered.
*/ */
p3 = p2->br_next; if (count >= 16)
while (p3 != NULL)
{ {
if (p3->br_this == currNode) he = HashFind(&BreakTable, (char *)currNode);
p3->br_this = p2->br_this; HashSetValue(he, (char *)p2->br_this);
}
else
{
p3 = p2->br_next;
while (p3 != NULL)
{
if (p3->br_this == currNode)
p3->br_this = p2->br_this;
p3 = p3->br_next; p3 = p3->br_next;
}
} }
} }
@ -1093,12 +1161,14 @@ MergeSortBreaks(Breakpoint *list, int xsort)
* bottleneck. * bottleneck.
* *
* Results: * Results:
* None * Return the length of the list (maximum truncated at 16) so that
* the calling routine can determine if this is a long or a short
* linked list and treat it accordingly.
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
void int
ResSortBreaks(masterlist, xsort) ResSortBreaks(masterlist, xsort)
Breakpoint **masterlist; Breakpoint **masterlist;
int xsort; int xsort;
@ -1113,7 +1183,7 @@ ResSortBreaks(masterlist, xsort)
if (count > 16) if (count > 16)
{ {
*masterlist = MergeSortBreaks(*masterlist, xsort); *masterlist = MergeSortBreaks(*masterlist, xsort);
return; return count;
} }
} }
@ -1154,5 +1224,6 @@ ResSortBreaks(masterlist, xsort)
} }
} }
} }
return count;
} }

View File

@ -543,7 +543,7 @@ extern void ResCheckExtNodes();
extern void ResSortByGate(); extern void ResSortByGate();
extern void ResFixDevName(); extern void ResFixDevName();
extern void ResWriteLumpFile(); extern void ResWriteLumpFile();
extern void ResSortBreaks(); extern int ResSortBreaks();
extern Plane *extResPrepSubstrate(); extern Plane *extResPrepSubstrate();
/* C99 compat */ /* C99 compat */