|
2 | 2 |
|
3 | 3 | from smmap.mman import * |
4 | 4 | from smmap.mman import MemoryCursor |
5 | | -from smmap.util import PAGESIZE |
6 | | - |
| 5 | +from smmap.util import PAGESIZE, align_to_page |
7 | 6 | from smmap.exc import RegionCollectionError |
8 | 7 |
|
| 8 | +from random import randint |
| 9 | +from time import time |
9 | 10 | import sys |
10 | 11 | from copy import copy |
11 | 12 |
|
@@ -59,8 +60,101 @@ def test_memory_manager(self): |
59 | 60 | # raises outside of limit |
60 | 61 | self.failUnlessRaises(RegionCollectionError, man._collect_lru_region, sys.maxint) |
61 | 62 |
|
62 | | - |
63 | 63 | # use a region, verify most basic functionality |
64 | 64 | fc = FileCreator(self.k_window_test_size, "manager_test") |
65 | 65 | c = man.make_cursor(fc.path) |
66 | 66 | 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 | + |
0 commit comments