Our colleague Frank Anderson wrote a blog post where he discussed the next evolution in virtualizing graphics. In that blog post, he briefly talked about the testing that has been going on at Solutions Lab around GPU intensive workloads. As we researched different applications, we noticed that varying the applications gave us varying workload intensity. We’ll go over our initial approach with various tools and applications and how we utilized PowerShell to automate them.

The Supporting Components

Similar to other testing done at Solutions Lab, we used Login VSI as our session launching orchestrator. We also used the custom command line with a CSV file connection option in which we specified our SFLauncher script that we previously presented in the automating the launch of HDX session through StoreFront blog post. Since Login VSI does not currently offer a GPU intensive workload, we set out to create and arrange the supporting components that would allow us to effectively test and collect data. The centerpiece of these components is a workload script that was published as a seamless application. Like in other tests, we keep most of the executables and logs in the VSI share and begin the workload script by defining this location (and other common items):

# UNC path to VSI share
$VSIShare = "\\TESTORCH01\vsi_share"
# Timestamp for log filename
$ScriptStart = [DateTime]::Now.ToString("yyy-MM-dd_HH-mm-ss")

Data Capture

Data capture is key and there are no good claims without supporting facts. With this in mind, we looked into different possibilities of collecting data. In addition to the typical data from things like Perfmon in Windows and Hypervisor specific metrics, we needed more graphics data and for this we considered several components:


GPU-Z offered us data about the GPU that was of interest to our testing. Luckily for us, this data was available as CSV file output by doing the following for each user that launched a session:

# Location of GPU-Z
$GPUZExe = "$VSIShare\Resources\GPU-Z\GPU-Z.0.7.3.exe"
# Location and name of GPU-Z data log
$GPUZLogFile = "$VSIShare\Logs\$($env:USERNAME)_$($ScriptStart)_GPU-Z_Log.txt"
#Configure and Start GPU-Z capture
New-Item HKCU:\Software\techPowerUp\GPU-Z -Force
Set-ItemProperty HKCU:\Software\techPowerUp\GPU-Z -Name "Install_Dir" -Value "no"
Set-ItemProperty HKCU:\Software\techPowerUp\GPU-Z -Name "LogFile" -Value $GPUZLogFile
Start-Process $GPUZExe -ArgumentList "-minimized"

Citrix WMI

One component of interest for us was the Frames Per Second (FPS) being sent from the server for the HDX session. For this we tapped into the Windows Management Instrumentation data available within the user’s HDX session. Additionally, because we didn’t want to interrupt the execution of the script to capture these WMI metrics, we used a PowerShell background job to create our CSV file with the data as follows:

# Location and name of WMI data log
$WMILogFile = "$VSIShare\Logs\$($env:USERNAME)_$($ScriptStart)_WMI_Log.txt"
# Start FPS capture through WMI from Citrix Thinwire
Start-Job -ScriptBlock {
    Param ($WMILogFile)
    if(-not (Test-Path $WMILogFile)) {
        New-Item $WMILogFile -ItemType "file" -Force | Out-Null
    $message = "Date,FPS,"
    $message | Out-File -FilePath $WMILogFile -Append

    $sessionId = ((quser | ? { $_ -match $env:USERNAME }) -split ' +')[2]

    while($true) {
        $currentFps = (gwmi -n root\citrix\hdx -cl Citrix_VirtualChannel_Thinwire_Enum -Filter "SessionID=$sessionId").Component_Fps
        $message = [DateTime]::Now.ToString("yyy-MM-dd HH:mm:ss") + ",$currentFps,"
        $message | Out-File -FilePath $WMILogFile -Append

        Start-Sleep 1
} -ArgumentList $WMILogFile

Workload Applications

For the test, we encapsulated each workload application within the workload loop that checks for the existence of Login VSI’s logoff.txt file, which is created when the time is up by Login VSI to halt execution for the current test. If the file exists, the user logs off. Otherwise, the user runs through the workload again. We included this in the PowerShell workload script as follows:

# Location of the VSI test's logoff file
$LogOffFile = "$((Get-ChildItem "$VSIShare\_vsi_logfiles" | ? {$_.PSIsContainer -eq $true} | sort -Property LastWriteTime | select -Last 1).FullName)\logoff.txt"
# Workload loop
do {
    #Launching of workload apps, waiting, and closing
} while (-not (Test-Path $LogOffFile))

As mentioned earlier, different applications gave us different levels of workload intensity. There were cases where we didn’t want to close the workload application after each iteration of the workload loop, but instead just wanted to keep the workload application running and monitor the existence of the logoff.txt file. For this, we would move the application launch to before the workloads loop, keep a shorter sleep in the workloads loop, and move the call to CloseMainWindow after the workloads loop (but before logoff). Now we’ll dig into the details of each workload application.

Microsoft HTML5 Fish Bowl with Google Chrome

A great web based app is Microsoft’s own HTML5 Fish Bowl. This application takes advantage of the GPU acceleration provided by browsers like Google Chrome.

In order to get this application into our workload and environment, we started by installing the alternate version of Google Chrome that can be shared across multiple users in a single computer and disabled Google Update though a GPO to ensure an expected and controlled version. Also, we downloaded the Fish Bowl app into a local lab server and made a few modifications to inject some JavaScript calls from a Custom.js file which we defined. In this file we varied the number of fish at specified intervals by using setTimeout calls to the Fish Bowl’s SetFishCount function.

Including the calls to Chrome also required us to control some of the aspects of the application using some of the command line switches (of the MANY supported by Chromium). The pieces that were included in the PowerShell workload script (with 60 seconds of workload execution as an example) were as follows:

Outside the workload loop:

# URL of Fishbowl web app
$FishbowlSite = "http://TESTORCH01/fishbowl"

Inside the workload loop:

# Launch Fishbowl workload, wait, and close
$fishbowl = Start-Process "C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" -ArgumentList "--start-maximized","--no-default-browser-check","--no-first-run",$FishbowlSite -PassThru
Start-Sleep -Seconds 60
$fishbowl.CloseMainWindow() | Out-Null

SolidWorks eDrawings Viewer

Dassault Systemes’ SolidWorks eDrawings Viewer offered a balanced (but light) GPU consumption when rotating its CAD rendered models. This would however, require the user to either use their mouse to click and drag the model around or click the play button within the application to start a continuous loop that rotates the model on each axis. We also gathered a few free and common .easm file demos like the Sea-Doo SeaScooter Dolphin model.

Our approach around this application was to use their API to create a simple Windows Forms application that embedded the drawing and programmatically kicked off the continuous rotation loop. We ended up calling this small application the Solutions Lab eDrawings Animator (or SLEDA). This however still required that we have installed the SolidWorks eDrawings Viewer 2013 on our VDA endpoints in order to provide access to the API. The pieces that were included in the PowerShell workload script (with 60 seconds of workload execution as an example) were as follows:

Outside the workload loop:

# Location of SLEDA app
$SLEDAExe = "$VSIShare\Resources\SLEDA\SLEDA.exe"
# Location of eDrawings file
$Drawing = "`"$VSIShare\Resources\eDrawings\_SEASCOOTER DOLPHIN.easm`""

Inside the workload loop:

# Launch eDrawings workload, wait, and close
$sleda = Start-Process $SLEDAExe -ArgumentList $Drawing -PassThru
Start-Sleep -Seconds 60
$sleda.CloseMainWindow() | Out-Null

Redway3d Redsdk CAD Turbine Demo

Redway3d’s Redsdk CAD Turbine demo offered a fairly constant mid-intensity GPU load that occurred as the model turbine spins around as part of the demo. This application includes a menu upon launching for which you must select some options before the demo launches.

The workload in this case needed to include a way to make selections in the menu and start the demo. For this, we used the Windows Script Host’s AppActivate and SendWait methods. Keep in mind that the Turbine Demo needs to be install on the VDA endpoint. The pieces that were included in the PowerShell workload script (with 60 seconds of workload execution as an example) were as follows:

Inside the workload loop:

# Launch Turbine workload, wait, and close
$turbine = Start-Process "C:\Program Files (x86)\Redway3d - Turbine Demo\Win32\REDTurbineDemo.exe" `
                         -WorkingDirectory "C:\Program Files (x86)\Redway3d - Turbine Demo\Win32" `
Start-Sleep -Seconds 5
$wshell = New-Object -ComObject wscript.shell
$wshell.AppActivate($turbine.Id) | Out-Null
Start-Sleep -Seconds 1
Start-Sleep -Seconds 1
Start-Sleep -Seconds 1
$wshell.SendKeys(' ')
Start-Sleep -Seconds 1
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($wshell) | Out-Null
Start-Sleep -Seconds 60
$turbine.CloseMainWindow() | Out-Null

Unigine Tropics

Unigine Tropics is another interesting application in terms of automation. This application offered command line execution that allowed for things like different resolutions, full screen/windowed mode, and choice of graphics library. Also, keep in mind that the Tropics application must be installed on the VDA endpoint. The pieces that were included in the PowerShell workload script (with 60 seconds of workload execution as an example) were as follows:

Inside the workload loop:

# Launch Tropics workload, wait, and close
$tropics = Start-Process "C:\Program Files (x86)\Unigine\Tropics\tropics.exe" `
                         -ArgumentList "-video_app direct3d11 -sound_app openal -video_fullscreen 0 -video_mode -1 -video_width 1024 -video_height 768 -data_path ./ -engine_config data/unigine.cfg -system_script tropics/unigine.cpp -extern_define RELEASE" `
                         -WorkingDirectory "C:\Program Files (x86)\Unigine\Tropics" `
Start-Sleep -Seconds 60

Hopefully you found all this information helpful when performing load testing using GPU intensive applications. We sure had a blast looking into these real-world applications since they each offered a different challenge for automation.

Happy Scripting!

Santiago Cardenas

Citrix Solutions Lab


This software / sample code is provided to you “AS IS” with no representations, warranties or conditions of any kind. You may use, modify and distribute it at your own risk. CITRIX DISCLAIMS ALL WARRANTIES WHATSOEVER, EXPRESS, IMPLIED, WRITTEN, ORAL OR STATUTORY, INCLUDING WITHOUT LIMITATION WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NONINFRINGEMENT. Without limiting the generality of the foregoing, you acknowledge and agree that (a) the software / sample code may exhibit errors, design flaws or other problems, possibly resulting in loss of data or damage to property; (b) it may not be possible to make the software / sample code fully functional; and (c) Citrix may, without notice or liability to you, cease to make available the current version and/or any future versions of the software / sample code. In no event should the software / code be used to support of ultra-hazardous activities, including but not limited to life support or blasting activities. NEITHER CITRIX NOR ITS AFFILIATES OR AGENTS WILL BE LIABLE, UNDER BREACH OF CONTRACT OR ANY OTHER THEORY OF LIABILITY, FOR ANY DAMAGES WHATSOEVER ARISING FROM USE OF THE SOFTWARE / SAMPLE CODE, INCLUDING WITHOUT LIMITATION DIRECT, SPECIAL, INCIDENTAL, PUNITIVE, CONSEQUENTIAL OR OTHER DAMAGES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. You agree to indemnify and defend Citrix against any and all claims arising from your use, modification or distribution of the code.