github.com/vuuihc/gocedar@v0.1.0/mmap.go (about) 1 package cedar 2 3 // mmap 为 cedar 提供 initData, addBlock 等接口,使得 cedar 中读取和写入的 array等信息直接 map 到文件中,做到 demanding page 4 // 输入:mmapDir,存放 mmap 文件的目录。由于要 map 的内容主要是3个slice,因而直接用3个文件映射。 5 // 输出:initData,addBlock 接口用来初始化相关数据、扩容 6 7 import ( 8 "fmt" 9 "math" 10 "os" 11 "path" 12 "syscall" 13 "unsafe" 14 ) 15 16 const ( 17 defaultMaxFileSize = 1 << 34 // 文件最大为 16G 18 defaultMaxSize = defaultMaxFileSize / int(unsafe.Sizeof(Node{})) // 最大插入10亿个key 19 20 nodeSize = int(unsafe.Sizeof(Node{})) 21 nInfoSize = int(unsafe.Sizeof(NInfo{})) 22 blockSize = int(unsafe.Sizeof(Block{})) 23 metaSize = int(unsafe.Sizeof(MetaInfo{})) 24 25 defaultNodeNumber = 256 26 27 arrayFileName = "array" 28 blockFileName = "block" // 头部存cedar里面非slice的信息 29 nInfoFileName = "nInfo" 30 fileMode = os.FileMode(0666) 31 ) 32 33 // nolint 34 type MetaInfo struct { 35 useMMap bool // determine if mmap inited 36 LoadSize int // key size when mmaped 37 Reduced bool 38 reject [257]int 39 40 blocksHeadFull int // the index of the first 'Full' block, 0 means no 'Full' block 41 blocksHeadClosed int // the index of the first 'Closed' block, 0 means no ' Closed' block 42 blocksHeadOpen int // the index of the first 'Open' block, 0 means no 'Open' block 43 44 capacity int 45 size int 46 ordered bool 47 maxTrial int // 48 } 49 50 type MMap struct { 51 loadSize int 52 mmapDir string 53 initSize int 54 array *[defaultMaxSize]Node 55 block *[defaultMaxSize >> 8]Block 56 nInfo *[defaultMaxSize]NInfo 57 metaInfo *MetaInfo 58 arrayBytes, blockBytes, nInfoBytes []byte 59 arrayFile, blockFile, nInfoFile *os.File 60 arrayMSize, blockMSize, nInfoMSize int 61 } 62 63 func NewMMap(mmapDir string) *MMap { 64 if _, err := os.Stat(mmapDir); err != nil { 65 _assert(os.MkdirAll(mmapDir, fileMode) == nil, "mkdir mmapdir fail") 66 } 67 68 // if file size isn't align, return error (not all is zero, or key size is not the same) 69 m := &MMap{ 70 mmapDir: mmapDir, 71 } 72 m.OpenFile() 73 ai, _ := m.arrayFile.Stat() 74 bi, _ := m.blockFile.Stat() 75 ni, _ := m.nInfoFile.Stat() 76 77 // check the node number is equal in every file 78 nodeNumber := ai.Size() / int64(nodeSize) 79 blockNumber := int64(math.Max(float64(int(bi.Size())-metaSize), 0)) / int64(blockSize) 80 ninfoNumber := ni.Size() / int64(nInfoSize) 81 _assert(nodeNumber>>8 == blockNumber, 82 "node number %d and block number %d not align, remove file in path and retry", nodeNumber, blockNumber) 83 _assert(nodeNumber == ninfoNumber, 84 "node number %d and ninfoNumber %d not align, remove file in path and retry", nodeNumber, ninfoNumber) 85 86 m.initSize = int(ai.Size()) / nodeSize 87 if m.initSize == 0 { 88 m.initSize = defaultNodeNumber 89 } else { 90 m.loadSize = m.initSize 91 } 92 m.allocate(m.initSize) 93 return m 94 } 95 96 func (m *MMap) OpenFile() { 97 var err error 98 m.arrayFile, err = os.OpenFile(path.Join(m.mmapDir, arrayFileName), os.O_CREATE|os.O_RDWR, fileMode) 99 _assert(err == nil, "Open arrayFile fail %v", err) 100 m.blockFile, err = os.OpenFile(path.Join(m.mmapDir, blockFileName), os.O_CREATE|os.O_RDWR, fileMode) 101 _assert(err == nil, "Open blockFile fail %v", err) 102 m.nInfoFile, err = os.OpenFile(path.Join(m.mmapDir, nInfoFileName), os.O_CREATE|os.O_RDWR, fileMode) 103 _assert(err == nil, "Open nInfoFile fail %v", err) 104 } 105 106 // initData in Cedar inplace 107 func (m *MMap) InitData(c *Cedar) { 108 c.array = m.array[:m.initSize] 109 c.blocks = m.block[:m.initSize>>8] 110 c.nInfos = m.nInfo[:m.initSize] 111 c.MetaInfo = m.metaInfo 112 c.mmap = m 113 c.LoadSize = m.loadSize 114 } 115 116 // addBlock in Cedar inplace depends on c.capacity 117 func (m *MMap) AddBlock(c *Cedar, capacity int) { 118 m.allocate(capacity) 119 120 c.MetaInfo = m.metaInfo 121 c.array = m.array[:c.capacity] 122 c.blocks = m.block[:c.capacity>>8] 123 c.nInfos = m.nInfo[:c.capacity] 124 } 125 126 // allocate remmap depends on arrayMSize blockMSize nInfoMSize 127 func (m *MMap) allocate(cap int) { 128 // compute memory size 129 m.arrayMSize = cap * nodeSize 130 m.blockMSize = metaSize + cap>>8*blockSize 131 m.nInfoMSize = cap * nInfoSize 132 133 if len(m.arrayBytes) > 0 { 134 munmap(m.arrayBytes) 135 munmap(m.blockBytes) 136 munmap(m.nInfoBytes) 137 } 138 139 // grow file to memory size 140 grow(m.arrayFile, int64(m.arrayMSize)) 141 grow(m.blockFile, int64(m.blockMSize)) 142 grow(m.nInfoFile, int64(m.nInfoMSize)) 143 144 // mmap memory 145 m.arrayBytes = mmap(m.arrayFile, int(m.arrayMSize)) 146 m.array = (*[defaultMaxSize]Node)(unsafe.Pointer(&m.arrayBytes[0])) 147 148 m.blockBytes = mmap(m.blockFile, int(m.blockMSize)) 149 m.metaInfo = (*MetaInfo)(unsafe.Pointer(&m.blockBytes[0])) 150 m.block = (*[defaultMaxSize >> 8]Block)(unsafe.Pointer(&m.blockBytes[metaSize])) 151 152 m.nInfoBytes = mmap(m.nInfoFile, int(m.nInfoMSize)) 153 m.nInfo = (*[defaultMaxSize]NInfo)(unsafe.Pointer(&m.nInfoBytes[0])) 154 } 155 156 func (c *Cedar) Close() { 157 if c.useMMap { 158 munmap(c.mmap.arrayBytes) 159 munmap(c.mmap.blockBytes) 160 munmap(c.mmap.nInfoBytes) 161 _assert(c.mmap.arrayFile.Close() == nil, "close file fail") 162 _assert(c.mmap.blockFile.Close() == nil, "close file fail") 163 _assert(c.mmap.nInfoFile.Close() == nil, "close file fail") 164 } 165 } 166 167 func _assert(condition bool, msg string, v ...interface{}) { 168 if !condition { 169 panic(fmt.Sprintf(msg, v...)) 170 } 171 } 172 173 func mmap(file *os.File, size int) []byte { 174 ab, err := syscall.Mmap(int(file.Fd()), 0, size, syscall.PROT_WRITE|syscall.PROT_READ, syscall.MAP_SHARED) 175 _assert(err == nil, "failed to mmap %v", err) 176 return ab 177 } 178 179 func grow(file *os.File, size int64) { 180 if info, _ := file.Stat(); info.Size() >= size { 181 return 182 } 183 _assert(file.Truncate(size) == nil, "failed to truncate, err %v, size %d", file.Truncate(size), size) 184 } 185 186 func munmap(data []byte) { 187 _assert(syscall.Munmap(data) == nil, "failed to munmap") 188 }