github.com/bhojpur/cache@v0.0.4/pkg/memory/freelist_hmap.go (about) 1 package memory 2 3 // Copyright (c) 2018 Bhojpur Consulting Private Limited, India. All rights reserved. 4 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 23 import "sort" 24 25 // hashmapFreeCount returns count of free pages(hashmap version) 26 func (f *freelist) hashmapFreeCount() int { 27 // use the forwardMap to get the total count 28 count := 0 29 for _, size := range f.forwardMap { 30 count += int(size) 31 } 32 return count 33 } 34 35 // hashmapAllocate serves the same purpose as arrayAllocate, but use hashmap as backend 36 func (f *freelist) hashmapAllocate(txid txid, n int) pgid { 37 if n == 0 { 38 return 0 39 } 40 41 // if we have a exact size match just return short path 42 if bm, ok := f.freemaps[uint64(n)]; ok { 43 for pid := range bm { 44 // remove the span 45 f.delSpan(pid, uint64(n)) 46 47 f.allocs[pid] = txid 48 49 for i := pgid(0); i < pgid(n); i++ { 50 delete(f.cache, pid+i) 51 } 52 return pid 53 } 54 } 55 56 // lookup the map to find larger span 57 for size, bm := range f.freemaps { 58 if size < uint64(n) { 59 continue 60 } 61 62 for pid := range bm { 63 // remove the initial 64 f.delSpan(pid, size) 65 66 f.allocs[pid] = txid 67 68 remain := size - uint64(n) 69 70 // add remain span 71 f.addSpan(pid+pgid(n), remain) 72 73 for i := pgid(0); i < pgid(n); i++ { 74 delete(f.cache, pid+i) 75 } 76 return pid 77 } 78 } 79 80 return 0 81 } 82 83 // hashmapReadIDs reads pgids as input an initial the freelist(hashmap version) 84 func (f *freelist) hashmapReadIDs(pgids []pgid) { 85 f.init(pgids) 86 87 // Rebuild the page cache. 88 f.reindex() 89 } 90 91 // hashmapGetFreePageIDs returns the sorted free page ids 92 func (f *freelist) hashmapGetFreePageIDs() []pgid { 93 count := f.free_count() 94 if count == 0 { 95 return nil 96 } 97 98 m := make([]pgid, 0, count) 99 for start, size := range f.forwardMap { 100 for i := 0; i < int(size); i++ { 101 m = append(m, start+pgid(i)) 102 } 103 } 104 sort.Sort(pgids(m)) 105 106 return m 107 } 108 109 // hashmapMergeSpans try to merge list of pages(represented by pgids) with existing spans 110 func (f *freelist) hashmapMergeSpans(ids pgids) { 111 for _, id := range ids { 112 // try to see if we can merge and update 113 f.mergeWithExistingSpan(id) 114 } 115 } 116 117 // mergeWithExistingSpan merges pid to the existing free spans, try to merge it backward and forward 118 func (f *freelist) mergeWithExistingSpan(pid pgid) { 119 prev := pid - 1 120 next := pid + 1 121 122 preSize, mergeWithPrev := f.backwardMap[prev] 123 nextSize, mergeWithNext := f.forwardMap[next] 124 newStart := pid 125 newSize := uint64(1) 126 127 if mergeWithPrev { 128 //merge with previous span 129 start := prev + 1 - pgid(preSize) 130 f.delSpan(start, preSize) 131 132 newStart -= pgid(preSize) 133 newSize += preSize 134 } 135 136 if mergeWithNext { 137 // merge with next span 138 f.delSpan(next, nextSize) 139 newSize += nextSize 140 } 141 142 f.addSpan(newStart, newSize) 143 } 144 145 func (f *freelist) addSpan(start pgid, size uint64) { 146 f.backwardMap[start-1+pgid(size)] = size 147 f.forwardMap[start] = size 148 if _, ok := f.freemaps[size]; !ok { 149 f.freemaps[size] = make(map[pgid]struct{}) 150 } 151 152 f.freemaps[size][start] = struct{}{} 153 } 154 155 func (f *freelist) delSpan(start pgid, size uint64) { 156 delete(f.forwardMap, start) 157 delete(f.backwardMap, start+pgid(size-1)) 158 delete(f.freemaps[size], start) 159 if len(f.freemaps[size]) == 0 { 160 delete(f.freemaps, size) 161 } 162 } 163 164 // initial from pgids using when use hashmap version 165 // pgids must be sorted 166 func (f *freelist) init(pgids []pgid) { 167 if len(pgids) == 0 { 168 return 169 } 170 171 size := uint64(1) 172 start := pgids[0] 173 174 if !sort.SliceIsSorted([]pgid(pgids), func(i, j int) bool { return pgids[i] < pgids[j] }) { 175 panic("pgids not sorted") 176 } 177 178 f.freemaps = make(map[uint64]pidSet) 179 f.forwardMap = make(map[pgid]uint64) 180 f.backwardMap = make(map[pgid]uint64) 181 182 for i := 1; i < len(pgids); i++ { 183 // continuous page 184 if pgids[i] == pgids[i-1]+1 { 185 size++ 186 } else { 187 f.addSpan(start, size) 188 189 size = 1 190 start = pgids[i] 191 } 192 } 193 194 // init the tail 195 if size != 0 && start != 0 { 196 f.addSpan(start, size) 197 } 198 }