View statistics of memory usage

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

View statistics of memory usage

Post by Michael Vogel »

Is there a possibility to enhance the debugging tools in the IDE to show the memory usage for specific arrays, lists, images etc.? Even a summarization of theses values could be helpful to get a better feeling about the of the memory consumption.
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: View statistics of memory usage

Post by Michael Vogel »

I'll to track a program to see, where it grabs all the memory which is seen in the task manager - around 300 MB in my case. I'd like to display a panel which shows the amount of memory for certain resources (images etc.) and arrays, but how to check that easily?

The following code is a first attempt for showing the memory usage of arrays. It does not work properly, and I was (also) wondering, that the protected array 'C' will be seen on the same memory place...

Code: Select all

DataSection
	start:
	Data.i $555555
EndDataSection

Structure BType
	l.l
	s.s
	i.i
EndStructure

Global Dim A.i(10000)
Global Dim B.BType(10000)


Procedure ShowMem()
	
	Protected Dim C.l(10000)
	
	Protected s=?Start
	Protected a=@A
	Protected b=@B
	Protected c=@C
	Protected a0,an
	Protected b0,bn
	Protected c0,cn
	
	a0=@A(0)
	an=@A(ArraySize(A()))
	b0=@B(0)
	bn=@B(ArraySize(B()))
	c0=@C(0)
	cn=@C(ArraySize(C()))
	
	Debug "S: "+Hex(s)
	Debug "A: "+ Hex(a)+ " ("+ Str(a-s)+ ")"
	Debug "B: "+ Hex(b)+ " ("+ Str(b-a)+ ")"
	Debug "C: "+ Hex(c)+ " ("+ Str(c-b)+ ")"
	Debug "A* "+ Hex(a0)+ " - "+ Hex(an)+ " > "+ Str(an-a0)
	Debug "B* "+ Hex(b0)+ " - "+ Hex(bn)+ " > "+ Str(bn-b0)
	Debug "C* "+ Hex(c0)+ " - "+ Hex(cn)+ " > "+ Str(cn-c0)
	Debug ""
	
	FreeArray(C())
	
EndProcedure

ShowMem()
ReDim A(20000)
For i=0 To 9999
	B(i)\s=RSet("",100,"*")
Next i
Global Dim D(10000)
ShowMem()
User avatar
Demivec
Addict
Addict
Posts: 4260
Joined: Mon Jul 25, 2005 3:51 pm
Location: Utah, USA

Re: View statistics of memory usage

Post by Demivec »

Here are some changes to your ShowMem() code:

Code: Select all

Procedure ShowMem()
  
  Protected Dim c.l(10000)
  
  Protected s=?Start
  Protected a=@a
  Protected b=@b
  Protected c=@c
  Protected a0,an
  Protected b0,bn
  Protected c0,cn
  Protected i, a_el, b_el, c_el, b_as, b_s
  
  a0=@a(0)
  an=@a(ArraySize(a()))
  b0=@b(0)
  bn=@b(ArraySize(b()))
  c0=@c(0)
  cn=@c(ArraySize(c()))
  
  a_el = ArraySize(a()) + 1
  b_el = ArraySize(b()) + 1
  c_el = ArraySize(c()) + 1
  
  b_as = ArraySize(b())
  For i = 0 To b_as
    b_s + Len(b(i)\s)
  Next
  b_s = (b_s + b_el) * SizeOf(Character) ;add memory for string nulls on each element, assumes there are no Null strings.
  
  
  Debug "S:  "+Hex(s)                      
  Debug "A:  "+ Hex(a)+ " ("+ Str(SizeOf(a))+ ")" 
  Debug "B:  "+ Hex(b)+ " ("+ Str(SizeOf(b))+ ")"
  Debug "C:  "+ Hex(c)+ " ("+ Str(SizeOf(b))+ ")"
  Debug "A*  "+ Hex(a0)+ " - "+ Hex(an)+ " > "+ Str(a_el * SizeOf(Integer))
  Debug "B*  "+ Hex(b0)+ " - "+ Hex(bn)+ " > "+ Str(b_el * SizeOf(BType))
  Debug "C*  "+ Hex(c0)+ " - "+ Hex(cn)+ " > "+ Str(c_el * SizeOf(Long))
  Debug "B*s "+ Str(b_el * SizeOf(BType)) + " + " + Str(b_s) + " > " + Str((b_el * SizeOf(BType)) + b_s)
  
  FreeArray(c())
  
EndProcedure
The placement of memory for the DataSection is not in any particular relationship to the variable data and so is not of any real value in determining memory used by variables. I tally the size of string memory used by each element in array b(). This tally includes an assumed Null and accommodates ASCII/Unicode differences.

I hope these changes have helped you a bit further.
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: View statistics of memory usage

Post by Michael Vogel »

Thanks,
I will do my best to find an easy way for showing the memory usage for all my different arrays and lists in my program, of course most of them use complex structures with strings and more...

For checking the images memory hunger, I am using these lines now:

Code: Select all

z=0
For i=0 To #LastImage
	If IsImage(i)
		n=ImageDepth(i,#PB_Image_InternalDepth)>>3*ImageWidth(i)*ImageHeight(i)
		z+n
		Debug Str(i)+" "+Str(n)+"bytes, total "+Str(z>>10)+"K"
	EndIf
Next i
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: View statistics of memory usage

Post by Michael Vogel »

Here's another approach to check the actual code, needs to insert one (or more) Memory call(s) in each procedure and will show the memory changes while the program is running...

Code: Select all

; Define Memory

	#MS_WindowName="Memory Watcher"
	#MS_Window=4999
	#MS_Gadget=4999
	#MS_X=360
	#MS_Y=600
	#MS_LineNumber=0
	#MS_Yellow=1000000
	#MS_Orange=5000000

	Global MS_PID.l
	Global MS_Name.s
	Global MS_Buffer.i
	Global MS_NextMemory.i
	Global MS_Memory.i
	Global MS_Proc.s
	Global MS_Line.i
	Global MS_Count.i=-1
	Global MS_Start.i=ElapsedMilliseconds()

	Enumeration
		#MS_Time
		#MS_Code
		#MS_Name
		#MS_Memory
		#MS_Delta
	EndEnumeration

	; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

	#TH32CS_SNAPPROCESS=$2

	Structure PROCESSENTRY32s
		dwsize.l
		cntusage.l
		th32ProcessID.l
		th32DefaultHeapID.l
		th32ModuleID.l
		cntThreads.l
		th32ParentProcessID.l
		pcPriClassBase.l
		dwFlags.l
		szExeFile.s{1024}
	EndStructure

	Prototype.l GetProcessMemoryInfo_(hProcess.l, *p, cb.l)

	Structure PROCESS_MEMORY_COUNTERS
		cb.l
		PageFaultCount.l
		PeakWorkingSetSize.l
		WorkingSetSize.l
		QuotaPeakPagedPoolUsage.l
		QuotaPagedPoolUsage.l
		QuotaPeakNonPagedPoolUsage.l
		QuotaNonPagedPoolUsage.l
		PagefileUsage.l
		PeakPagefileUsage.l
	EndStructure

; EndDefine
Procedure.s MemoryStr(i.i)

	Protected s.s=Str(i)
	Protected minus.i
	
	If i<0
		minus=1
	EndIf
	
	i=Len(s)
	

	While i>3+minus
		i-3
		s=InsertString(s,".",i+1)
	Wend

	ProcedureReturn s

EndProcedure
Procedure Memory_Events(time)

	time+ElapsedMilliseconds()
	While WaitWindowEvent(1)
		If ElapsedMilliseconds()>time
			Break
		EndIf
	Wend

EndProcedure
Procedure Memory_Text(pos,s.s,column)
	
	If GetGadgetItemText(#MS_Gadget,pos,column)<>s
		SetGadgetItemText(#MS_Gadget,pos,s,column)
	EndIf
	
EndProcedure
Procedure MemoryInit()

	Protected MS_Handle.l
	Protected MS_Return.l
	Protected lvm.LV_COLUMN

	OpenWindow(#MS_Window,0,0,#MS_X,#MS_Y,#MS_WindowName)
	ListIconGadget(#MS_Gadget,0,0,#MS_X,#MS_Y,"Time",60,#PB_ListIcon_GridLines|#PB_ListIcon_FullRowSelect)
	AddGadgetColumn(#MS_Gadget,#MS_Code,"Code",60)
	AddGadgetColumn(#MS_Gadget,#MS_Name,"Name",80)
	AddGadgetColumn(#MS_Gadget,#MS_Memory,"Memory",80)
	AddGadgetColumn(#MS_Gadget,#MS_Delta,"Delta",80)

	lvm\mask=#LVCF_FMT
	lvm\fmt=#LVCFMT_RIGHT
	SendMessage_(GadgetID(#MS_Gadget),#LVM_SETCOLUMN,#MS_Code,@lvm)
	SendMessage_(GadgetID(#MS_Gadget),#LVM_SETCOLUMN,#MS_Memory,@lvm)
	SendMessage_(GadgetID(#MS_Gadget),#LVM_SETCOLUMN,#MS_Delta,@lvm)

	StickyWindow(#MS_Window,#True)

	MS_Handle=FindWindow_ (0,#MS_WindowName)
	MS_Return=GetWindowThreadProcessId_(MS_Handle,@MS_PID)
	
	Memory_Events(10)
	ProcedureReturn MS_Return

EndProcedure
Procedure.l MemoryUsage(PID.l)

	Protected pmc.PROCESS_MEMORY_COUNTERS
	Protected hProcess.l=OpenProcess_(#PROCESS_QUERY_INFORMATION|#PROCESS_VM_READ,#False,PID)
	Protected *F
	Protected Memory

	If OpenLibrary(0,"PSAPI.DLL")
		*F=GetFunction(0,"GetProcessMemoryInfo")
		If *F
			CallFunctionFast(*F,hProcess,@pmc,SizeOf(pmc))
			Memory=pmc\WorkingSetSize
		Else
			CloseLibrary(0)
			End
		EndIf
	Else
		End
	EndIf

	ProcedureReturn Memory

EndProcedure
Procedure MemoryShow(Name.s="",wait=0)

	Protected Memory.i
	Protected Time.i=ElapsedMilliseconds()-MS_Start
	Protected Temp.s
	
	Memory=MemoryUsage(MS_PID)
	
	If MS_Name=MS_Proc+Name
		If MS_Buffer=0
			MS_Buffer=MS_Memory
		EndIf
	Else
		MS_Count+1
		MS_Name=MS_Proc+Name
		MS_Buffer=0
		MS_Memory=MS_NextMemory
		AddGadgetItem(#MS_Gadget,MS_Count,"")
	EndIf
	
	If #MS_LineNumber
		Temp=Str(MS_Line)
	Else
		Temp=MS_Proc
	EndIf

	Memory_Text(MS_Count,FormatDate("%ii:%ss.",Time/1000)+RSet(Str(Time%1000),3,"0"),#MS_Time)
	Memory_Text(MS_Count,Temp,#MS_Code)
	Memory_Text(MS_Count,Name,#MS_Name)
	Memory_Text(MS_Count,MemoryStr(Memory),#MS_Memory)
	
	MS_NextMemory=Memory
	
	If MS_Buffer
		Time=Memory-MS_Buffer
	Else
		Time=Memory-MS_Memory
	EndIf
	Memory_Text(MS_Count,MemoryStr(Time),#MS_Delta)
	If Time>#MS_Orange
		SetGadgetItemColor(#MS_Gadget,MS_Count,#PB_Gadget_BackColor,$A0D0FF,#MS_Delta)
	ElseIf Time>#MS_Yellow
		SetGadgetItemColor(#MS_Gadget,MS_Count,#PB_Gadget_BackColor,$A0FFFF,#MS_Delta)
	EndIf

EndProcedure
Macro Memory(x="",w=0)

	MS_Line=#PB_Compiler_Line
	MS_Proc=#PB_Compiler_Procedure

	MemoryShow(x,w)

EndMacro

; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; ~~   D  E  M  O   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Procedure TestProcedure()

	Memory("A")
	
	Structure TestType
		buffer.i [2500]
	EndStructure
	
	Protected NewList TestList.TestType()

	For i=0 To 999
		Memory("Add Element")
		AddElement(TestList())
		If i%100=0
			Memory_Events(5)
		EndIf
	Next i

	Memory("Exit")

EndProcedure

MemoryInit()
Memory("Start",10)

TestProcedure()
TestProcedure()

Memory("All Done")

Repeat
	Memory("‹Press Key›")
Until WaitWindowEvent()=#WM_CHAR
Puffolino
User
User
Posts: 49
Joined: Thu Jan 05, 2012 12:27 am

Re: View statistics of memory usage

Post by Puffolino »

Michael, your code seems to show wrong memory numbers, "virtual memory" is not included.
User avatar
Michael Vogel
Addict
Addict
Posts: 2797
Joined: Thu Feb 09, 2006 11:27 pm
Contact:

Re: View statistics of memory usage

Post by Michael Vogel »

Puffolino wrote:Michael, your code seems to show wrong memory numbers, "virtual memory" is not included.
Not sure, what virtual memory means (and how to show the information), sorry.
Post Reply