Use Performance Counter Sets and PowerShell to Ease Baselining

Summary: Microsoft Scripting Guy Ed Wilson teaches how to use performance counter sets with Windows PowerShell to simplify profiling a system.


Hey, Scripting Guy! QuestionHey, Scripting Guy! I am wondering if there is an easier way to work with performance counters? For example, rather than having to pick out a whole bunch of counters, are there groups of counters that I can use? If not, that is ok, but I feel like I had to ask. By the way, the Scripting Guys rock. I just had to say it.



Hey, Scripting Guy! AnswerHello BB,

Microsoft Scripting Guy Ed Wilson here. I want to thank you for your vote of confidence. One of the things I enjoy so much about speaking in person to users groups (like I did in Columbus, Ohio) or appearing at events (like SQLSaturday in Wheeling, West Virginia) is getting to meet people who come up to me and say, “I have been reading your stuff for years, and you have saved me so many times.” And though it is fun to meet “groupies,” it is more fulfilling to meet people who have read my material and have been able to reduce their workload.

Anyway, BB, if you don’t ask, I cannot help you. Yes, you can query groups of counters! To see a list of all the counter sets, use the ListSet parameter and use a wildcard character. The ListSet parameter returns detailed information about counter sets. To limit the output to only the counter set names, select only the counterset property. The commands that will return counter set information or limit to only counter set names are shown here:

#Produce listing of all counter sets

Get-Counter -ListSet *

#to see only the counter set names

Get-Counter -ListSet * | select countersetname

Using the wildcard character trick, it is possible to search for specific counter sets that relate to a particular technology. A few sample searches and their associated output are shown here.

PS C:\Users\edwilson> Get-Counter -ListSet *disk*| select countersetname





PS C:\Users\edwilson> Get-Counter -ListSet *processor* |select countersetname


Processor Information                                                                                                         

Per Processor Network Activity Cycles                                                                                         

Per Processor Network Interface Card Activity                                                                                 



PS C:\Users\edwilson> Get-Counter -ListSet *memory* | select countersetname


.NET CLR Memory                                                                                                               

MSSQL$SQLEXPRESS:Memory Manager                                                                                               


.NET Memory Cache 4.0                                                                                                          

If I use a wildcard character pattern that matches a single countersetname value and I do not select only the countersetname with the Select-Object cmdlet (as I was doing earlier), I receive detailed information about the counter set. The properties and values associated with the LogicalDisk counter set (I use the wild card pattern L*disk) are shown in the output here:

PS C:\> Get-Counter -ListSet l*disk

CounterSetName     : LogicalDisk

MachineName        : .

CounterSetType     : MultiInstance

Description        : The Logical Disk performance object consists of counters that monitor logical partitions of hard or fixed disk drives. Performance Monitor identifies logical disks by their drive letter, such as C.

Paths              : {\LogicalDisk(*)\% Free Space, \LogicalDisk(*)\Free Megabytes, \LogicalDisk(*)

                     \Current Disk Queue Length, \LogicalDisk(*)\% Disk Time…}

PathsWithInstances : {\LogicalDisk(HarddiskVolume1)\% Free Space, \LogicalDisk(C:)\% Free Space, \LogicalDisk(_Total)\% Free Space, \LogicalDisk(HarddiskVolume1)\Free Megabytes…}

Counter            : {\LogicalDisk(*)\% Free Space, \LogicalDisk(*)\Free Megabytes, \LogicalDisk(*)

                     \Current Disk Queue Length, \LogicalDisk(*)\% Disk Time…}

Two properties are of particular interest: the paths property and the pathsWithInstances properties. The counter paths in the paths property use a wildcard character mapping, and do not map to specific instances of the resource. The command and associated output are shown here:

PS C:\Users\edwils> (Get-Counter -ListSet l*disk).paths

\LogicalDisk(*)\% Free Space

\LogicalDisk(*)\Free Megabytes

\LogicalDisk(*)\Current Disk Queue Length

\LogicalDisk(*)\% Disk Time

\LogicalDisk(*)\Avg. Disk Queue Length

\LogicalDisk(*)\% Disk Read Time

\LogicalDisk(*)\Avg. Disk Read Queue Length

\LogicalDisk(*)\% Disk Write Time

\LogicalDisk(*)\Avg. Disk Write Queue Length

\LogicalDisk(*)\Avg. Disk sec/Transfer

\LogicalDisk(*)\Avg. Disk sec/Read

\LogicalDisk(*)\Avg. Disk sec/Write

\LogicalDisk(*)\Disk Transfers/sec

\LogicalDisk(*)\Disk Reads/sec

\LogicalDisk(*)\Disk Writes/sec

\LogicalDisk(*)\Disk Bytes/sec

\LogicalDisk(*)\Disk Read Bytes/sec

\LogicalDisk(*)\Disk Write Bytes/sec

\LogicalDisk(*)\Avg. Disk Bytes/Transfer

\LogicalDisk(*)\Avg. Disk Bytes/Read

\LogicalDisk(*)\Avg. Disk Bytes/Write

\LogicalDisk(*)\% Idle Time

\LogicalDisk(*)\Split IO/Sec


If I use the pathsWithInstances property, it will triple the number of counters (on my laptop anyway). The pathsWithInstances property returns the counter path, and then a path for each instance. On my Hyper-V server with seven drives, the pathsWithInstances property will return an instance for each logical drive, as well as an instance for the _Total instance. Compare the truncated output here with the first few lines of output from the previous command:

PS C:\Users\edwils> (Get-Counter -ListSet l*disk).PathsWithInstances

\LogicalDisk(HarddiskVolume1)\% Free Space

\LogicalDisk(C:)\% Free Space

\LogicalDisk(_Total)\% Free Space

\LogicalDisk(HarddiskVolume1)\Free Megabytes

\LogicalDisk(C:)\Free Megabytes

\LogicalDisk(_Total)\Free Megabytes

To verify the number of paths, I piped the Get-Counter command to the Measure-Object cmdlet and returned only the count property. The two commands I used are shown here:

PS C:\Users\edwils> ((Get-Counter -ListSet l*disk).Paths | Measure-Object).count


PS C:\Users\edwils> ((Get-Counter -ListSet l*disk).PathsWithInstances | Measure-Object).count


After I have picked out the counter set, I can use either the paths property or the pathsWithInstances property to query. All I need to do is to supply the counter paths directly to the counter parameter of the Get-Counter cmdlet. I like to pipe the results to More so that I can page through the output (the pager does not work in the output pane of the Windows PowerShell ISE). The code to do this is shown here:

Get-Counter -Counter (Get-Counter -ListSet l*disk).Paths | more

The command and associated output are shown in the following figure.

Image of command and associated output

I can use the Get-Counter cmdlet to return processor information, memory utilization, and logical and physical disk activity. The following commands accomplish this task:

Get-Counter -Counter(Get-Counter -ListSet “Processor Information”).paths

Get-Counter -counter (Get-Counter -listSet “memory”).paths

Get-Counter -counter (Get-Counter -listSet “LogicalDisk”).paths

Get-Counter -counter (Get-Counter -listSet “PhysicalDisk”).paths

Because the counter property accepts an array (in fact, each command above supplies an array), I can create my own array of counter paths and make a single query with the Get-Counter cmdlet. In the following command, I get all of the paths from the above four commands and store them in a single variable.

$a = (Get-Counter -ListSet “Processor Information”).paths

$a += (Get-Counter -listSet “memory”).paths

$a += (Get-Counter -listSet “LogicalDisk”).paths

$a += (Get-Counter -listSet “PhysicalDisk”).paths

The $a variable contains 99 paths on my laptop. I found this by using the following command:

($a | Measure-Object).count

I can now query for performance information from all 99 counters at once:

Get-Counter -Counter $a

What about performance? On my laptop, the first command takes about four seconds. This command is shown here:

PS C:\Users\edwils> Measure-command -expression `


 Get-Counter -Counter(Get-Counter -ListSet “Processor Information”).paths

Get-Counter -counter (Get-Counter -listSet “memory”).paths

Get-Counter -counter (Get-Counter -listSet “LogicalDisk”).paths

Get-Counter -counter (Get-Counter -listSet “PhysicalDisk”).paths




Days              : 0

Hours             : 0

Minutes           : 0

Seconds           : 4

Milliseconds      : 96

Ticks             : 40963324

TotalDays         : 4.74112546296296E-05

TotalHours        : 0.00113787011111111

TotalMinutes      : 0.0682722066666667

TotalSeconds      : 4.0963324

TotalMilliseconds : 4096.3324


After I have put all of the paths in a variable, the command takes about a second, as shown here:

PS C:\Users\edwils> Measure-command -Expression { Get-Counter -Counter $a }



Days              : 0

Hours             : 0

Minutes           : 0

Seconds           : 1

Milliseconds      : 69

Ticks             : 10690628

TotalDays         : 1.2373412037037E-05

TotalHours        : 0.000296961888888889

TotalMinutes      : 0.0178177133333333

TotalSeconds      : 1.0690628

TotalMilliseconds : 1069.0628


Okay, that is cheating a bit because I did not time obtaining the paths first. But when I run the command to pick up all the paths and then perform the query, I still see a huge improvement in performance. (I would need to do some additional testing, such as rebooting my machine between each query, to get rid of any caching that might be taking place. It would bear checking, and this might be a great way to speed up querying for lots of performance counters).

PS C:\Users\edwils> Measure-command `


$a = (Get-Counter -ListSet “Processor Information”).paths

$a += (Get-Counter -listSet “memory”).paths

$a += (Get-Counter -listSet “LogicalDisk”).paths

$a += (Get-Counter -listSet “PhysicalDisk”).paths

Get-Counter -Counter $a




Days              : 0

Hours             : 0

Minutes           : 0

Seconds           : 1

Milliseconds      : 102

Ticks             : 11021647

TotalDays         : 1.27565358796296E-05

TotalHours        : 0.000306156861111111

TotalMinutes      : 0.0183694116666667

TotalSeconds      : 1.1021647

TotalMilliseconds : 1102.1647


Well, BB, this ends another Hey, Scripting Guy! Blog post. I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy