
Using metrics in your AF SDK code
Posted by Rick Davin
Using RPC Metrics in your AF SDK has never been easier!
Occasionally you may have the need to view various metrics regarding your AF SDK code. Recently I was given a bit of code to review performance metrics, which I have modified and will present below via a text file to download. Note the code references PIServer.GetClientRpcMetrics, which is new to AF SDK 2.9. If you are interested in the code but are working with an earlier version of AF, you will need to strip out any references to PIServer to get it to compile.
Modify App.Config
You will need to modify the <configuration> section of your App.Config file. If the App.Config file does not exist, you will need to create it. You will add the following lines:
<system.net> <settings> <performanceCounters enabled = "true" /> </ settings > </ system.net >
This again would be in the <configuration> section, following the <startup> node. If you have to create the App.Config file from scratch, the whole thing would look like:
<?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" /> </startup> <system.net> <settings> <performanceCounters enabled="true" /> </settings> </system.net> </configuration>
For the metrics code further below, you may put it in a library project. However, the library DLL as well as any application referencing the library should all have their App.Config modified as shown above.
MetricsTicker and MetricsSnapshot
I have 2 classes defined within one file named "Metrics.cs". The MetricsTicker will track the starting and ending snapshot taken by MetricsSnapshot, and then neatly display the differences between the start and end. MetricsSnapshot will take a snapshot of these metrics:
- AF Server RPC Metrics (if any)
- AF Client RPC Metrics (if any)
- PI Client RPC Metrics (if any)
- Network bytes sent (a performance counter)
- Network bytes received (a performance counter)
- Garbage collected memory
- Allocated working set memory (a performance counter)
- Allocated peak working set memory (a performance counter)
- Timestamp (DateTime.UtcNow)
Simple Usage Example
Let's say I have some method named YourCustomMethod where I make lots of AF calls that I wish to review the metrics related to that method. I could reference the classes by passing the Asset Server (PISystem) of interest:
var metrics = new MetricsTicker(assetServer); metrics.Start(); YourCustomMethod(); metrics.Stop();
Well, that seems easy enough so far! If you want a high precision timer, you may optionally wrap a Stopwatch around your custom method call. However, the last thing a starting snapshot does is grab DateTime.UtcNow, and the first thing an ending snapshot does is also grab DateTime.UtcNow so there is already a built-in way to measure the timespan between snapshots. Plus if you want to focus just on the AF RPC calls, you can review the subtotals of the duration.
Sample Output
Okay, starting and stopping our metrics was easy. What about reporting? How difficult is that? It too is easy. To output the the difference in metrics, there is one simple command:
Console.WriteLine(metrics.DisplayDelta());
Which would produce something like:
AF Server RPC Metrics Count Duration(ms) PerCall(ms)
------------------------- -------- ------------ ------------
GetSDCollection 1 0.5 0.484
GetElement 11 81.4 7.400
GetTableList 1 3.1 3.092
GetTable 1 1.6 1.649
GetElementTemplate 2 9.3 4.634
GetCategory 2 10.2 5.120
GetUOMDatabase 1 2.0 2.020
SearchTotalCount 1 246.4 246.412
SearchRefresh 1 0.0 0.021
SearchObjectIds 3 221.9 73.953
GetEventFrames 24 5386.9 224.456
GetCategoryList 3 9.3 3.102
GetAnalyses 24 545.2 22.717
GetAnalysisTemplates 1 9.5 9.532
GetElementTemplates 1 3.7 3.720
GetAnalysisTemplateList 1 1.9 1.894
GetModels 47 17257.7 367.185
------------------------- -------- ------------ ------------
Subtotal 125 23790.8 23790.770
AF Client RPC Metrics Count Duration(ms) PerCall(ms)
------------------------- -------- ------------ ------------
GetTableList 1 58.7 58.652
GetTable 1 30.8 30.814
GetElementTemplate 2 51.2 25.599
GetCategory 2 39.6 19.786
GetUOMDatabase 1 68.5 68.490
SearchTotalCount 1 269.2 269.233
SearchObjectIds 3 328.5 109.488
GetEventFrames 24 13932.8 580.535
GetCategoryList 3 23.9 7.983
GetAnalyses 24 1706.0 71.083
GetAnalysisTemplates 1 72.0 72.034
GetElementTemplates 1 15.0 15.026
GetAnalysisTemplateList 1 15.8 15.765
GetModels 47 31414.9 668.402
SearchRefresh 1 4.7 4.748
------------------------- -------- ------------ ------------
Subtotal 113 48031.7 48031.692
Total GC Memory: 385.37 MB
Working Memory : 524.71 MB
Peak Wrk Memory: 539.85 MB
Network Sent : 8.84 MB
Network Receivd: 181.44 MB
Elapsed Time : 02:44.6
How easy is that to generate a report of your metrics?!!
There are different combinations to DisplayDelta() method since it's full signature is:
public string DisplayDelta(bool round = true, bool showServerRpcMetrics = true, bool showClientRpcMetrics = true)
If you notice the bottom of the above output neatly shows bytes in MB rounded to 2 decimal places, and the elapsed time span is to 1/10th of a second. This is due to the round parameter defaulting to true. You can get the full bytes and time span if you try DisplayDelta(round: false). Here's an example of that:
Total GC Memory: 464,306,792 bytes
Working Memory : 600,330,240 bytes
Peak Wrk Memory: 601,427,968 bytes
Network Sent : 9,271,104 bytes
Network Receivd: 190,250,782 bytes
Elapsed Time : 00:02:47.1073888
Memory is a Guesstimation
The Network Bytes Sent and Received are fairly accurate, but the values for memory usage should be considered a ballpark value rather than a highly accurate value. There's so much that goes on inside of .NET with garbage collection, the memory heap, locked pages, etc., that makes it tough to be precise. Suffice to say that if you to have an app that routinely uses 400 MB of Total GC Memory, and then you make changes to your app to see the memory drop to 100 MB routinely, then you should have peace-of-mind that you've reduced your memory needs by 75%. Note that I peppered that last sentence with "routinely". That's because, thanks to the mysteries of garbage collection, you may run the same app 10 times in a row and 9 of those times the memory may hover around 400 MB and 1 of those times it may unexpectedly drop to 200 MB. This should be considered a one-off due to GC doing something independent of your app but obviously affecting your app.
While the memory usage is not highly accurate, I personally find it to be an acceptable gauge for comparisons.
Download Files
Here's the "Metrics29.cs" file to add to your projects. You may need to change the namespace accordingly. Note that "Metrics29.cs" requires AF SDK 2.9 or better. For AF versions 2.6 through AF 2.8, you may use the "Metrics28.cs" version.
-
Metrics29.zip 3.6 KB
-
Metrics28.cs.zip 3.4 KB
Comments