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  }