From 09fbfb1905a9f04bb46cc50b19ed9b66087d12b2 Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Tue, 13 Dec 2022 21:05:55 -0600 Subject: [PATCH] Maintain a pool of empty symbol tables that can be reused. The motivation for this is that allocating and clearing symbol tables is a common operation, especially with C99+, where a construct like "if (...) { ... }" involves three levels of scope with their own symbol tables. In some tests, it could take an appreciable fraction of total execution time (sometimes ~10%). This patch allows symbol tables that have already been allocated and cleared to be reused for a subsequent scope, as long as they are still empty. It does this by maintaining a pool of empty symbol tables and taking one from there rather than allocating a new one when possible. We impose a somewhat arbitrary limit of MaxBlock/150000 on the number of symbol tables we keep, to avoid filling up memory with them. It would probably be better to use purgeable handles here, but that would be a little more work, and this should be good enough for now. --- CCommon.pas | 1 + Symbol.asm | 5 +++-- Symbol.pas | 61 ++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/CCommon.pas b/CCommon.pas index a7f4337..0a00b22 100644 --- a/CCommon.pas +++ b/CCommon.pas @@ -74,6 +74,7 @@ interface const {hashsize appears in CCOMMON.ASM} hashSize = 876; {# hash buckets - 1} + {NOTE: hashsize2 is used in Symbol.asm} hashSize2 = 1753; {# hash buckets * 2 - 1} maxLine = 255; {max length of a line} maxPath = 255; {max length of a path name} diff --git a/Symbol.asm b/Symbol.asm index 03c5317..9163226 100644 --- a/Symbol.asm +++ b/Symbol.asm @@ -9,11 +9,12 @@ **************************************************************** * ClearTable private cc -tableSize equ 7026 sizeof(symbolTable) +hashSize2 equ 1753 # hash buckets * 2 - 1 +sizeofBuckets equ 4*(hashSize2+1) sizeof(symbolTable.buckets) subroutine (4:table),0 - ldy #tableSize-2 + ldy #sizeofBuckets-2 lda #0 lb1 sta [table],Y dey diff --git a/Symbol.pas b/Symbol.pas index e5069bd..6618184 100644 --- a/Symbol.pas +++ b/Symbol.pas @@ -75,6 +75,7 @@ type buckets: array[0..hashSize2] of identPtr; {hash buckets} next: symbolTablePtr; {next symbol table} staticNum: packed array[1..6] of char; {staticNum at start of table} + isEmpty: boolean; {is the pool empty (nothing in buckets)?} end; var @@ -310,6 +311,9 @@ type var staticNum: packed array[1..6] of char; {static variable number} + tablePool: symbolTablePtr; {pool of reusable empty symbol tables} + tablePoolSize: 0..maxint; {number of tables in pool} + tablePoolMaxSize: 0..maxint; {max number of tables in pool} {- Imported from expression.pas --------------------------------} @@ -414,6 +418,10 @@ procedure Purge; extern; { write any constant bytes to the output buffer } +{- Imported from IIGS Memory Manager ---------------------------} + +function MaxBlock: longint; tool ($02, $1C); + {---------------------------------------------------------------} procedure ClearTable (table: symbolTable); extern; @@ -698,6 +706,22 @@ procedure DoGlobals; { declare the ~globals and ~arrays segments } + procedure FreeTablePool; + + { free the symbol table pool } + + var + tPtr: symbolTablePtr; + + begin {FreeTablePool} + while tablePool <> nil do begin + tPtr := tablePool; + tablePool := tPtr^.next; + dispose(tPtr); + end; + end; {FreeTablePool} + + procedure StaticInit (variable: identPtr); { statically initialize a variable } @@ -1053,6 +1077,8 @@ begin {DoGlobals} {if printSymbols then {debug} { PrintTable(globalTable); {debug} +FreeTablePool; {dispose of unneeded symbol tables} + {declare the ~globals segment, which holds non-array data types} if smallMemoryModel then currentSegment := ' ' @@ -1677,8 +1703,12 @@ var begin {InitSymbol} staticNum := '~0000'; {no functions processed} table := nil; {initialize the global symbol table} +tablePool := nil; {table pool is initially empty} +tablePoolSize := 0; +tablePoolMaxSize := ord(MaxBlock div 150000); {limit size of pool based on RAM} PushTable; globalTable := table; +globalTable^.isEmpty := false; {global table is never treated as empty} functionTable := nil; {declare base types} new(sCharPtr); {signed char} @@ -2348,12 +2378,13 @@ if needSymbol then begin {p^.used := false;} {unused for now} if space <> fieldListSpace then {insert the symbol in the hash bucket} begin - if itype = nil then - hashPtr := pointer(ord4(table)+Hash(name)) - else if isGlobal then - hashPtr := pointer(ord4(globalTable)+Hash(name)) - else + if (itype = nil) or not isGlobal then begin hashPtr := pointer(ord4(table)+Hash(name)); + table^.isEmpty := false; + end {if} + else begin + hashPtr := pointer(ord4(globalTable)+Hash(name)); + end; {else} if space = tagSpace then hashPtr := pointer(ord4(hashPtr) + 4*(hashSize+1)); p^.next := hashPtr^; @@ -2452,7 +2483,13 @@ if (lint & lintUnused) <> 0 then CheckUnused(tPtr); if tPtr^.next <> nil then begin table := table^.next; - dispose(tPtr); + if not tPtr^.isEmpty or (tablePoolSize = tablePoolMaxSize) then + dispose(tPtr) + else begin + tPtr^.next := tablePool; + tablePool := tPtr; + tablePoolSize := tablePoolSize + 1; + end; {else} end; {if} end; {PopTable} @@ -2480,8 +2517,16 @@ repeat done := i = 1; end; {if} until done; -new(tPtr); {create a new symbol table} -ClearTable(tPtr^); +if tablePool <> nil then begin {use existing empty table if available} + tPtr := tablePool; + tablePool := tPtr^.next; + tablePoolSize := tablePoolSize - 1; + end {if} +else begin + new(tPtr); {...or create a new symbol table} + ClearTable(tPtr^); + tPtr^.isEmpty := true; + end; {else} tPtr^.next := table; table := tPtr; tPtr^.staticNum := staticNum; {record the static symbol table number}