VSPerfCmd and you!

I love writing fast code. There’s something so deeply satisfying about having something perform well that gives me a rush and I hope the same is true for you, my little smuglords-in-training. Performance will probably continue to be a major theme for a lot of my posts here, so if you at all interested in going fast, please read on. If you like your code shitty and slow, well, I guess go find another article, or eat paste or whatever it is you do, but you should probably read on. Who knows, you might come across some student beauticians who aren’t used to geniuses, you might impress them! Plus, making something fast teaches you how to take advantage of all the resources you’ve got available, and there are plenty of useful things you can learn during that process.

The free lunch is over

In case you didn’t know it by now, you don’t get exponential software speed for free anymore. You can’t just rely on better hardware to make your code run faster. Sorry, your job just got harder. Writing performant code now requires a little something extra during your development cycle, namely, profiling to find your hot-spots. Maybe it’s I/O related, or your CPU isn’t taking advantage of cache, or you have an opportunity to parallelize something, or make it asynchronous, or perhaps you’ve tried to apply parallelism and now you’ve got some kind of thread synchronization happening around a shared lock causing your application to hang. In all of these scenarios (and many more) profiling tools can be invaluable in determining the root cause of the performance loss.

If possible, this should be done early on in the life of the application to establish ongoing metrics, but even for large-scale existing systems it can help to profile your application when debugging to get a general sense of what the performance characteristics of the app are, and whether or not you’ve accidentally introduced a feature which hurts performance.

Performance testing in development.

This is by far the best possible way to do performance testing. You can isolate testing behavior more reliably, and you get the immediate feedback if you correct the bottleneck, and may find that this uncovers other bottlenecks that need fixing also. There are of course plenty of tutorials on MSDN about how to use the performance wizard, here let me google that for you, but if there is one thing my internal smug-sense won’t allow me to do, it’s use a GUI when there is a perfectly good Command line interface available.

Show me the code already! Save your pathetic prose for someone who cares!

Here I’m using powershell to help me automate the repetitive bits of instrumenting and running the profiler. Sampling is good, but if you really want to go deep, I find that just instrumenting the assembly from the start is the best approach. I would highly recommend adding this to your powershell profile for ease of use. This assumes that sn.exe is on your PATH.


function get-assemblies([string] $path, [regex] $expr) {
	# if you havin' regex problems I feel bad for you son
	# I got \d+ problems and matching text ain't one.
	dir $path | where { $expr.IsMatch($_.Name) } | select Name
}

function instrument-binaries ([string] $basePath, $assemblies) {
    $profilerPath = "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Performance Tools"

    if (-not ($env:Path.Contains($profilerPath))) {
    	write-host "appending vs_profiler path to Path environment variable."
        $env:Path += (";" + $profilerPath);
    }

    push-location $basePath

    if (-not (test-path ( where.exe VsInstr.exe ))) {
        write-host "VsInstr.exe is not installed!"
    } else {
        $flag = "is a delay-signed or test-signed assembly"

        foreach ($instr in $assemblies) {
            $path = [System.IO.Path]::Combine($basePath, $instr);

            VsInstr.exe $path /ExcludeSmallFuncs

            $output = (sn.exe -v $path)
            $resign = (($output -match $flag).Count -gt 0)
                
            if ($resign) {
                write-host $instr needs to be resigned...
                # assuming your keyfile is in the project root.
                # adjust accordingly
                sn.exe -Ra "$path" ..\..\your-keyfile-here.snk
            }
        }
    }

    pop-location
}

Invoking the script then might look like this (and could itself be another script in your profile):


$path = "c:\dev\product\bin\debug"
$assemblies = get-assemblies $path "MyCompany.*\.dll"
instrument-assmblies $path $assemblies

That’s it. Your assemblies are now ready to be poked and prodded by the profiler. Let’s see what that might look like.


function begin-profiling ( [string] $session, [string] $report_path ) {
    if (-not (test-path $report_path )) {
        mkdir "$report_path"| out-null
    }

    $env:_NT_SYMBOL_PATH = "srv*C:\mssymbols*http://msdl.microsoft.com/downloads/symbols"
    $profilerPath = "C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Performance Tools"

    if (-not ($env:Path.Contains($profilerPath))) {
        $env:Path += (";" + $profilerPath);
    }

    # make sure the profiler isn't already running.
    VsPerfCmd.exe /Status

    if ($LastExitCode -ne 0) {
        $name = $session + [DateTime]::Now.ToString("MM-dd-yyyy-hh-mm")
        $report = $report_path + $name + ".vsp"

        VsPerfCmd.exe /user:Everyone /start:Trace /output:$report /CrossSession

        write-host "Profiling report will be stored in:" $report
    } else {
        write-host "Profiler already running. New session will not be started."
    }
}

I could probably go more in-depth about these arguments, but just trust me when I say that they were arrived upon through much trial and error, and represent my best attempt at a general solution that works for both dev and production profiling. We finally invoke the real profiler VsPerfCmd.exe, setting the output path to our desired report folder. The /user:Everyone is just for convenience in production. You can use this to restrict which users have access to write to the profiler report.


begin-profiling "my_app_startup" "c:\perf\reports"

# trace it all baby!
VSPerfClrEnv /TraceOn
VSPerfClrEnv /TraceGCLife
VSPerfClrEnv /InteractionOn

start-process your.application.here.exe /some /args /maybe:?

Run your test scenario, whatever that looks like. Close your application (the profiler will block if you forget). Then shut down the profilers. This is actually really important. You won’t see any data written to your report until after the process being profiled terminates.


function end-profiling {
    VsPerfCmd.exe /GlobalOff
    VsPerfCmd /Shutdown
    VsPerfClrEnv /off

    write-host "Profilers detached."
}

You did it! /slowclap

You can now double-click your .vsp report (in whichever directory you specified in begin-profiling) and review your findings inside the comfort of Visual Studio. You’ve captured a lot of interesting data I’m sure. That /InteractionOn flag enables Tier-Interaction Profiling, which will keep track of all the ADO.Net invocations executed under the profiler. It sure beats asking a DBA to run a trace on the database eh? You’ve also got CPU metrics, and some default windows performance monitor counters you can examine as well.

Okay, that’s all for now. Next time we’ll talk about production profiling, or whatever else I feel like talking about. Maybe the continuation of that other postYou’ll just have to check back and see. Comments help. If you like this, let me know. If you want hands-on labs, I could probably accommodate that too.

Anyway, until next time.

Yours smugly,
Ross

4 thoughts on “VSPerfCmd and you!

  1. What would be nice is if you maybe displayed this example on a real application that way we can see results and see how we can use it in our own. Good stuff though.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s