Skip to content

Commit c5815d0

Browse files
committed
Add object caching
1 parent d5ac852 commit c5815d0

6 files changed

Lines changed: 69 additions & 46 deletions

File tree

pkg-runtime/src/peakrdl_pyral_runtime/dbapi.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from collections import OrderedDict
55
import importlib
66
import weakref
7+
from functools import lru_cache
78

89
from .model import RALRegister, RALField, RALGroup, RALArray, RegValue
910
from .model import AddressableRALNode
@@ -60,7 +61,7 @@ def __init__(self, path: str, origin_module_name: str) -> None:
6061
# Close the DB connection when this DBAPI gets garbage collected
6162
weakref.finalize(self, self.db.close)
6263

63-
64+
@lru_cache()
6465
def get_root(self, offset: int = 0) -> RALGroup:
6566
"""
6667
Get the root node of a RAL DB
@@ -122,6 +123,7 @@ def get_ref_dbapi(self, ref_dbid: int) -> "DBAPI":
122123

123124
return new_dbapi
124125

126+
@lru_cache()
125127
def get_child(self, parent: AddressableRALNode, child_name: str) -> Union[None, RALArray, RALRegister, RALGroup, RALField]:
126128
"""
127129
Get a single child of a node by name
@@ -141,7 +143,8 @@ def get_child(self, parent: AddressableRALNode, child_name: str) -> Union[None,
141143

142144
return self.build_child(parent, row)
143145

144-
def get_children(self, parent: AddressableRALNode) -> list[Union[RALArray, RALRegister, RALGroup, RALField]]:
146+
@lru_cache()
147+
def get_children(self, parent: AddressableRALNode) -> tuple[Union[RALArray, RALRegister, RALGroup, RALField], ...]:
145148
"""
146149
Get all the children of a node
147150
"""
@@ -152,9 +155,10 @@ def get_children(self, parent: AddressableRALNode) -> list[Union[RALArray, RALRe
152155
)
153156
rows = cur.fetchall()
154157
cur.close()
155-
return [self.build_child(parent, row) for row in rows]
158+
return tuple([self.build_child(parent, row) for row in rows])
156159

157-
def regvalue_from_int(self, parent_reg_dbid: int, reg_value: int) -> RegValue:
160+
@lru_cache()
161+
def get_regvalue_spec(self, parent_reg_dbid: int) -> dict[str, tuple[int, int]]:
158162
"""
159163
Convert a raw integer value to a field-aware RegValue
160164
"""
@@ -172,6 +176,13 @@ def regvalue_from_int(self, parent_reg_dbid: int, reg_value: int) -> RegValue:
172176
for row in rows:
173177
spec[row["name"]] = (row["offset"], row["size"])
174178

179+
return spec
180+
181+
def regvalue_from_int(self, parent_reg_dbid: int, reg_value: int) -> RegValue:
182+
"""
183+
Convert a raw integer value to a field-aware RegValue
184+
"""
185+
spec = self.get_regvalue_spec(parent_reg_dbid)
175186
return RegValue(reg_value, spec)
176187

177188
def build_child(self, parent: AddressableRALNode, row: sqlite3.Row) -> Union[RALArray, RALRegister, RALGroup, RALField]:

pkg-runtime/src/peakrdl_pyral_runtime/hwio/openocd.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,16 @@ def _cmd(self, cmd: str) -> str:
5454
self._sock.sendall(data)
5555

5656
# Get response
57-
data = bytearray()
57+
rdata = bytearray()
5858
while True:
5959
chunk = self._sock.recv(4096)
60-
data.extend(chunk)
60+
rdata.extend(chunk)
6161
if chunk.endswith(RPC_TERM):
6262
break
6363

6464
# strip trailing RPC_TERM token
65-
data = data[:-1]
66-
return data.decode("utf-8").strip()
65+
rdata = rdata[:-1]
66+
return rdata.decode("utf-8").strip()
6767

6868
def _read_impl(self, addr: int, size: int) -> int:
6969
suffix = SUFFIX_MAP[size]

pkg-runtime/src/peakrdl_pyral_runtime/model/array.py

Lines changed: 37 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import TYPE_CHECKING, Any, Union, overload
22
from collections.abc import Sequence
3+
from functools import lru_cache
34

45
if TYPE_CHECKING:
56
import sqlite3
@@ -36,6 +37,40 @@ def __repr__(self) -> str:
3637
suffix += "[]" * (len(self._dims) - len(self._resolved_dims))
3738
return f"<RALArray of {obj_name}: {self._parent.path}.{self._row['name']}{suffix}>"
3839

40+
@lru_cache()
41+
def _get_single_item(self, idx: int) -> "RALChild":
42+
dim = self._dims[self._this_dim_idx]
43+
44+
if idx >= dim or idx < 0:
45+
raise IndexError("array index out of range")
46+
47+
# Update known dimensions
48+
resolved_dims = self._resolved_dims.copy()
49+
resolved_dims.append(idx)
50+
51+
if len(resolved_dims) != len(self._dims):
52+
# Multi-dimensional array still has unresolved dimensions
53+
# Create another array
54+
return RALArray(
55+
self._parent,
56+
self._dbapi,
57+
resolved_dims,
58+
self._dims,
59+
self._row,
60+
)
61+
62+
# All dimensions are known. Create the actual node
63+
flat_idx = 0
64+
for i, current_idx in enumerate(resolved_dims):
65+
sz = 1
66+
for j in range(i + 1, len(self._dims)):
67+
sz *= self._dims[j]
68+
flat_idx += sz * current_idx
69+
array_offset = flat_idx * self._row["stride"]
70+
71+
array_suffix = "".join([f"[{d}]" for d in resolved_dims])
72+
return self._dbapi.build_node(self._parent, self._row, array_suffix, array_offset)
73+
3974
@overload
4075
def __getitem__(self, subscript: int) -> "RALChild": ...
4176

@@ -63,44 +98,15 @@ def __getitem__(self, subscript: Any) -> Union["RALChild", list["RALChild"]]:
6398
# This is consistent with Python's behavior for lists
6499
start = max(start, 0)
65100
stop = min(stop, dim)
66-
return [self[i] for i in range(start, stop, step)]
101+
return [self._get_single_item(i) for i in range(start, stop, step)]
67102
elif not isinstance(subscript, int):
68103
raise TypeError("array indices must be integers or slices")
69104

70-
71105
# Handle negative subscripts that index from end of array
72106
if subscript < 0:
73107
subscript += dim
74108

75-
if subscript >= dim or subscript < 0:
76-
raise IndexError("array index out of range")
77-
78-
# Update known dimensions
79-
resolved_dims = self._resolved_dims.copy()
80-
resolved_dims.append(subscript)
81-
82-
if len(resolved_dims) != len(self._dims):
83-
# Multi-dimensional array still has unresolved dimensions
84-
# Create another array
85-
return RALArray(
86-
self._parent,
87-
self._dbapi,
88-
resolved_dims,
89-
self._dims,
90-
self._row,
91-
)
92-
93-
# All dimensions are known. Create the actual node
94-
flat_idx = 0
95-
for i, current_idx in enumerate(resolved_dims):
96-
sz = 1
97-
for j in range(i + 1, len(self._dims)):
98-
sz *= self._dims[j]
99-
flat_idx += sz * current_idx
100-
array_offset = flat_idx * self._row["stride"]
101-
102-
array_suffix = "".join([f"[{d}]" for d in resolved_dims])
103-
return self._dbapi.build_node(self._parent, self._row, array_suffix, array_offset)
109+
return self._get_single_item(subscript)
104110

105111
def __len__(self) -> int:
106112
return self._dims[self._this_dim_idx]

pkg-runtime/src/peakrdl_pyral_runtime/model/base.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,13 @@ def __getattr__(self, name: str) -> Union["RALArray", "RALRegister", "RALGroup",
5858
raise AttributeError(f"'{repr(self)}' has no attribute '{name}'")
5959
return child
6060

61-
def children(self) -> list[Union["RALArray", "RALRegister", "RALGroup", "RALField"]]:
61+
def children(self) -> tuple[Union["RALArray", "RALRegister", "RALGroup", "RALField"], ...]:
6262
"""
63-
Returns a list of child RAL elements
63+
Returns a tuple of child RAL elements
6464
"""
6565
return self._dbapi.get_children(self)
6666

6767
def _lookup_hwio(self) -> tuple["HWIO", int]:
68-
# TODO: Implement HWIO object caching
6968
if self._hwio is not None:
7069
# This node has a HWIO bound to it
7170
if self.parent is None:

test/benchmark/benchmark_ral.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from systemrdl.compiler import RDLCompiler
77
from peakrdl_pyral.exporter import PyRALExporter
8-
from peakrdl_pyral_runtime.hwio.demo import DemoHWIO
8+
from peakrdl_pyral_runtime.hwio import HWIO
99

1010
this_dir = os.path.normpath(os.path.dirname(__file__))
1111

@@ -16,15 +16,21 @@
1616
e = PyRALExporter()
1717
e.export(root.top, this_dir)
1818

19+
# Make a dummy HWIO
20+
class DummyHWIO(HWIO):
21+
def _read_impl(self, addr: int, size: int) -> int:
22+
return 0
23+
def _write_impl(self, addr: int, value: int, size: int) -> None:
24+
pass
1925

2026
# Prepare ral
2127
import structural
2228
ral = structural.get_ral()
23-
hwio = DemoHWIO()
29+
hwio = DummyHWIO()
2430
ral.attach_hwio(hwio)
2531

2632
def test_func():
2733
ral.sub2[4].sub[0].r1.read()
2834

29-
result = timeit.timeit("test_func()", globals=globals(), number=100_000)
35+
result = timeit.timeit("test_func()", globals=globals(), number=200_000)
3036
print(result)

test/pylint.rc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ disable=
9696
protected-access,
9797
duplicate-code,
9898
unused-argument,
99-
consider-using-f-string
99+
consider-using-f-string,
100+
consider-using-generator
100101

101102
# Enable the message, report, category or checker with the given id(s). You can
102103
# either give multiple identifier separated by comma (,) or put this option

0 commit comments

Comments
 (0)