MemMMap/SimState: speed up SimState.get_mem() for large memories
authorJacob Lifshay <programmerjake@gmail.com>
Sun, 3 Dec 2023 07:57:21 +0000 (23:57 -0800)
committerJacob Lifshay <programmerjake@gmail.com>
Wed, 13 Dec 2023 00:59:12 +0000 (16:59 -0800)
make MemMMap use struct.Struct.unpack_from to read a whole page at
once, rather than doing a sequence of loads. This makes an ELF
binary statically-linked to glibc able to run many instructions per
second rather than one every tens of seconds or so.

src/openpower/decoder/isa/mem.py
src/openpower/decoder/isa/test_mem.py
src/openpower/test/state.py

index 2b3ec844436230e3fb285ba83396ab378afacf69..a5867f3d3bfa7d4623d75d72b0b8a75aaafcdeae 100644 (file)
@@ -19,6 +19,7 @@ import math
 import enum
 from cached_property import cached_property
 import mmap
+import struct
 from pickle import PicklingError
 import ctypes
 from nmutil import plain_data
@@ -115,6 +116,19 @@ class MemCommon:
         raise NotImplementedError
         yield 0
 
+    def make_sim_state_dict(self):
+        """ returns a dict equivalent to:
+        retval = {}
+        for k in list(self.word_idxs()):
+            data = self.ld(k*8, 8, False)
+            retval[k*8] = data
+        """
+        retval = {}
+        for k in list(self.word_idxs()):
+            data = self.ld(k*8, 8, False, reason=_ReadReason.Dump)
+            retval[k*8] = data
+        return retval
+
     def _get_shifter_mask(self, wid, remainder, do_log=True):
         shifter = ((self.bytes_per_word - wid) - remainder) * \
             8  # bits per byte
@@ -1024,6 +1038,29 @@ class MemMMap(MemCommon):
                 if bytes_ != zeros:
                     yield word_idx
 
+    def make_sim_state_dict(self):
+        """ returns a dict equivalent to:
+        retval = {}
+        for k in list(self.word_idxs()):
+            data = self.ld(k*8, 8, False)
+            retval[k*8] = data
+        """
+        if self.bytes_per_word != 8:
+            return super().make_sim_state_dict()
+        retval = {}
+        page_struct = struct.Struct("<%dQ" % (MMAP_PAGE_SIZE // 8,))
+        assert page_struct.size == MMAP_PAGE_SIZE, "got wrong format"
+        for page_idx in self.modified_pages:
+            start = self.mmap_page_idx_to_addr(page_idx)
+            block, block_addr = self.__access_addr_range(
+                start, MMAP_PAGE_SIZE, MMapPageFlags.R)
+            # written this way to avoid unnecessary allocations
+            words = page_struct.unpack_from(block, block_addr)
+            for i, v in zip(range(start, start + MMAP_PAGE_SIZE, 8), words):
+                if v != 0:
+                    retval[i] = v
+        return retval
+
 
 @plain_data.plain_data()
 class LoadedELF:
index 04d3f70c168cbdef1c479e790fd1d89972432cc7..3180f0fb521e32f32e954869e7a2b890f85f7338 100644 (file)
@@ -80,6 +80,36 @@ Memory:
 0x7FFFFEDCBA90:  00 00 00 00 00 00 00 00  89 67 45 23 01 EF CD AB  |.........gE#....|
 """)
 
+    def test_make_sim_state_dict(self):
+        m = self.MemCls(row_bytes=8, initial_mem=(0x58, [
+            0x5DE6DA2A1137745E, 0x6054D17B4C773D2D,
+            0x5B66920D9540B825, 0x7753D053D9854A8F,
+            0x9F2A58E2B5B79829, 0x974AC142D081CE83,
+            0xAA963F95FC566F57, 0xE63A95A3F654A57E,
+            0x103709510CBE0EEF, 0,
+            0x5053575776376ACD, 0xCFDFF67B7C5096C2,
+            0x9F8FC1B06E7868A0, 0x6E7B1D27CCBAF8E7,
+            0xEB91B92FAF546BA1, 0x21FB683F34641876,
+        ]))
+
+        a = m.make_sim_state_dict()
+        b = m.make_sim_state_dict()
+        self.assertIsNot(a, b)  # must always return a new dict
+        self.assertEqual(a, b)  # should return the same result
+        expected = {
+            0x58: 0x5DE6DA2A1137745E, 0x60: 0x6054D17B4C773D2D,
+            0x68: 0x5B66920D9540B825, 0x70: 0x7753D053D9854A8F,
+            0x78: 0x9F2A58E2B5B79829, 0x80: 0x974AC142D081CE83,
+            0x88: 0xAA963F95FC566F57, 0x90: 0xE63A95A3F654A57E,
+            0x98: 0x103709510CBE0EEF, 0xA0: 0,
+            0xA8: 0x5053575776376ACD, 0xB0: 0xCFDFF67B7C5096C2,
+            0xB8: 0x9F8FC1B06E7868A0, 0xC0: 0x6E7B1D27CCBAF8E7,
+            0xC8: 0xEB91B92FAF546BA1, 0xD0: 0x21FB683F34641876,
+        }
+        if isinstance(m, MemMMap):
+            del expected[0xA0]  # MemMMap doesn't list zero entries
+        self.assertEqual(a, expected)
+
 
 class TestMemMMap(TestMemCommon):
     MemCls = MemMMap
index 50a015208c587d167a0cb6ab3a92c614a0a90cb2..a8bec4729726e73669be57925f9390bdc715fef4 100644 (file)
@@ -425,13 +425,7 @@ class SimState(State):
         mem = self.sim.mem
         if isinstance(mem, RADIX):
             mem = mem.mem
-        keys = list(mem.word_idxs())
-        self.mem = {}
-        # from each address in the underlying mem-simulated dictionary
-        # issue a 64-bit LD (with no byte-swapping)
-        for k in keys:
-            data = mem.ld(k*8, 8, False)
-            self.mem[k*8] = data
+        self.mem = mem.make_sim_state_dict()
 
 
 class ExpectedState(State):