Comparison of Memory Allocation - CreateFileMapping, GlobalAlloc, HeapAlloc, CoTaskMemAlloc

Post your working scripts, libraries and tools for AHK v1.1 and older
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Comparison of Memory Allocation - CreateFileMapping, GlobalAlloc, HeapAlloc, CoTaskMemAlloc

Post by iseahound » 23 May 2022, 19:12

image.png
image.png (5.93 KiB) Viewed 470 times
So it seems that a file mapping object is the slowest, while CoTaskMemAlloc is the fastest. VirtualAlloc and malloc aren't included, I think if anyone is interested, they could add them.

Code: Select all

SetBatchLines -1
f := 10000, a := b := c := d := 0
MsgBox Ready?

size := 256

DllCall("QueryPerformanceFrequency", "int64*", frequency:=0)
loop % f {

   ; ((Test 1))
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   hMap := DllCall("CreateFileMapping", "Ptr", -1, "Ptr", 0, "uInt", 0x04, "uInt", 0, "uInt", size, "Ptr", 0, "Ptr")
   pMap := DllCall("MapViewOfFile", "Ptr", hMap, "uInt", 0x2, "uInt", 0, "uInt", 0, "uPtr", 0, "Ptr")
   NumPut(9777, pMap+0, "uint")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(pMap+0, "uint")
   DllCall("UnmapViewOfFile", "Ptr", pMap)
   DllCall("CloseHandle", "Ptr", hMap)
   a += end - start


   ; ((Test 2))
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   ptr := DllCall("GlobalAlloc", "uint", 0, "uptr", size, "ptr")
   NumPut(9777, ptr+0, "uint")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(ptr+0, "uint")
   DllCall("GlobalFree", "ptr", ptr)
   b += end - start


   ; ((Test 3))
   ProcessHeap := DllCall("GetProcessHeap")
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   ptr := DllCall("HeapAlloc", "uint", ProcessHeap, "uint", 0, "uint", size)
   NumPut(9777, ptr+0, "uint")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(ptr+0, "uint")
   DllCall("HeapFree", "ptr", ProcessHeap, "uint", HeapFree, "ptr", ptr)
   c += end - start


   ; ((Test 4))
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   ptr := DllCall("Ole32\CoTaskMemAlloc", "uint", size)
   NumPut(9777, ptr+0, "uint")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(ptr+0, "uint")
   DllCall("Ole32\CoTaskMemFree", "ptr", ptr)
   d += end - start

}


a := a / frequency
a := f / a
b := b / frequency
b := f / b
c := c / frequency
c := f / c
d := d / frequency
d := f / d


MsgBox % "File Mapping:`t"   Round(a, 2)  " fps"
  . "`n" "GlobalAlloc:`t"    Round(b, 2)  " fps"
  . "`n" "HeapAlloc:`t"      Round(c, 2)  " fps"
  . "`n" "CoTaskMemAlloc:`t" Round(d, 2)  " fps"
Last edited by iseahound on 23 May 2022, 19:23, edited 1 time in total.

iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: Comparison of Memory Allocation - CreateFileMapping, GlobalAlloc, HeapAlloc, CoTaskMemAlloc

Post by iseahound » 23 May 2022, 19:22

image.png
image.png (5.6 KiB) Viewed 466 times
Test filling a medium block of memory. Somehow the filemapping is over 12x slower than the fastest.

Code: Select all

SetBatchLines -1
f := 10000, a := b := c := d := 0
MsgBox Ready?

size := 999999

DllCall("QueryPerformanceFrequency", "int64*", frequency:=0)
loop % f {

   ; ((Test 1))
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   hMap := DllCall("CreateFileMapping", "Ptr", -1, "Ptr", 0, "uInt", 0x04, "uInt", 0, "uInt", size, "Ptr", 0, "Ptr")
   pMap := DllCall("MapViewOfFile", "Ptr", hMap, "uInt", 0x2, "uInt", 0, "uInt", 0, "uPtr", 0, "Ptr")
   DllCall("NtDll\RtlFillMemory", "Ptr", pMap, "uptr", size, "uint", 0xCAFEB0BA, "Ptr")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(pMap+0, "uint")
   DllCall("UnmapViewOfFile", "Ptr", pMap)
   DllCall("CloseHandle", "Ptr", hMap)
   a += end - start


   ; ((Test 2))
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   ptr := DllCall("GlobalAlloc", "uint", 0, "uptr", size, "ptr")
   DllCall("NtDll\RtlFillMemory", "Ptr", ptr, "uptr", size, "uint", 0xCAFEB0BA, "Ptr")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(ptr+0, "uint")
   DllCall("GlobalFree", "ptr", ptr)
   b += end - start


   ; ((Test 3))
   ProcessHeap := DllCall("GetProcessHeap")
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   ptr := DllCall("HeapAlloc", "uint", ProcessHeap, "uint", 0, "uint", size)
   DllCall("NtDll\RtlFillMemory", "Ptr", ptr, "uptr", size, "uint", 0xCAFEB0BA, "Ptr")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(ptr+0, "uint")
   DllCall("HeapFree", "ptr", ProcessHeap, "uint", HeapFree, "ptr", ptr)
   c += end - start


   ; ((Test 4))
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   ptr := DllCall("Ole32\CoTaskMemAlloc", "uint", size)
   DllCall("NtDll\RtlFillMemory", "Ptr", ptr, "uptr", size, "uint", 0xCAFEB0BA, "Ptr")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(ptr+0, "uint")
   DllCall("Ole32\CoTaskMemFree", "ptr", ptr)
   d += end - start

}


a := a / frequency
a := f / a
b := b / frequency
b := f / b
c := c / frequency
c := f / c
d := d / frequency
d := f / d


MsgBox % "File Mapping:`t"   Round(a, 2)  " fps"
  . "`n" "GlobalAlloc:`t"    Round(b, 2)  " fps"
  . "`n" "HeapAlloc:`t"      Round(c, 2)  " fps"
  . "`n" "CoTaskMemAlloc:`t" Round(d, 2)  " fps"

iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: Comparison of Memory Allocation - CreateFileMapping, GlobalAlloc, HeapAlloc, CoTaskMemAlloc

Post by iseahound » 23 May 2022, 19:28

For large blocks of memory, GlobalALloc seems to win:
image.png
image.png (5 KiB) Viewed 460 times

Code: Select all

SetBatchLines -1
f := 1000, a := b := c := d := 0
MsgBox Ready?

size := 83886080 ; 80 megabytes - typical image

DllCall("QueryPerformanceFrequency", "int64*", frequency:=0)
loop % f {

   ; ((Test 1))
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   hMap := DllCall("CreateFileMapping", "Ptr", -1, "Ptr", 0, "uInt", 0x04, "uInt", 0, "uInt", size, "Ptr", 0, "Ptr")
   pMap := DllCall("MapViewOfFile", "Ptr", hMap, "uInt", 0x2, "uInt", 0, "uInt", 0, "uPtr", 0, "Ptr")
   DllCall("NtDll\RtlFillMemory", "Ptr", pMap, "uptr", size, "uint", 0xCAFEB0BA, "Ptr")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(pMap+0, "uint")
   DllCall("UnmapViewOfFile", "Ptr", pMap)
   DllCall("CloseHandle", "Ptr", hMap)
   a += end - start


   ; ((Test 2))
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   ptr := DllCall("GlobalAlloc", "uint", 0, "uptr", size, "ptr")
   DllCall("NtDll\RtlFillMemory", "Ptr", ptr, "uptr", size, "uint", 0xCAFEB0BA, "Ptr")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(ptr+0, "uint")
   DllCall("GlobalFree", "ptr", ptr)
   b += end - start


   ; ((Test 3))
   ProcessHeap := DllCall("GetProcessHeap")
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   ptr := DllCall("HeapAlloc", "uint", ProcessHeap, "uint", 0, "uint", size)
   DllCall("NtDll\RtlFillMemory", "Ptr", ptr, "uptr", size, "uint", 0xCAFEB0BA, "Ptr")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(ptr+0, "uint")
   DllCall("HeapFree", "ptr", ProcessHeap, "uint", HeapFree, "ptr", ptr)
   c += end - start


   ; ((Test 4))
   DllCall("QueryPerformanceCounter", "int64*", start:=0)

   ptr := DllCall("Ole32\CoTaskMemAlloc", "uint", size)
   DllCall("NtDll\RtlFillMemory", "Ptr", ptr, "uptr", size, "uint", 0xCAFEB0BA, "Ptr")

   DllCall("QueryPerformanceCounter", "int64*", end:=0)
   NumGet(ptr+0, "uint")
   DllCall("Ole32\CoTaskMemFree", "ptr", ptr)
   d += end - start

}


a := a / frequency
a := f / a
b := b / frequency
b := f / b
c := c / frequency
c := f / c
d := d / frequency
d := f / d


MsgBox % "File Mapping:`t"   Round(a, 2)  " fps"
  . "`n" "GlobalAlloc:`t"    Round(b, 2)  " fps"
  . "`n" "HeapAlloc:`t"      Round(c, 2)  " fps"
  . "`n" "CoTaskMemAlloc:`t" Round(d, 2)  " fps"

Conclusion: GlobalAlloc seems to win with real world data like medium sized images. For very small allocations, where the overhead matters, use CoTaskMemAlloc.

Post Reply

Return to “Scripts and Functions (v1)”