Virtual lock memory pages and Set Process Working set size to hint to windows harder not to write pages to disk as much, add finalize function, minor renaming

This commit is contained in:
j.laycock 2019-09-10 16:18:51 +01:00
parent d757ed2fe3
commit 9c9dff7386

View file

@ -5,6 +5,7 @@ package memory_map
import ( import (
"os" "os"
"reflect" "reflect"
"runtime"
"syscall" "syscall"
"unsafe" "unsafe"
@ -33,32 +34,41 @@ type DWORD = uint32
type WORD = uint16 type WORD = uint16
var ( var (
procGetSystemInfo = syscall.NewLazyDLL("kernel32.dll").NewProc("GetSystemInfo") modkernel32 = syscall.NewLazyDLL("kernel32.dll")
procGetSystemInfo = modkernel32.NewProc("GetSystemInfo")
procGetProcessWorkingSetSize = modkernel32.NewProc("GetProcessWorkingSetSize")
procSetProcessWorkingSetSize = modkernel32.NewProc("SetProcessWorkingSetSize")
) )
var system_info, err = getSystemInfo() var currentProcess, _ = windows.GetCurrentProcess()
var currentMinWorkingSet uint64 = 0
var currentMaxWorkingSet uint64 = 0
var _ = getProcessWorkingSetSize(uintptr(currentProcess), &currentMinWorkingSet, &currentMaxWorkingSet)
var chunk_size = uint64(system_info.dwAllocationGranularity) * 256 var systemInfo, _ = getSystemInfo()
var chunkSize = uint64(systemInfo.dwAllocationGranularity) * 256
func (mMap *MemoryMap) CreateMemoryMap(file *os.File, maxlength uint64) { func (mMap *MemoryMap) CreateMemoryMap(file *os.File, maxLength uint64) {
chunks := (maxlength / chunk_size) chunks := (maxLength / chunkSize)
if chunks*chunk_size < maxlength { if chunks*chunkSize < maxLength {
chunks = chunks + 1 chunks = chunks + 1
} }
alignedMaxLength := chunks * chunk_size alignedMaxLength := chunks * chunkSize
maxlength_high := uint32(alignedMaxLength >> 32) maxLength_high := uint32(alignedMaxLength >> 32)
maxlength_low := uint32(alignedMaxLength & 0xFFFFFFFF) maxLength_low := uint32(alignedMaxLength & 0xFFFFFFFF)
file_memory_map_handle, err := windows.CreateFileMapping(windows.Handle(file.Fd()), nil, windows.PAGE_READWRITE, maxlength_high, maxlength_low, nil) file_memory_map_handle, err := windows.CreateFileMapping(windows.Handle(file.Fd()), nil, windows.PAGE_READWRITE, maxLength_high, maxLength_low, nil)
if err == nil { if err == nil {
mMap.File = file mMap.File = file
mMap.file_memory_map_handle = uintptr(file_memory_map_handle) mMap.file_memory_map_handle = uintptr(file_memory_map_handle)
mMap.write_map_views = make([]MemoryBuffer, 0, alignedMaxLength/chunk_size) mMap.write_map_views = make([]MemoryBuffer, 0, alignedMaxLength/chunkSize)
mMap.max_length = alignedMaxLength mMap.max_length = alignedMaxLength
mMap.End_of_file = -1 mMap.End_of_file = -1
runtime.SetFinalizer(mMap, mMap.DeleteFileAndMemoryMap)
} }
} }
@ -84,7 +94,7 @@ func min(x, y uint64) uint64 {
func (mMap *MemoryMap) WriteMemory(offset uint64, length uint64, data []byte) { func (mMap *MemoryMap) WriteMemory(offset uint64, length uint64, data []byte) {
for { for {
if ((offset+length)/chunk_size)+1 > uint64(len(mMap.write_map_views)) { if ((offset+length)/chunkSize)+1 > uint64(len(mMap.write_map_views)) {
allocateChunk(mMap) allocateChunk(mMap)
} else { } else {
break break
@ -92,19 +102,19 @@ func (mMap *MemoryMap) WriteMemory(offset uint64, length uint64, data []byte) {
} }
remaining_length := length remaining_length := length
slice_index := offset / chunk_size sliceIndex := offset / chunkSize
slice_offset := offset - (slice_index * chunk_size) sliceOffset := offset - (sliceIndex * chunkSize)
data_offset := uint64(0) dataOffset := uint64(0)
for { for {
write_end := min((remaining_length + slice_offset), chunk_size) writeEnd := min((remaining_length + sliceOffset), chunkSize)
copy(mMap.write_map_views[slice_index].Buffer[slice_offset:write_end], data[data_offset:]) copy(mMap.write_map_views[sliceIndex].Buffer[sliceOffset:writeEnd], data[dataOffset:])
remaining_length -= (write_end - slice_offset) remaining_length -= (writeEnd - sliceOffset)
data_offset += (write_end - slice_offset) dataOffset += (writeEnd - sliceOffset)
if remaining_length > 0 { if remaining_length > 0 {
slice_index += 1 sliceIndex += 1
slice_offset = 0 sliceOffset = 0
} else { } else {
break break
} }
@ -120,8 +130,15 @@ func (mMap *MemoryMap) ReadMemory(offset uint64, length uint64) (MemoryBuffer, e
} }
func (mBuffer *MemoryBuffer) ReleaseMemory() { func (mBuffer *MemoryBuffer) ReleaseMemory() {
currentMinWorkingSet = currentMinWorkingSet - mBuffer.aligned_length
currentMaxWorkingSet = currentMaxWorkingSet - mBuffer.aligned_length
windows.VirtualUnlock(mBuffer.aligned_ptr, uintptr(mBuffer.aligned_length))
windows.UnmapViewOfFile(mBuffer.aligned_ptr) windows.UnmapViewOfFile(mBuffer.aligned_ptr)
var _ = setProcessWorkingSetSize(uintptr(currentProcess), uintptr(currentMinWorkingSet), uintptr(currentMaxWorkingSet))
mBuffer.ptr = 0 mBuffer.ptr = 0
mBuffer.aligned_ptr = 0 mBuffer.aligned_ptr = 0
mBuffer.length = 0 mBuffer.length = 0
@ -131,11 +148,12 @@ func (mBuffer *MemoryBuffer) ReleaseMemory() {
func allocateChunk(mMap *MemoryMap) { func allocateChunk(mMap *MemoryMap) {
start := uint64(len(mMap.write_map_views)) * chunk_size start := uint64(len(mMap.write_map_views)) * chunkSize
mBuffer, err := allocate(windows.Handle(mMap.file_memory_map_handle), start, chunk_size, true) mBuffer, err := allocate(windows.Handle(mMap.file_memory_map_handle), start, chunkSize, true)
if err == nil { if err == nil {
mMap.write_map_views = append(mMap.write_map_views, mBuffer) mMap.write_map_views = append(mMap.write_map_views, mBuffer)
windows.VirtualLock(mBuffer.aligned_ptr, uintptr(mBuffer.aligned_length))
} }
} }
@ -143,7 +161,7 @@ func allocate(hMapFile windows.Handle, offset uint64, length uint64, write bool)
mBuffer := MemoryBuffer{} mBuffer := MemoryBuffer{}
dwSysGran := system_info.dwAllocationGranularity dwSysGran := systemInfo.dwAllocationGranularity
start := (offset / uint64(dwSysGran)) * uint64(dwSysGran) start := (offset / uint64(dwSysGran)) * uint64(dwSysGran)
diff := offset - start diff := offset - start
@ -158,6 +176,11 @@ func allocate(hMapFile windows.Handle, offset uint64, length uint64, write bool)
access = windows.FILE_MAP_WRITE access = windows.FILE_MAP_WRITE
} }
currentMinWorkingSet = currentMinWorkingSet + aligned_length
currentMaxWorkingSet = currentMaxWorkingSet + aligned_length
var _ = setProcessWorkingSetSize(uintptr(currentProcess), uintptr(currentMinWorkingSet), uintptr(currentMaxWorkingSet))
addr_ptr, errno := windows.MapViewOfFile(hMapFile, addr_ptr, errno := windows.MapViewOfFile(hMapFile,
uint32(access), // read/write permission uint32(access), // read/write permission
offset_high, offset_high,
@ -227,3 +250,35 @@ func getSystemInfo() (_SYSTEM_INFO, error) {
} }
return si, nil return si, nil
} }
// BOOL GetProcessWorkingSetSize(
// HANDLE hProcess,
// PSIZE_T lpMinimumWorkingSetSize,
// PSIZE_T lpMaximumWorkingSetSize
// );
func getProcessWorkingSetSize(process uintptr, dwMinWorkingSet *uint64, dwMaxWorkingSet *uint64) error {
r1, _, err := syscall.Syscall(procGetProcessWorkingSetSize.Addr(), 3, process, uintptr(unsafe.Pointer(dwMinWorkingSet)), uintptr(unsafe.Pointer(dwMaxWorkingSet)))
if r1 == 0 {
if err != syscall.Errno(0) {
return err
}
}
return nil
}
// BOOL SetProcessWorkingSetSize(
// HANDLE hProcess,
// SIZE_T dwMinimumWorkingSetSize,
// SIZE_T dwMaximumWorkingSetSize
// );
func setProcessWorkingSetSize(process uintptr, dwMinWorkingSet uintptr, dwMaxWorkingSet uintptr) error {
r1, _, err := syscall.Syscall(procSetProcessWorkingSetSize.Addr(), 3, process, (dwMinWorkingSet), (dwMaxWorkingSet))
if r1 == 0 {
if err != syscall.Errno(0) {
return err
}
}
return nil
}