For what it's worth, I threw together a basic implementation of what I wrote 
above:
    
    
    # Free Public License 1.0.0
    #
    # Permission to use, copy, modify, and/or distribute this software for any
    # purpose with or without fee is hereby granted.
    #
    # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    
    import posix, os
    
    type
      RawArray {.unchecked.} [T] = array[0..0, T]
      MemBlock*[T] = ref object
        data: ptr RawArray[T]
        count: int
    
    const
      triggerGCfactor = 3
    
    var
      prevAllocated {.threadvar.}: int
      newAllocated {.threadvar.}: int
    
    # Some missing POSIX parts:
    
    {.emit: """
    #ifndef MAP_ANONYMOUS
    #define MAP_ANONYMOUS MAP_ANON
    #endif
    """.}
    
    var
      MAP_ANONYMOUS {.importc, header: "<sys/mman.h>".}: cint
    
    proc getpagesize(): cint {.importc, header: "<unistd.h>".}
    
    let pagemask = getpagesize().int - 1
    
    proc blocksize(n: int, elemsize: int): int =
      let size = n * elemsize
      if (size and pagemask) == 0:
        size
      else:
        (size + pagemask) and not pagemask
    
    proc finalizeMemBlock[T](p: MemBlock[T]) =
      discard munmap(p.data, blocksize(p.count, sizeof(T)))
    
    proc newMemBlock*[T](count: int): MemBlock[T] =
      let occupied = getOccupiedMem() + prevAllocated
      let factor = occupied / newAllocated
      if factor <= triggerGCfactor:
        GC_fullCollect()
        prevAllocated += newAllocated
        newAllocated = 0
      let mem = mmap(nil, blocksize(count, sizeof(T)),
        PROT_READ or PROT_WRITE,
        MAP_PRIVATE or MAP_ANONYMOUS,
        -1, 0)
      if mem == cast[pointer](MAP_FAILED):
        raiseOSError(osLastError())
      new(result, finalizeMemBlock[T])
      result.data = cast[ptr RawArray[T]](mem)
      result.count = count
    
    proc `[]`*[T](m: MemBlock[T], index: int): T {.inline.} =
      assert index >= 0 and index < m.count
      m.data[index]
    
    proc `[]=`*[T](m: MemBlock[T], index: int, value: T) {.inline.} =
      assert index >= 0 and index < m.count
      m.data[index] = value
    
    when isMainModule:
      proc main =
        for j in 1..1024:
          echo j
          let a = newMemBlock[int](1_000_000)
          for i in 0..1_000_000-1:
            a[i] = 0
      main()
    

This is still missing functionality (items/mitems iterators, resizing, etc.) 
and has only been tested on macOS, but could be a starting point if needed.

Note also that the data cannot contain references to GCed memory (or has to 
manage them explicitly with `GC_ref` and `GC_unref`).

Reply via email to