Skip to content

Commit b00bc5e

Browse files
committed
Finished implementation of buffer including a test which shows the performance should be usable in the real world. Its actually not too bad
1 parent 8670443 commit b00bc5e

4 files changed

Lines changed: 66 additions & 32 deletions

File tree

smmap/buf.py

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,40 @@ def __del__(self):
4444

4545
def __getitem__(self, i):
4646
c = self._c
47+
assert c.is_valid()
4748
if not c.includes_ofs(i):
4849
c.use_region(i, 1)
4950
# END handle region usage
50-
assert c.is_valid() # TODO: remove for performance
51-
return c.buffer()[i]
51+
return c.buffer()[i-c.ofs_begin()]
5252

5353
def __getslice__(self, i, j):
5454
c = self._c
5555
# fast path, slice fully included - safes a concatenate operation and
5656
# should be the default
57-
if c.ofs_begin() >= i and j < c.ofs_end():
58-
return c.buffer()[i:j]
59-
raise NotImplementedError()
57+
assert c.is_valid()
58+
if (c.ofs_begin() <= i) and (j < c.ofs_end()):
59+
b = c.ofs_begin()
60+
return c.buffer()[i-b:j-b]
61+
else:
62+
l = j-i # total length
63+
ofs = i
64+
# keep tokens, and join afterwards. This is faster
65+
# as it can preallocate the total amoint of space needed
66+
# (and its verified the implementation does that)
67+
# Question is whether the list allocation doesn't counteract this,
68+
# but lets see ...
69+
tokens = list()
70+
tappend = tokens.append
71+
72+
while l:
73+
c.use_region(ofs, l)
74+
d = c.buffer()[:l]
75+
ofs += len(d)
76+
l -= len(d)
77+
tappend(d)
78+
#END while there are bytes to read
79+
return ''.join(tokens)
80+
# END fast or slow path
6081
#{ Interface
6182

6283
def begin_access(self, path = None, offset = 0, size = sys.maxint, flags = 0):

smmap/mman.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,8 @@ def unuse_region(self):
222222
to unuse the region once you are done reading from it in persistent cursors as it
223223
helps to free up resource more quickly"""
224224
self._region = None
225+
# note: should reset ofs and size, but we spare that for performance. Its not
226+
# allowed to query information if we are not valid !
225227

226228
def buffer(self):
227229
"""Return a buffer object which allows access to our memory region from our offset
@@ -240,7 +242,8 @@ def is_associated(self):
240242
return self._rlist is not None
241243

242244
def ofs_begin(self):
243-
""":return: offset to the first byte pointed to by our cursor"""
245+
""":return: offset to the first byte pointed to by our cursor
246+
:note: only if is_valid() is True"""
244247
return self._region._b + self._ofs
245248

246249
def ofs_end(self):

smmap/test/test_buf.py

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -61,32 +61,42 @@ def test_basics(self):
6161
# exagerate the manager's overhead, but measure the buffer overhead
6262
# We do it once with an optimal setting, and with a worse manager which
6363
# will produce small mappings only !
64-
max_num_accesses = 5000
65-
num_accesses_left = max_num_accesses
66-
67-
for manager in (MappedMemoryManager(window_size=fc.size/100, max_memory_size=fc.size/3, max_open_handles=15), man):
64+
max_num_accesses = 1000
65+
for manager, man_id in ( (man, 'optimal'),
66+
(MappedMemoryManager(window_size=fc.size/100, max_memory_size=fc.size/3, max_open_handles=15), 'worst case')):
6867
TestBuffer.manager = manager
69-
st = time()
7068
buf = TestBuffer(fc.path)
7169
assert manager.num_file_handles() == 1
72-
num_bytes = 0
73-
fsize = fc.size
74-
while num_accesses_left:
75-
num_accesses_left -= 1
76-
ofs_start = randint(0, fsize)
77-
ofs_end = randint(ofs_start, fsize)
78-
d = buf[ofs_start:ofs_end]
79-
assert len(d) == ofs_end - ofs_start
80-
assert d == data[ofs_start:ofs_end]
81-
num_bytes += len(d)
82-
pos = randint(0, fsize)
83-
assert buf[pos] == data[pos]
84-
# END handle num accesses
85-
buf.end_access()
86-
assert manager.num_file_handles() == 1
87-
assert manager.collect() == 1
88-
assert manager.num_file_handles() == 0
89-
elapsed = time() - st
90-
mb = 1000*1000
91-
sys.stderr.write("Made %i random slices to buffer reading a total of %f mb in %f s (%f mb/s)\n" % (max_num_accesses, num_bytes/mb, elapsed, (num_bytes/mb)/elapsed))
70+
for access_mode in range(2): # single, multi
71+
num_accesses_left = max_num_accesses
72+
num_bytes = 0
73+
fsize = fc.size
74+
75+
st = time()
76+
buf.begin_access()
77+
while num_accesses_left:
78+
num_accesses_left -= 1
79+
if access_mode: # multi
80+
ofs_start = randint(0, fsize)
81+
ofs_end = randint(ofs_start, fsize)
82+
d = buf[ofs_start:ofs_end]
83+
assert len(d) == ofs_end - ofs_start
84+
assert d == data[ofs_start:ofs_end]
85+
num_bytes += len(d)
86+
else:
87+
pos = randint(0, fsize)
88+
assert buf[pos] == data[pos]
89+
num_bytes += 1
90+
#END handle mode
91+
# END handle num accesses
92+
93+
buf.end_access()
94+
assert manager.num_file_handles()
95+
assert manager.collect()
96+
assert manager.num_file_handles() == 0
97+
elapsed = time() - st
98+
mb = float(1000*1000)
99+
mode_str = (access_mode and "slice") or "single byte"
100+
sys.stderr.write("%s: Made %i random %s accesses to buffer reading a total of %f mb in %f s (%f mb/s)\n" % (man_id, max_num_accesses, mode_str, num_bytes/mb, elapsed, (num_bytes/mb)/elapsed))
101+
# END handle access mode
92102
# END for each manager

smmap/test/test_mman.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def test_memman_operation(self):
158158
assert not includes_ofs(base_offset+csize)
159159
# END while we should do an access
160160
elapsed = time() - st
161-
mb = 1000 * 1000
161+
mb = float(1000 * 1000)
162162
sys.stderr.write("Read %i mb of memory with %i random accesses in %fs (%f mb/s)\n"
163163
% (memory_read/mb, max_random_accesses, elapsed, (memory_read/mb)/elapsed))
164164

0 commit comments

Comments
 (0)