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  }