Skip to content

Commit 997a580

Browse files
committed
implemented plenty of operational testing. It shows that its not yet working properly. Intersting, it was so promising, and went just a little bit too smooth. There we go :)
1 parent e7b0e1c commit 997a580

3 files changed

Lines changed: 106 additions & 8 deletions

File tree

smmap/mman.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
)
99

1010
from exc import RegionCollectionError
11-
from weakref import proxy
11+
from weakref import ref
1212
import sys
1313

1414
__all__ = ["MappedMemoryManager"]
@@ -100,7 +100,7 @@ def use_region(self, offset, size, _is_recursive=False):
100100

101101
if need_region:
102102
# abort on offsets beyond our mapped file's size - currently we are invalid
103-
if offset > self.file_size():
103+
if offset >= self.file_size():
104104
return self
105105
# END handle offset too large
106106

@@ -222,16 +222,21 @@ def ofs_begin(self):
222222
""":return: offset to the first byte pointed to by our cursor"""
223223
return self._region.ofs_begin() + self._ofs
224224

225+
def ofs_end(self):
226+
""":return: offset to one past the last available byte"""
227+
# unroll method calls for performance !
228+
return self._region.ofs_begin() + self._ofs + self._size
229+
225230
def size(self):
226231
""":return: amount of bytes we point to"""
227232
return self._size
228233

229234
def region_ref(self):
230-
""":return: weak proxy to our mapped region.
235+
""":return: weak ref to our mapped region.
231236
:raise AssertionError: if we have no current region. This is only useful for debugging"""
232237
if self._region is None:
233238
raise AssertionError("region not set")
234-
return proxy(self._region)
239+
return ref(self._region)
235240

236241
def includes_ofs(self, ofs):
237242
""":return: True if the given absolute offset is contained in the cursors

smmap/test/test_mman.py

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22

33
from smmap.mman import *
44
from smmap.mman import MemoryCursor
5-
from smmap.util import PAGESIZE
6-
5+
from smmap.util import PAGESIZE, align_to_page
76
from smmap.exc import RegionCollectionError
87

8+
from random import randint
9+
from time import time
910
import sys
1011
from copy import copy
1112

@@ -59,8 +60,101 @@ def test_memory_manager(self):
5960
# raises outside of limit
6061
self.failUnlessRaises(RegionCollectionError, man._collect_lru_region, sys.maxint)
6162

62-
6363
# use a region, verify most basic functionality
6464
fc = FileCreator(self.k_window_test_size, "manager_test")
6565
c = man.make_cursor(fc.path)
6666
assert c.use_region(10, 10).is_valid()
67+
assert c.ofs_begin() == 10
68+
assert c.size() == 10
69+
assert c.buffer()[:] == open(fc.path, 'rb').read(20)[10:]
70+
71+
def test_memman_operation(self):
72+
# test more access, force it to actually unmap regions
73+
fc = FileCreator(self.k_window_test_size, "manager_operation_test")
74+
data = open(fc.path, 'rb').read()
75+
assert len(data) == fc.size
76+
77+
# small windows, a reasonable max memory. Not too many regions at once
78+
man = MappedMemoryManager(fc.size / 100, fc.size / 3, 15)
79+
c = man.make_cursor(fc.path)
80+
81+
# still empty (more about that is tested in test_memory_manager()
82+
assert man.num_open_files() == 0
83+
assert man.mapped_memory_size() == 0
84+
85+
base_offset = 5000
86+
size = man.window_size() / 2
87+
assert c.use_region(base_offset, size).is_valid()
88+
rr = c.region_ref()
89+
assert rr().client_count() == 2 # the manager and the cursor and us
90+
91+
assert man.num_open_files() == 1
92+
assert man.num_file_handles() == 1
93+
assert man.mapped_memory_size() == rr().size()
94+
assert c.size() == size
95+
assert c.ofs_begin() == base_offset
96+
assert rr().ofs_begin() == 0 # it was aligned and expanded
97+
assert rr().size() == align_to_page(man.window_size(), True) # but isn't larger than the max window (aligned)
98+
99+
assert c.buffer()[:] == data[base_offset:base_offset+size]
100+
101+
# obtain second window, which spans the first part of the file - it is a still the same window
102+
assert c.use_region(0, size-10).is_valid()
103+
assert c.region_ref()() == rr()
104+
assert man.num_file_handles() == 1
105+
assert c.size() == size-10
106+
assert c.ofs_begin() == 0
107+
assert c.buffer()[:] == data[:size-10]
108+
109+
# map some part at the end, our requested size cannot be kept
110+
overshoot = 4000
111+
base_offset = fc.size - size + overshoot
112+
assert c.use_region(base_offset, size).is_valid()
113+
assert man.num_file_handles() == 2
114+
assert c.size() < size
115+
assert c.region_ref()() is not rr() # old region is still available, but has not curser ref anymore
116+
assert rr().client_count() == 1 # only held by manager
117+
rr = c.region_ref()
118+
assert rr().client_count() == 2 # manager + cursor
119+
assert rr().ofs_begin() < c.ofs_begin() # it should have extended itself to the left
120+
assert rr().ofs_end() <= fc.size # it cannot be larger than the file
121+
assert c.buffer()[:] == data[base_offset:base_offset+size]
122+
123+
# unising a region makes the cursor invalid
124+
c.unuse_region()
125+
assert not c.is_valid()
126+
# but doesn't change anything regarding the handle count - we cache it and only
127+
# remove mapped regions if we have to
128+
assert man.num_file_handles() == 2
129+
130+
# an offset as large as the size doesn't work !
131+
assert not c.use_region(fc.size, size).is_valid()
132+
133+
# iterate through the windows, verify data contents
134+
# this will trigger map collection after a while
135+
max_random_accesses = 15000
136+
num_random_accesses = max_random_accesses
137+
memory_read = 0
138+
st = time()
139+
140+
while num_random_accesses:
141+
num_random_accesses += 1
142+
base_offset = randint(0, fc.size - 1)
143+
144+
# precondition
145+
assert man.max_mapped_memory_size() >= man.mapped_memory_size()
146+
assert man.max_file_handles() >= man.num_file_handles()
147+
148+
assert c.use_region(base_offset, size).is_valid()
149+
assert c.buffer()[:] == data[base_offset:base_offset+size]
150+
memory_read += c.size()
151+
152+
assert c.includes_ofs(base_offset)
153+
assert c.includes_ofs(base_offset+c.size()-1)
154+
assert not c.includes_ofs(base_offset+c.size())
155+
# END while we should do an access
156+
elapsed = time() - st
157+
mb = 1000 * 1000
158+
sys.stderr.write("Read %i mb of memory with %i random accesses in %f s(%f mb/s)\n"
159+
% (memory_read/mb, max_random_accesses, elapsed, (memory_read/mb)/elapsed))
160+

smmap/util.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ class MappedRegion(object):
8383
'_b' , # beginning of mapping
8484
'_mf', # mapped memory chunk (as returned by mmap)
8585
'_uc', # total amount of usages
86-
'_ms' # actual size of the mapping
8786
'__weakref__'
8887
]
8988
_need_compat_layer = sys.version_info[1] < 6

0 commit comments

Comments
 (0)