bring back id table algorithm instead of std table [backport:2.2] (#24930)

refs #24929, partially reverts #23403

Instead of using `Table[ItemId, T]`, the old algorithm is brought back
into `TIdTable[T]` to prevent a performance regression. The inheritance
removal from #23403 still holds, only `ItemId`s are stored.

(cherry picked from commit 82553384d1)
This commit is contained in:
metagn
2025-05-06 10:36:20 +03:00
committed by narimiran
parent 39757d421e
commit c385fcb6be
7 changed files with 111 additions and 26 deletions

View File

@@ -796,6 +796,15 @@ type
TPairSeq* = seq[TPair]
TIdPair*[T] = object
key*: ItemId
val*: T
TIdPairSeq*[T] = seq[TIdPair[T]]
TIdTable*[T] = object
counter*: int
data*: TIdPairSeq[T]
TNodePair* = object
h*: Hash # because it is expensive to compute!
key*: PNode
@@ -940,9 +949,11 @@ proc getPIdent*(a: PNode): PIdent {.inline.} =
const
moduleShift = when defined(cpu32): 20 else: 24
template id*(a: PType | PSym): int =
template toId*(a: ItemId): int =
let x = a
(x.itemId.module.int shl moduleShift) + x.itemId.item.int
(x.module.int shl moduleShift) + x.item.int
template id*(a: PType | PSym): int = toId(a.itemId)
type
IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here.
@@ -1269,6 +1280,11 @@ proc copyStrTable*(dest: var TStrTable, src: TStrTable) =
setLen(dest.data, src.data.len)
for i in 0..high(src.data): dest.data[i] = src.data[i]
proc copyIdTable*[T](dest: var TIdTable[T], src: TIdTable[T]) =
dest.counter = src.counter
newSeq(dest.data, src.data.len)
for i in 0..high(src.data): dest.data[i] = src.data[i]
proc copyObjectSet*(dest: var TObjectSet, src: TObjectSet) =
dest.counter = src.counter
setLen(dest.data, src.data.len)
@@ -1607,6 +1623,16 @@ proc initStrTable*(): TStrTable =
result = TStrTable(counter: 0)
newSeq(result.data, StartSize)
proc initIdTable*[T](): TIdTable[T] =
result = TIdTable[T](counter: 0)
newSeq(result.data, StartSize)
proc resetIdTable*[T](x: var TIdTable[T]) =
x.counter = 0
# clear and set to old initial size:
setLen(x.data, 0)
setLen(x.data, StartSize)
proc initObjectSet*(): TObjectSet =
result = TObjectSet(counter: 0)
newSeq(result.data, StartSize)
@@ -2135,14 +2161,8 @@ proc isTrue*(n: PNode): bool =
n.kind == nkIntLit and n.intVal != 0
type
TypeMapping* = Table[ItemId, PType]
SymMapping* = Table[ItemId, PSym]
TypeMapping* = TIdTable[PType]
SymMapping* = TIdTable[PSym]
template idTableGet*(tab: typed; key: PSym | PType): untyped = tab.getOrDefault(key.itemId)
template idTablePut*(tab: typed; key, val: PSym | PType) = tab[key.itemId] = val
template initSymMapping*(): Table[ItemId, PSym] = initTable[ItemId, PSym]()
template initTypeMapping*(): Table[ItemId, PType] = initTable[ItemId, PType]()
template resetIdTable*(tab: Table[ItemId, PSym]) = tab.clear()
template resetIdTable*(tab: Table[ItemId, PType]) = tab.clear()
template initSymMapping*(): SymMapping = initIdTable[PSym]()
template initTypeMapping*(): TypeMapping = initIdTable[PType]()

View File

@@ -713,6 +713,70 @@ iterator items*(tab: TStrTable): PSym =
yield s
s = nextIter(it, tab)
proc isNil(x: ItemId): bool {.inline.} =
x.module == 0 and x.item == 0
proc hasEmptySlot[T](data: TIdPairSeq[T]): bool =
for h in 0..high(data):
if isNil(data[h].key):
return true
result = false
proc idTableRawGet[T](t: TIdTable[T], key: int): int =
var h: Hash
h = key and high(t.data) # start with real hash value
while not isNil(t.data[h].key):
if toId(t.data[h].key) == key:
return h
h = nextTry(h, high(t.data))
result = - 1
proc getOrDefault*[T](t: TIdTable[T], key: ItemId): T =
var index = idTableRawGet(t, toId(key))
if index >= 0: result = t.data[index].val
else: result = default(T)
template idTableGet*[T](t: TIdTable[T], key: PType | PSym): T =
getOrDefault(t, key.itemId)
proc idTableRawInsert[T](data: var TIdPairSeq[T], key: ItemId, val: T) =
var h: Hash
let keyId = toId(key)
h = keyId and high(data)
while not isNil(data[h].key):
assert(toId(data[h].key) != keyId)
h = nextTry(h, high(data))
assert(isNil(data[h].key))
data[h].key = key
data[h].val = val
proc `[]=`*[T](t: var TIdTable[T], key: ItemId, val: T) =
var
index: int
n: TIdPairSeq[T]
index = idTableRawGet(t, toId(key))
if index >= 0:
assert(not isNil(t.data[index].key))
t.data[index].val = val
else:
if mustRehash(t.data.len, t.counter):
newSeq(n, t.data.len * GrowthFactor)
for i in 0..high(t.data):
if not isNil(t.data[i].key):
idTableRawInsert(n, t.data[i].key, t.data[i].val)
assert(hasEmptySlot(n))
swap(t.data, n)
idTableRawInsert(t.data, key, val)
inc(t.counter)
template idTablePut*[T](t: var TIdTable[T], key: PType | PSym, val: T) =
t[key.itemId] = val
iterator idTablePairs*[T](t: TIdTable[T]): tuple[key: ItemId, val: T] =
for i in 0..high(t.data):
if not isNil(t.data[i].key):
yield (t.data[i].key, t.data[i].val)
proc initIITable(x: var TIITable) =
x.counter = 0
newSeq(x.data, StartSize)

View File

@@ -1,5 +1,5 @@
import std/[tables]
import ast
import ast, astalgo
type
LayeredIdTableObj* {.acyclic.} = object
@@ -28,14 +28,15 @@ proc shallowCopy*(pt: LayeredIdTable): LayeredIdTable {.inline.} =
## copies only the type bindings of the current layer, but not any parent layers,
## useful for write-only bindings
result = LayeredIdTable(topLayer: pt.topLayer, nextLayer: pt.nextLayer, previousLen: pt.previousLen)
#copyIdTable(result.topLayer, pt.topLayer)
proc currentLen*(pt: LayeredIdTable): int =
## the sum of the cached total binding count of the parents and
## the current binding count, just used to track if bindings were added
pt.previousLen + pt.topLayer.len
pt.previousLen + pt.topLayer.counter
proc newTypeMapLayer*(pt: LayeredIdTable): LayeredIdTable =
result = LayeredIdTable(topLayer: initTable[ItemId, PType](), previousLen: pt.currentLen)
result = LayeredIdTable(topLayer: initTypeMapping(), previousLen: pt.currentLen)
when useRef:
result.nextLayer = pt
else:
@@ -56,7 +57,7 @@ proc setToPreviousLayer*(pt: var LayeredIdTable) {.inline.} =
iterator pairs*(pt: LayeredIdTable): (ItemId, PType) =
var tm = pt
while true:
for (k, v) in pairs(tm.topLayer):
for (k, v) in idTablePairs(tm.topLayer):
yield (k, v)
if tm.nextLayer == nil:
break

View File

@@ -909,15 +909,15 @@ proc semOverloadedCall(c: PContext, n, nOrig: PNode,
if c.inGenericContext > 0 and c.matchedConcept == nil:
result = semGenericStmt(c, n)
result.typ() = makeTypeFromExpr(c, result.copyTree)
elif efNoUndeclared in flags:
result = nil
elif efExplain notin flags:
# repeat the overload resolution,
# this time enabling all the diagnostic output (this should fail again)
result = semOverloadedCall(c, n, nOrig, filter, flags + {efExplain})
elif efNoUndeclared notin flags:
result = nil
notFoundError(c, n, errors)
else:
result = nil
notFoundError(c, n, errors)
proc explicitGenericInstError(c: PContext; n: PNode): PNode =
localError(c.config, getCallLineInfo(n), errCannotInstantiateX % renderTree(n))

View File

@@ -17,7 +17,7 @@ when defined(nimPreviewSlimSystem):
import
options, ast, msgs, idents, renderer,
magicsys, vmdef, modulegraphs, lineinfos, pathutils, layeredtable,
types, lowerings, trees, parampatterns
types, lowerings, trees, parampatterns, astalgo
import ic / ic
@@ -42,7 +42,7 @@ type
breakInLoop*: bool # whether we are in a loop without block
next*: PProcCon # used for stacking procedure contexts
mappingExists*: bool
mapping*: Table[ItemId, PSym]
mapping*: SymMapping
caseContext*: seq[tuple[n: PNode, idx: int]]
localBindStmts*: seq[PNode]
@@ -258,7 +258,7 @@ proc popProcCon*(c: PContext) {.inline.} = c.p = c.p.next
proc put*(p: PProcCon; key, val: PSym) =
if not p.mappingExists:
p.mapping = initTable[ItemId, PSym]()
p.mapping = initSymMapping()
p.mappingExists = true
#echo "put into table ", key.info
p.mapping[key.itemId] = val

View File

@@ -68,8 +68,8 @@ type
TReplTypeVars* = object
c*: PContext
typeMap*: LayeredIdTable # map PType to PType
symMap*: SymMapping # map PSym to PSym
localCache*: TypeMapping # local cache for remembering already replaced
symMap*: SymMapping # map PSym to PSym
localCache*: TypeMapping # local cache for remembering already replaced
# types during instantiation of meta types
# (they are not stored in the global cache)
info*: TLineInfo

View File

@@ -40,7 +40,7 @@ import closureiters, lambdalifting
type
PTransCon = ref object # part of TContext; stackable
mapping: Table[ItemId, PNode] # mapping from symbols to nodes
mapping: TIdTable[PNode] # mapping from symbols to nodes
owner: PSym # current owner
forStmt: PNode # current for stmt
forLoopBody: PNode # transformed for loop body
@@ -78,7 +78,7 @@ proc newTransNode(kind: TNodeKind, n: PNode,
proc newTransCon(owner: PSym): PTransCon =
assert owner != nil
result = PTransCon(mapping: initTable[ItemId, PNode](), owner: owner)
result = PTransCon(mapping: initIdTable[PNode](), owner: owner)
proc pushTransCon(c: PTransf, t: PTransCon) =
t.next = c.transCon