back.pysim: use arrays instead of dicts for signal values.

This makes the Glasgow testsuite about 40% faster.
This commit is contained in:
whitequark 2018-12-18 05:19:12 +00:00
parent 39605ef551
commit c5f169988b

View file

@ -20,24 +20,30 @@ class _State:
__slots__ = ("curr", "curr_dirty", "next", "next_dirty") __slots__ = ("curr", "curr_dirty", "next", "next_dirty")
def __init__(self): def __init__(self):
self.curr = SignalDict() self.curr = []
self.next = SignalDict() self.next = []
self.curr_dirty = SignalSet() self.curr_dirty = SignalSet()
self.next_dirty = SignalSet() self.next_dirty = SignalSet()
def set(self, signal, value): def add(self, signal, value):
assert isinstance(value, int) slot = len(self.curr)
if self.next[signal] != value: self.curr.append(value)
self.next_dirty.add(signal) self.next.append(value)
self.next[signal] = value self.curr_dirty.add(signal)
return slot
def commit(self, signal): def set(self, signal, slot, value):
old_value = self.curr[signal] if self.next[slot] != value:
new_value = self.next[signal] self.next_dirty.add(signal)
self.next[slot] = value
def commit(self, signal, slot):
old_value = self.curr[slot]
new_value = self.next[slot]
if old_value != new_value: if old_value != new_value:
self.next_dirty.remove(signal) self.next_dirty.remove(signal)
self.curr_dirty.add(signal) self.curr_dirty.add(signal)
self.curr[signal] = new_value self.curr[slot] = new_value
return old_value, new_value return old_value, new_value
@ -45,9 +51,10 @@ normalize = Const.normalize
class _RHSValueCompiler(AbstractValueTransformer): class _RHSValueCompiler(AbstractValueTransformer):
def __init__(self, sensitivity=None, mode="rhs"): def __init__(self, signal_slots, sensitivity=None, mode="rhs"):
self.sensitivity = sensitivity self.signal_slots = signal_slots
self.signal_mode = mode self.sensitivity = sensitivity
self.signal_mode = mode
def on_Const(self, value): def on_Const(self, value):
return lambda state: value.value return lambda state: value.value
@ -55,10 +62,11 @@ class _RHSValueCompiler(AbstractValueTransformer):
def on_Signal(self, value): def on_Signal(self, value):
if self.sensitivity is not None: if self.sensitivity is not None:
self.sensitivity.add(value) self.sensitivity.add(value)
value_slot = self.signal_slots[value]
if self.signal_mode == "rhs": if self.signal_mode == "rhs":
return lambda state: state.curr[value] return lambda state: state.curr[value_slot]
elif self.signal_mode == "lhs": elif self.signal_mode == "lhs":
return lambda state: state.next[value] return lambda state: state.next[value_slot]
else: else:
raise ValueError # :nocov: raise ValueError # :nocov:
@ -166,7 +174,8 @@ class _RHSValueCompiler(AbstractValueTransformer):
class _LHSValueCompiler(AbstractValueTransformer): class _LHSValueCompiler(AbstractValueTransformer):
def __init__(self, rhs_compiler): def __init__(self, signal_slots, rhs_compiler):
self.signal_slots = signal_slots
self.rhs_compiler = rhs_compiler self.rhs_compiler = rhs_compiler
def on_Const(self, value): def on_Const(self, value):
@ -174,8 +183,9 @@ class _LHSValueCompiler(AbstractValueTransformer):
def on_Signal(self, value): def on_Signal(self, value):
shape = value.shape() shape = value.shape()
value_slot = self.signal_slots[value]
def eval(state, rhs): def eval(state, rhs):
state.set(value, normalize(rhs, shape)) state.set(value, value_slot, normalize(rhs, shape))
return eval return eval
def on_ClockSignal(self, value): def on_ClockSignal(self, value):
@ -235,11 +245,11 @@ class _LHSValueCompiler(AbstractValueTransformer):
class _StatementCompiler(AbstractStatementTransformer): class _StatementCompiler(AbstractStatementTransformer):
def __init__(self): def __init__(self, signal_slots):
self.sensitivity = SignalSet() self.sensitivity = SignalSet()
self.rrhs_compiler = _RHSValueCompiler(self.sensitivity, mode="rhs") self.rrhs_compiler = _RHSValueCompiler(signal_slots, self.sensitivity, mode="rhs")
self.lrhs_compiler = _RHSValueCompiler(self.sensitivity, mode="lhs") self.lrhs_compiler = _RHSValueCompiler(signal_slots, self.sensitivity, mode="lhs")
self.lhs_compiler = _LHSValueCompiler(self.lrhs_compiler) self.lhs_compiler = _LHSValueCompiler(signal_slots, self.lrhs_compiler)
def on_Assign(self, stmt): def on_Assign(self, stmt):
shape = stmt.lhs.shape() shape = stmt.lhs.shape()
@ -287,10 +297,11 @@ class Simulator:
self._domain_triggers = SignalDict() # Signal -> str/domain self._domain_triggers = SignalDict() # Signal -> str/domain
self._domain_signals = dict() # str/domain -> {Signal} self._domain_signals = dict() # str/domain -> {Signal}
self._signals = SignalSet() # {Signal} self._signals = SignalSet() # {Signal}
self._comb_signals = SignalSet() # {Signal} self._comb_signals = SignalSet() # {Signal}
self._sync_signals = SignalSet() # {Signal} self._sync_signals = SignalSet() # {Signal}
self._user_signals = SignalSet() # {Signal} self._user_signals = SignalSet() # {Signal}
self._signal_slots = SignalDict() # Signal -> int/slot
self._started = False self._started = False
self._timestamp = 0. self._timestamp = 0.
@ -393,12 +404,14 @@ class Simulator:
for fragment, fragment_scope in hierarchy.items(): for fragment, fragment_scope in hierarchy.items():
for signal in fragment.iter_signals(): for signal in fragment.iter_signals():
self._signals.add(signal) if signal not in self._signals:
self._signals.add(signal)
self._state.curr[signal] = self._state.next[signal] = \ signal_slot = self._state.add(signal, normalize(signal.reset, signal.shape()))
normalize(signal.reset, signal.shape()) self._signal_slots[signal] = signal_slot
self._state.curr_dirty.add(signal)
for fragment, fragment_scope in hierarchy.items():
for signal in fragment.iter_signals():
if not self._vcd_writer: if not self._vcd_writer:
continue continue
@ -451,7 +464,7 @@ class Simulator:
statements.append(signal.eq(signal)) statements.append(signal.eq(signal))
statements += fragment.statements statements += fragment.statements
compiler = _StatementCompiler() compiler = _StatementCompiler(self._signal_slots)
funclet = compiler(statements) funclet = compiler(statements)
def add_funclet(signal, funclet): def add_funclet(signal, funclet):
@ -491,7 +504,7 @@ class Simulator:
# Take the computed value (at the start of this delta cycle) of a signal (that could have # Take the computed value (at the start of this delta cycle) of a signal (that could have
# come from an IR process that ran earlier, or modified by a simulator process) and update # come from an IR process that ran earlier, or modified by a simulator process) and update
# the value for this delta cycle. # the value for this delta cycle.
old, new = self._state.commit(signal) old, new = self._state.commit(signal, self._signal_slots[signal])
# If the signal is a clock that triggers synchronous logic, record that fact. # If the signal is a clock that triggers synchronous logic, record that fact.
if (old, new) == (0, 1) and signal in self._domain_triggers: if (old, new) == (0, 1) and signal in self._domain_triggers:
@ -573,7 +586,7 @@ class Simulator:
self._passive.add(process) self._passive.add(process)
elif isinstance(cmd, Value): elif isinstance(cmd, Value):
compiler = _RHSValueCompiler() compiler = _RHSValueCompiler(self._signal_slots)
funclet = compiler(cmd) funclet = compiler(cmd)
cmd = process.send(funclet(self._state)) cmd = process.send(funclet(self._state))
continue continue
@ -591,7 +604,7 @@ class Simulator:
"simulation" "simulation"
.format(self._name_process(process), signal)) .format(self._name_process(process), signal))
compiler = _StatementCompiler() compiler = _StatementCompiler(self._signal_slots)
funclet = compiler(cmd) funclet = compiler(cmd)
funclet(self._state) funclet(self._state)