Measure Windows Performance

Windows comes with a built-in performance assessment tool. It can be used to compare system performance, and you'll also learn a lot about PowerShell techniques.

Many years ago when Windows Vista was introduced, it came with new GUI effects like transparency (called “Aero”), and at that time computer hardware wasn’t always capable of supporting these. That’s why Microsoft invented the Windows Experience Index and an assessment tool that calculates the performance of your hardware.

Initially, this assessment tool was used to ensure that computer hardware was capable of supporting advanced GUI tricks. Today, any hardware can handle 3D effects, so the experience index was removed from dialogs. Yet, the assessment engine is still there, and you can use it to calculate and test your computer hardware and compare it to other systems.

Accessing Hardware Performance Data

PowerShell can easily retrieve the hardware performance indices of your machine. The WMI class Win32_WinSAT queries the performance data:

Get-CimInstance -ClassName Win32_WinSAT

You get back an object with the scores for various hardware subsystems:

CPUScore              : 8,9
D3DScore              : 9,9
DiskScore             : 8,75
GraphicsScore         : 8
MemoryScore           : 8,9
TimeTaken             : MostRecentAssessment
WinSATAssessmentState : 1
WinSPRLevel           : 8
PSComputerName        :

The score is a cooked number between 0 and 10. To retrieve the actual raw technical performance data, you need to access XML files directly that were created by a tool named WinSAT.exe.

Accessing Raw Performance Data

All assessments are created by WinSAT.exe. This command line tool requires Administrator privileges. When you run the tool without Administrator privileges, it auto-elevates itself in a new window and closes again immediately. So make sure you run the following examples with an elevated PowerShell host.

Dumping Raw Assessments

To get a quick overview of the current assessments, use the parameter query:

winsat query

This outputs the data as text:

Windows System Assessment Tool
> Run Time 00:00:00.00
> CPU LZW Compression                          473.49 MB/s
> CPU AES256 Encryption                        7352.66 MB/s
> CPU Vista Compression                        1147.18 MB/s
> CPU SHA1 Hash                                2157.81 MB/s
> Uniproc CPU LZW Compression                  92.76 MB/s
> Uniproc CPU AES256 Encryption                1099.06 MB/s
> Uniproc CPU Vista Compression                229.82 MB/s
> Uniproc CPU SHA1 Hash                        660.36 MB/s
> Memory Performance                           39903.06 MB/s
> Dshow Video Encode Time                      0.00000 s
> Dshow Video Decode Time                      0.00000 s
> Media Foundation Decode Time                 0.00000 s
> Disk  Sequential 64.0 Read                   2377.30 MB/s          9.1
> Disk  Random 16.0 Read                       619.60 MB/s          8.4

Obviously, while this is ok for a quick read, it won’t help much when you plan to read the data in an automated way.

Directly Accessing XML Data

WinSAT.exe stores the assessment data in various XML files in this folder:

explorer 'C:\Windows\Performance\WinSAT\DataStore'

The files come with a timestamp and a description of the hardware tests. Windows performs an automatic initial assessment when you install it. These initial assessments are marked with (Initial). If you recalculated the index later, additional files may be present.

Name                                                               Length
----                                                               ------
2019-09-14 09.39.31.893.winsat.etl                                  98304
2019-09-14 09.39.33.309 Cpu.Assessment (Initial).WinSAT.xml         58668
2019-09-14 09.39.33.309 Disk.Assessment (Initial).WinSAT.xml        51196
2019-09-14 09.39.33.309 DWM.Assessment (Initial).WinSAT.xml         10910
2019-09-14 09.39.33.309 Graphics3D.Assessment (Initial).WinSAT.xml  40334
2019-09-14 09.39.33.309 Mem.Assessment (Initial).WinSAT.xml          9700
2019-09-14 09.41.06.573 Formal.Assessment (Initial).WinSAT.xml     146108

The file(s) starting with Formal contain a summary of all assessments. To get to the latest assessments for your system, simply pick the latest xml file that starts with Formal:

# get the freshest file that starts with "Formal" from the WinSAT datastore:
$path = Get-ChildItem -Path 'C:\Windows\Performance\WinSAT\DataStore\*Formal.*.xml' | Sort-Object -Property CreationTime -Descending | Select-Object -First 1 -ExpandProperty FullName

Examining XML Content

The file contains XML data and looks similar to this:

Start-Process -FilePath iexplore -ArgumentList $Path

I am using the ancient Internet Explorer to visualize the XML content. It’s the easiest way of getting a quick overview of the nested nodes because Internet Explorer automatically prettifies the raw XML and adds appropriate indentation and line breaks.

Here is a nice trick that you can use to prettify XML in the PowerShell console:

# get the freshest file that starts with "Formal" from the WinSAT datastore:
$path = Get-ChildItem -Path 'C:\Windows\Performance\WinSAT\DataStore\*Formal.*.xml' | Sort-Object -Property CreationTime -Descending | Select-Object -First 1 -ExpandProperty FullName

# read the file content
$content = Get-Content -Path $Path -Raw

# the default output looks aweful:
$content

# prettify it:
([xml]$content).Save([Console]::Out)

Reading XML Content

The easiest and fastest way to process XML content is to load it directly into an XML object via its method Load(). Next, you can output the nodes you are after in object-oriented fashion:

# get the freshest file that starts with "Formal" from the WinSAT datastore:
$path = Get-ChildItem -Path 'C:\Windows\Performance\WinSAT\DataStore\*Formal.*.xml' | Sort-Object -Property CreationTime -Descending | Select-Object -First 1 -ExpandProperty FullName

# load xml content
$xml = [xml]::new()
$xml.Load($Path)

# access node with data:
$xml.WinSAT.Metrics.CPUMetrics.CompressionMetric

The result is outputted as objects:

units #text
----- -----
MB/s  473.49160

To access the compression rate, try this:

# access node with data:
$node = $xml.WinSAT.Metrics.CPUMetrics.CompressionMetric
'CPU Compression Performance is {0} {1}' -f $node.'#text', $node.units

Investigating Hardware

The xml data produced by WinSAT.exe does not only contain performance data. It also provides important details about your computer hardware. The hardware details can be found in WinSAT.SystemConfig:

# get the freshest file that starts with "Formal" from the WinSAT datastore:
$path = Get-ChildItem -Path 'C:\Windows\Performance\WinSAT\DataStore\*Formal.*.xml' | Sort-Object -Property CreationTime -Descending | Select-Object -First 1 -ExpandProperty FullName

# load xml content
$xml = [xml]::new()
$xml.Load($Path)

# access node with data:
$xml.WinSAT.SystemConfig

You’ll see a list of hardware components that you can query:

OperationVersion  : OperationVersion
OSVersion         : OSVersion
HistoryVersion    : 0
Platform          : Platform
System            : System
Processor         : Processor
Memory            : Memory
Monitors          : Monitors
Graphics          : Graphics
Disk              : Disk
CompletionStatus  : CompletionStatus
AssessmentRunTime : AssessmentRunTime

For example, if you’d like to get exact information about the processor your machine is using, try this:

$xml.WinSAT.SystemConfig.Processor.Instance.Signature 

The result is a detailed description:

Manufacturer     : Manufacturer
Stepping         : 5
Model            : 14
Family           : 6
ExtendedModel    : 7
ExtendedFamily   : 0
CompactSignature : 460517

Unfortunately, the XML files are a very nested structure. If you wanted to know the Manufacturer of your processor, you’d have to requery this property:

$xml.WinSAT.SystemConfig.Processor.Instance.Signature.Manufacturer

Invoking Hardware Assessment

You can use WinSAT.exe anytime you want to assess your hardware. When you run WinSAT.exe manually, you can specify a path to your own xml file, too.

Running all assessments can take a minute or two. Keep in mind that running WinSAT.exe requires Administrator privileges. Reading the produced xml files does not.

Running Fresh Formal Assessment

To run a fresh formal test (including all hardware subsystems) and save the result to a custom xml file path, run this (with Administrator privileges):

$Path = "$env:temp\myreport.xml"
winsat formal -xml $Path
$xml = [xml]::new()
$xml.Load($Path)

Querying WinSAT

WinSAT.exe can create a range of assessments that are not necessarily performance-oriented. You can also query hardware features and discover devices such as optical disks. For this, WinSAT.exe supports a range of report types.

New-WinSatAssessment is a quick PowerShell function that helps you create assessments easily. The function takes care of most of the routine steps:

  • It suggests the different types of available assessments and takes a path to a custom xml file
  • It runs the requested assessment and shows a progress bar
  • It returns a ready-to-use XML object that you can use to drill into the produced data

Running the assessment can take some time. You can prettify the tool by piping the output to a progress bar:

function New-WinSatAssessment
{
  param
  (
    [string]
    [ValidateSet('formal','dwm','cpu','mem','d3d','disk','media','mfmedia','features')]
    $CommandLine = 'formal',
    
    [string]
    $Path = "$env:temp\winsat_temp.xml"
  )
  
  # test admin status
  If (!([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
  { Write-Warning "Run this with Administrator privileges!"; return}
  
  # use Regex to parse the real-time status messages emitted by winsat.exe
  $patternTime = '\d{2}:\d{2}:\d{2}\.\d{2}'
  $patternString = "^> (.*?)\s*('.*'){0,1}$"
  
  # run the tool...
  winsat $CommandLine -xml $Path | ForEach-Object {
    # ignore feedback about the time tests took:
    $isTime = $_ -match $patternTime
    if (!$isTime)
    {
      # output the parsed text in a progress bar:
      $IsParsed = $_ -match $patternString
      $status = @{}

      if ($IsParsed)
      {
        if ($matches[2])
        {
          $status['Status'] = $matches[2]
        }
            
        Write-Progress -Activity $matches[1] @status
      }
    }
  }
  
  # load the xml file into an xml object...
  $xml = [xml]::new()
  $xml.Load($Path)
  # and return it:
  $xml
}

Here is how you can use New-WinSatAssessment to discover computer hardware (make sure you run it from an elevated console):

# do a hardware scan:
$result = New-WinSatAssessment -CommandLine features

# emit information about disks:
$result.WinSAT.SystemConfig.Disk.Disk

The result looks similar to this:

id                : \\.\PhysicalDrive1
DiskNum           : 1
Vendor            : Vendor
Model             : Model
Size              : Size
WriteCacheEnabled : FALSE

id                : \\.\PhysicalDrive2
DiskNum           : 2
Vendor            : Vendor
Model             : Model
Size              : Size
WriteCacheEnabled : FALSE

id                : \\.\PhysicalDrive3
DiskNum           : 3
Vendor            : Vendor
Model             : Model
Size              : Size
WriteCacheEnabled : FALSE

Due to the nature of the produced XML files, any detailed information is stored in a nested property. For a full report, you’d have to query these properties separately. Or, you can use calculated properties:

$result.WinSAT.SystemConfig.Disk.Disk | Select-Object -Property Id, DiskNum, WriteCacheEnabled, @{N='Vendor';E={$_.Vendor.'#cdata-section'}}, @{N='Model';E={$_.Model.'#cdata-section'}},@{N='SizeGB';E={[Math]::Round([int64]($_.Size.'#text')/1GB,1)}}

Now the result shows all information in one view:

id                 DiskNum WriteCacheEnabled Vendor   Model            SizeGB
--                 ------- ----------------- ------   -----            ------
\\.\PhysicalDrive1 1       FALSE             Samsung  Flash Drive       119,5
\\.\PhysicalDrive2 2       FALSE             USB      Flash Disk           60
\\.\PhysicalDrive3 3       FALSE             Samsung  Flash Drive         239