Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate

a method to determine processor utilization


  • Please log in to reply
10 replies to this topic
shimanov
  • Guests
  • Last active:
  • Joined: --
SetBatchLines, -1

SetFormat, float, 0.0

CoordMode, ToolTip, screen

period = 1000

loop
{
	idleTime = idleTime1
	GetIdleTime( idleTime )
	
	start := a_TickCount
	
	Sleep, %period%
	
	elapsed := a_TickCount-start
	
	idleTime = idleTime2
	GetIdleTime( idleTime )
	
	total_idle_ticks := ( idleTime2?ticks_high-idleTime1?ticks_high ) << 32
		total_idle_ticks := total_idle_ticks+( idleTime2?ticks_low-idleTime1?ticks_low )
		total_idle_ticks := total_idle_ticks*0.0001
	ppu_idle := total_idle_ticks/elapsed*100

	total := elapsed-total_idle_ticks
	ppu := 100-ppu_idle

	ToolTip, period = %period% (ideal)`nelapsed = %elapsed% (actual)`n`nppu = %total% ms (%ppu%`%)`nidle = %total_idle_ticks% ms (%ppu_idle%`%), 10, 10
}

ReadInteger( p_address, p_offset, p_size, p_hex=true )
{
    old_FormatInteger := a_FormatInteger

	if ( p_hex )
	    SetFormat, integer, hex
	else
	    SetFormat, integer, dec

	value = 0

	loop, %p_size%
	    value := value+( *( ( p_address+p_offset )+( a_Index-1 ) ) << ( 8* ( a_Index-1 ) ) )

	SetFormat, integer, %old_FormatInteger%

	return, value
}

GetIdleTime( p_IdleTime )
{
	global
	local	success

	/*
	struct FILETIME {
		DWORD	dwLowDateTime;		uint4	0
		DWORD	dwHighDateTime;		uint4	4
	}
	*/
	
	VarSetCapacity( %p_IdleTime%?ticks, 4+4 )
	
	success := DllCall( "kernel32.dll\GetSystemTimes", "uint", &%p_IdleTime%?ticks, "uint", 0, "uint", 0 )
	if ( ! success )
	{
		MsgBox, GetSystemTimes failed!
		
		ExitApp
	}
		
	%p_IdleTime%?ticks_low := ReadInteger( &%p_IdleTime%?ticks, 0, 4 )
	%p_IdleTime%?ticks_high := ReadInteger( &%p_IdleTime%?ticks, 4, 4 )
}

This method tracks the system idle time (i.e., the OS idle process). The assumption is that processor utilization can be determined by examining the difference between system idle time and elapsed time during any given period.

I have tested this against the "CPU Usage" and "System Idle Process - CPU Usage" statistics as presented by Process Explorer (PE) from SysInternals. In my testing I noticed that this method produces a processor utilization which fairly accurately tracks that given by PE. In fact, this method produces statistics which seem to anticipate that given by PE.

The code above is a proof-of-concept. A preferred implementation would likely use a timer to gather statistics and save them in a temporal list which could later be accessed as needed.

Anyway... no guarantees.

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
Hi shimanov,

Thank you so much for your script. That was one of the things I always wanted to have. I tested your script at it worked accourate enough. I have modified it. But in between I ran into trouble. It doesn't work any more. Could I ask you to have a look at it, please?

I added some msgbox into the code to see where it stops, but is was working, only when i click the msgboxes very fast, the code just stoped working. I do not know what I messed up, that causes such a behavior.

Thanks.
toralf

EDIT: Found it. It was just missing a #persistent. Now it works.

#Persistent
period = 1000 
SetBatchLines, -1 
SetFormat, float, 0.0 
CoordMode, ToolTip, screen 
SetTimer, CheckCPULoad, %period%
return

CheckCPULoad:
   start := a_TickCount 
   elapsed := start - Laststart
   
   GetIdleTime( Low, High ) 
    
   If Laststart
     {
       Idle_ticks := ( High - Lasthigh ) << 32 
       Idle_ticks := ( Idle_ticks + ( Low - Lastlow ) ) * 0.0001 
       ppu_idle := Idle_ticks / elapsed * 100 
     
       total := elapsed - Idle_ticks 
       ppu := 100 - ppu_idle 

       ToolTip,
         (LTrim
           period = %period% (ideal)
           elapsed = %elapsed% (actual)
         
           ppu = %total% ms (%ppu%`%)
           idle = %Idle_ticks% ms (%ppu_idle%`%)
         ), 10, 10 
     }
   Laststart := start
   Lastlow := Low
   Lasthigh := High
return

GetIdleTime( ByRef Low, ByRef High ) 
  { 
    VarSetCapacity( Ticks, 4+4 ) 
    success := DllCall( "kernel32.dll\GetSystemTimes", "uint", &Ticks, "uint", 0, "uint", 0 ) 
    if ( ! success ) 
      { 
        MsgBox, GetSystemTimes failed! 
        ExitApp 
      } 
        
    Low := ReadInteger( &Ticks, 0, 4 ) 
    High := ReadInteger( &Ticks, 4, 4 )
  } 

ReadInteger( Address, Offset, Size ) 
  { 
    old_FormatInteger := A_FormatInteger 
    SetFormat, Integer, hex 
    value = 0 
    Loop, %Size% 
        value := value + ( *( ( Address + Offset ) + ( A_Index - 1 ) ) << ( 8 * ( A_Index - 1 ) ) ) 
    SetFormat, Integer, %old_FormatInteger% 
    Return, value 
  }

Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

Chris
  • Administrators
  • 10727 posts
  • Last active:
  • Joined: 02 Mar 2004
Really nice. I tried it on my system and it seems exactly accurate. For example, it indicates 50% CPU utilitization when some other script runs an infinite loop with SetBatchLines 10ms, which is exactly what it is designed to use and what Task Manager says it uses.

Many people have asked for this, so thanks for spending the time to create and post it.

shimanov
  • Guests
  • Last active:
  • Joined: --
I am glad you have found value in my demo.

to: toralf

It's always good to know others can read your code. You have quickly transformed it to something of practical use. Thanks.

to: Chris

It is the least I can do. I have found AHk useful and purposeful. I appreciate your thoughtful design of the language, and the extensibility inherent to its design. Of course, the best part, is the AHk community. It draws you in, and then you have to participate. An enjoyable experience.

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005

It's always good to know others can read your code.

I admit I had some problems. You used a "?" inside your var names, which caused some irritations in my head.
And I still do not understand some of the math you did (bitshift and *). But I didn't mess with that. The rest was easy, just some re-structuring. I currently have it running all day in the upper left corner (0,0) just as a one-liner ToolTip. Very neat. Thanks for your participation. I can't wait for more shimanov-code. :)
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

shimanov
  • Guests
  • Last active:
  • Joined: --
I forgot to mention, the "GetSystemTimes" routine requires, at minimum, Windows XP SP1.

You used a "?" inside your var names


I use "?" as a record operator to denote a field, as opposed to "." since that is unavailable.

And I still do not understand some of the math you did (bitshift and *).


First, all times are stored in ms. Second, most of the math is irrelevant -- my mistake. I forgot AHk (and ReadInteger) supports 64-bit signed integers.

Below is the revised version with some comments to explain why?:

SetBatchLines, -1

SetFormat, float, 0.0       ; set same display format as that in Process Explorer

CoordMode, ToolTip, screen

period = 1000               ; sample period

loop
{
    ; first sample
    idleTime = idleTime1
    GetIdleTime( idleTime )
    
    start := a_TickCount
    
    Sleep, %period%
    
    ; second sample
    elapsed := a_TickCount-start
    
    idleTime = idleTime2
    GetIdleTime( idleTime )
    
    total_idle_ticks := ( idleTime2?ticks-idleTime1?ticks )*0.0001  ; calculate difference and convert to ms
    ppu_idle := total_idle_ticks/elapsed*100                        ; ppu = Percentage Processor Usage
    
    total := elapsed-total_idle_ticks
    ppu := 100-ppu_idle
    
    ToolTip, period = %period% (ideal)`nelapsed = %elapsed% (actual)`n`nppu = %total% ms (%ppu%`%)`nidle = %total_idle_ticks% ms (%ppu_idle%`%), 10, 10
}

ReadInteger( p_address, p_offset, p_size, p_hex=true )
{
    old_FormatInteger := a_FormatInteger
    
    if ( p_hex )
       SetFormat, integer, hex
    else
       SetFormat, integer, dec
    
    value = 0
    
    loop, %p_size%
       value := value+( *( ( p_address+p_offset )+( a_Index-1 ) ) << ( 8* ( a_Index-1 ) ) )
    
    SetFormat, integer, %old_FormatInteger%
    
    return, value
}

GetIdleTime( p_IdleTime )
{
    global
    local   success
    
    /*
    struct FILETIME {
      DWORD   dwLowDateTime;      uint4   0
      DWORD   dwHighDateTime;      uint4   4
    }
    */
    
    VarSetCapacity( %p_IdleTime%?ticks, 4+4 )

    ; GetSystemTimes returns pointers to 64-bit signed integers (aka, ticks).  ticks are are the
    ;   number of 100-nanosecond intervals since January 1, 1601 -- a Microsoft thing. 
    success := DllCall( "kernel32.dll\GetSystemTimes", "uint", &%p_IdleTime%?ticks, "uint", 0, "uint", 0 )
    if ( ! success )
    {
      MsgBox, GetSystemTimes failed!
      
      ExitApp
    }

    ; convert binary 64-bit signed integer to AHk format
    %p_IdleTime%?ticks := ReadInteger( &%p_IdleTime%?ticks, 0, 8 )
}

When evaluating discrepancies between the processor usage (PU) as given by this method and others, consider the following:

1. The sample period is set at 1 s.
2. The clock resolution for timing may only be 10 ms.
3. The system PU is tracking the PU of the System Idle Process. This may or may not be the statistic used by the Windows performance library to determine the system PU.
4. There may occur an offset, due to indirect tracking of the System Idle Process. Code within the Idle Process, will produce the most accurate results (i.e., discrepancy between retrieving system idle time and elapsed time).

Decarlo110
  • Members
  • 303 posts
  • Last active: Feb 12 2006 02:15 AM
  • Joined: 15 Dec 2004
This is a very nice script, which can be used, among other things, for keeping script/prog activity within a certain threshold. As far as i know, this cpu monitoring code is unique in the forum. Thanks for sharing it.
1) The Open Source Definition http://www.opensourc...ition_plain.php

2) Intuitive. Logical. Versatile. Adaptable. <>

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

This is a very nice script, which can be used, among other things, for keeping script/prog activity within a certain threshold. As far as i know, this cpu monitoring code is unique in the forum. Thanks for sharing it.


Thanks. It was one of my early attempts to "get to know" Windows.

To monitor processor usage for a particular process, you may want to consider "GetProcessTimes".

Laszlo
  • Moderators
  • 4713 posts
  • Last active: Mar 31 2012 03:17 AM
  • Joined: 14 Feb 2005
Shimanov, is there a simple modification which calculates also the available free memory?

shimanov
  • Members
  • 610 posts
  • Last active: Jul 18 2006 08:35 PM
  • Joined: 25 Sep 2005

Shimanov, is there a simple modification which calculates also the available free memory?


Very simple indeed. Check this post.

Bobo8
  • Members
  • 5 posts
  • Last active: Nov 28 2009 11:42 PM
  • Joined: 09 Sep 2008
This helpful script didn't count with multi core CPU. I have included 1 line, where I'v found out number of (logical, I hope that it is correct) processors and then only I'v divided total_idle_ticks by this:
EnvGet, ProcessorCount, NUMBER_OF_PROCESSORS 
SetBatchLines, -1 
SetFormat, float, 0.0       ; set same display format as that in Process Explorer 
CoordMode, ToolTip, screen 
period = 1000               ; sample period 
loop 
{ 
    ; first sample 
    idleTime = idleTime1 
    GetIdleTime( idleTime ) 
    start := a_TickCount 
    Sleep, %period% 
    ; second sample 
    elapsed := a_TickCount-start 
    idleTime = idleTime2 
    GetIdleTime( idleTime ) 
    total_idle_ticks := ( idleTime2?ticks-idleTime1?ticks )*0.0001/ProcessorCount  ; calculate difference and convert to ms 
    ppu_idle := total_idle_ticks/elapsed*100                        ; ppu = Percentage Processor Usage 
    total := elapsed-total_idle_ticks 
    ppu := 100-ppu_idle 
    ToolTip, period = %period% (ideal)`nelapsed = %elapsed% (actual)`n`nppu = %total% ms (%ppu%`%)`nidle = %total_idle_ticks% ms (%ppu_idle%`%) , 10, 10 
} 

ReadInteger( p_address, p_offset, p_size, p_hex=true ) 
{ 
    old_FormatInteger := a_FormatInteger 
    if ( p_hex ) 
       SetFormat, integer, hex 
    else 
       SetFormat, integer, dec 
    value = 0 
    loop, %p_size% 
       value := value+( *( ( p_address+p_offset )+( a_Index-1 ) ) << ( 8* ( a_Index-1 ) ) ) 
    SetFormat, integer, %old_FormatInteger% 
    return, value 
} 

GetIdleTime( p_IdleTime ) 
{ 
    global 
    local   success 
    /* 
    struct FILETIME { 
      DWORD   dwLowDateTime;      uint4   0 
      DWORD   dwHighDateTime;      uint4   4 
    } 
    */ 
    VarSetCapacity( %p_IdleTime%?ticks, 4+4 ) 
    ; GetSystemTimes returns pointers to 64-bit signed integers (aka, ticks).  ticks are are the 
    ;   number of 100-nanosecond intervals since January 1, 1601 -- a Microsoft thing. 
    success := DllCall( "kernel32.dll\GetSystemTimes", "uint", &%p_IdleTime%?ticks, "uint", 0, "uint", 0 ) 
    if ( ! success ) 
    { 
      MsgBox, GetSystemTimes failed! 
      ExitApp 
    } 
    ; convert binary 64-bit signed integer to AHk format 
    %p_IdleTime%?ticks := ReadInteger( &%p_IdleTime%?ticks, 0, 8 ) 
}