{"id":251545,"date":"2025-01-07T05:00:08","date_gmt":"2025-01-07T13:00:08","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/visualstudio\/?p=251545"},"modified":"2025-01-09T07:09:36","modified_gmt":"2025-01-09T15:09:36","slug":"benchmarking-with-visual-studio-profiler","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/visualstudio\/benchmarking-with-visual-studio-profiler\/","title":{"rendered":"Benchmarking with Visual Studio Profiler"},"content":{"rendered":"<p>In Visual Studio 17.13 Preview we have released our updated BenchmarkDotNet diagnosers, allowing you to use more of the tools in the performance profiler to analyze benchmarks. With this change it is super quick to dig into CPU usage and allocations of benchmarks making the measure, change, measure cycle quick and efficient.<\/p>\n<h2>Benchmarking a real project<\/h2>\n<p>So, to show how we can use the tools to make things better let&#8217;s go ahead and benchmark a real project. At the time of writing this article, <a href=\"https:\/\/www.nuget.org\/packages\/CsvHelper\/\">CsvHelper<\/a> is the 67<sup>th<\/sup> most popular package on Nuget.org with over 9 million downloads of the current version. If we can benchmark this and make it better, we can help lots of users.<\/p>\n<p>You can follow along by pulling down my fork of this at: <a href=\"https:\/\/github.com\/karpinsn\/CsvHelper\">CsvHelper Fork<\/a>. The notable changes are that I have added are a new console project (CsvHelper.Benchmarks) that we can use to store our benchmarks, adding the <a href=\"https:\/\/benchmarkdotnet.org\/\">BenchmarkDotNet<\/a> package to do the actual benchmark runs, and a simple <code>EnumerateRecords<\/code> benchmark that parses a CSV stream into records which is shown below.<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">public class BenchmarkEnumerateRecords\r\n{\r\n\tprivate const int entryCount = 10000;\r\n\tprivate readonly MemoryStream stream = new();\r\n\r\n\tpublic class Simple\r\n\t{\r\n\t\tpublic int Id { get; set; }\r\n\t\tpublic string Name { get; set; }\r\n\t}\r\n\r\n\t[GlobalSetup]\r\n\tpublic void GlobalSetup()\r\n\t{\r\n\t\tusing var streamWriter = new StreamWriter(this.stream, null, -1, true);\r\n\t\tusing var writer = new CsvWriter(streamWriter, CultureInfo.InvariantCulture, true);\r\n\t\tvar random = new Random(42); \/\/ Pick a known seed to keep things consistent\r\n\r\n\t\tvar chars = new char[10];\r\n\t\tstring getRandomString()\r\n\t\t{\r\n\t\t\tfor (int i = 0; i &lt; 10; ++i)\r\n\t\t\t\tchars[i] = (char)random.Next('a', 'z' + 1);\r\n\t\t\treturn new string(chars);\r\n\t\t}\r\n\r\n\t\twriter.WriteHeader(typeof(Simple));\r\n\t\twriter.NextRecord();\r\n\t\tfor (int i = 0; i &lt; BenchmarkEnumerateRecords.entryCount; ++i)\r\n\t\t{\r\n\t\t\twriter.WriteRecord(new Simple()\r\n\t\t\t{\r\n\t\t\t\tId = random.Next(),\r\n\t\t\t\tName = getRandomString()\r\n\t\t\t});\r\n\t\t\twriter.NextRecord();\r\n\t\t}\r\n\t}\r\n\r\n\t[GlobalCleanup]\r\n\tpublic void GlobalCleanup()\r\n\t{\r\n\t\tthis.stream.Dispose();\r\n\t}\r\n\r\n\t[Benchmark]\r\n\tpublic void EnumerateRecords()\r\n\t{\r\n\t\tthis.stream.Position = 0;\r\n\t\tusing var streamReader = new StreamReader(this.stream, null, true, -1, true);\r\n\t\tusing var csv = new CsvReader(streamReader, CultureInfo.InvariantCulture, true);\r\n\t\tforeach (var record in csv.GetRecords&lt;Simple&gt;())\r\n\t\t{\r\n\t\t\t_ = record;\r\n\t\t}\r\n\t}\r\n}<\/code><\/pre>\n<p>A few things to note here. We have a global setup function that creates a simple CSV stream and holds it in a memory stream for us. We do this inside the <code>[GlobalSetup]<\/code> of the benchmark run so that it doesn\u2019t skew the results of our benchmark, we only want to benchmark the actual parsing of the CSV file, not creating test data.<\/p>\n<p>Next, we have a global cleanup function that properly disposes our memory stream, which is just good practice in the event more benchmarks get added so we don\u2019t continuously leak memory.<\/p>\n<p>Lastly, our benchmark simply creates a <code>CsvReader<\/code> from a stream and then reads each record from it. This exercises the parsing functionality of CsvHelper which is what we are going to try and optimize.<\/p>\n<h2>Getting insight into the benchmark<\/h2>\n<p>From here you can add a BenchmarkDotNet (BDN) Diagnoser to the benchmark class to capture information about the benchmark while it is running. BenchmarkDotNet comes with an included <code>[MemoryDiagnosers]<\/code> which captures allocation and overall memory usage information. If we add this attribute and run the benchmark you should get something like<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkBefore-1.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251575\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkBefore-1.png\" alt=\"Benchmark run results before local changes\" width=\"1235\" height=\"346\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkBefore-1.png 1235w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkBefore-1-300x84.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkBefore-1-1024x287.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkBefore-1-768x215.png 768w\" sizes=\"(max-width: 1235px) 100vw, 1235px\" \/><\/a><\/p>\n<p>From here you can see the normal mean, error, and standard deviation that BDN provides along with output from our diagnoser which shows we have allocated 1.69 MB of memory during the benchmark and its breakdown in the different GC heaps. If we want to dig further, which I do \ud83d\ude0a, then we can include the <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers\">Microsoft.VisualStudio.DiagnosticsHub.BenchmarkDotNetDiagnosers<\/a> package from Nuget.org, which hooks BenchmarkDotNet into the Visual Studio Profiler so we can see what is happening during the run. After including this package and adding the <code>[DotNetObjectAllocDiagnoser]<\/code> and <code>[DotNetObjectAllocJobConfiguration]<\/code> to the benchmark and rerunning we get:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAlloc.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251605\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAlloc.png\" alt=\"Allocation benchmark\" width=\"1689\" height=\"484\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAlloc.png 1689w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAlloc-300x86.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAlloc-1024x293.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAlloc-768x220.png 768w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAlloc-1536x440.png 1536w\" sizes=\"(max-width: 1689px) 100vw, 1689px\" \/><\/a><\/p>\n<p>The biggest thing of note is the line at the bottom showing the path to a collected diagsession file which is the Visual Studio profilers file format for its results. With the new updates it automatically opens in VS and now we have all the stuff we need to dig in and maybe reduce some allocations.<\/p>\n<h2>Hunting for allocations<\/h2>\n<p>Now that we have a diagsession detailing all the allocations from the run, lets see if we can reduce the allocations we are doing and reduce the load on the garbage collector.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/AllocatedTypes.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251598\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/AllocatedTypes.png\" alt=\"Types allocated in benchmark run\" width=\"1279\" height=\"799\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/AllocatedTypes.png 1279w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/AllocatedTypes-300x187.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/AllocatedTypes-1024x640.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/AllocatedTypes-768x480.png 768w\" sizes=\"(max-width: 1279px) 100vw, 1279px\" \/><\/a><\/p>\n<p>Since our benchmark is designed to deserialize 10,000 records from the memory stream we want to look for anything that is a multiple of 10,000 as that is indicating it is allocating for each record. Immediately we see, <code>string<\/code>, <code>Type[]<\/code>, <code>int32<\/code>, and <code>Simple<\/code>. <code>String<\/code> and <code>Int32<\/code> are the properties on our <code>Simple<\/code> record type so that makes sense, and <code>Simple<\/code> is the record type we are deserializing so again that makes sense. The <code>Type[]<\/code> is a bit suspicious and digging in further things just look worse:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/EmptyTypes.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251599\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/EmptyTypes.png\" alt=\"Empty array allocations during benchmark run\" width=\"1279\" height=\"799\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/EmptyTypes.png 1279w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/EmptyTypes-300x187.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/EmptyTypes-1024x640.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/EmptyTypes-768x480.png 768w\" sizes=\"(max-width: 1279px) 100vw, 1279px\" \/><\/a><\/p>\n<p>In this case it looks like we are allocating an empty <code>Type[]<\/code> for each record we deserialize, each one being 24 bytes adding up to 7.6MB of allocations in this benchmark run. These junk allocations that can&#8217;t hold any data wind up accounting for 14% of allocations in the benchmark run. This is crazy, and something we should be able to fix. Double clicking on the type shows us the backtraces, which shows it is coming from some anonymous function:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/TypeAllocationLocation.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251600\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/TypeAllocationLocation.png\" alt=\"Empty type array allocation location\" width=\"1279\" height=\"799\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/TypeAllocationLocation.png 1279w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/TypeAllocationLocation-300x187.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/TypeAllocationLocation-1024x640.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/TypeAllocationLocation-768x480.png 768w\" sizes=\"(max-width: 1279px) 100vw, 1279px\" \/><\/a><\/p>\n<p>Going to source for this (right click context menu-&gt; Go to Source File) we see the following:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolver.png\"><img decoding=\"async\" class=\"alignnone wp-image-251564 size-full\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolver.png\" alt=\"ObjectResolver function from CsvHelper NuGet package\" width=\"790\" height=\"328\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolver.png 790w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolver-300x125.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolver-768x319.png 768w\" sizes=\"(max-width: 790px) 100vw, 790px\" \/><\/a><\/p>\n<p>To me it isn\u2019t immediately obvious where this allocation is coming from, so the easiest thing to do is just add a breakpoint to the <code>CreateInstance<\/code> call and then look around in the debugger. Now BDN runs our benchmarks in a separate process to better control the benchmarks, so to debug we can just instantiate our benchmark and call our benchmark method ourself. We can do this by updating main like so:<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">static async Task Main(string[] args)\r\n{\r\n\t\/\/_ = BenchmarkRunner.Run&lt;BenchmarkEnumerateRecords&gt;();\r\n\r\n\tvar benchmarks = new BenchmarkEnumerateRecords();\r\n\tbenchmarks.GlobalSetup();\r\n\tawait benchmarks.EnumerateRecords();\r\n\tbenchmarks.GlobalCleanup();\r\n}<\/code><\/pre>\n<p>Running in the debugger we hit where our allocation is occurring:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolverDebugger.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251565\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolverDebugger.png\" alt=\"In ObjectResolver code running under the debugger\" width=\"984\" height=\"337\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolverDebugger.png 984w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolverDebugger-300x103.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/ObjectResolverDebugger-768x263.png 768w\" sizes=\"(max-width: 984px) 100vw, 984px\" \/><\/a><\/p>\n<p>Again, it isn\u2019t super obvious where this allocation is occurring so let\u2019s step into the call in case this is coming from an inlined frame:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/CreateInstanceDebugger.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251569\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/CreateInstanceDebugger.png\" alt=\"CreateInstance function running under VS debugger\" width=\"1054\" height=\"309\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/CreateInstanceDebugger.png 1054w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/CreateInstanceDebugger-300x88.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/CreateInstanceDebugger-1024x300.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/CreateInstanceDebugger-768x225.png 768w\" sizes=\"(max-width: 1054px) 100vw, 1054px\" \/><\/a><\/p>\n<p>This method has a <code>Type<\/code> parameter but not <code>Type[]<\/code>. It is a relatively short function though so maybe it and its caller are getting inlined so let&#8217;s step in again.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetFuncDebugger.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251566\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetFuncDebugger.png\" alt=\"Image GetFuncDebugger\" width=\"1027\" height=\"339\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetFuncDebugger.png 1027w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetFuncDebugger-300x99.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetFuncDebugger-1024x338.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetFuncDebugger-768x254.png 768w\" sizes=\"(max-width: 1027px) 100vw, 1027px\" \/><\/a><\/p>\n<p>Unfortunately no <code>Type[]<\/code> yet, but this method is marked as <code>AggressiveInlining<\/code> which explains why we didn&#8217;t see it in the allocation stack. One last step in and boom, we got our <code>Type[]<\/code> allocation!<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetArgTypesDebugger.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251567\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetArgTypesDebugger.png\" alt=\"Image GetArgTypesDebugger\" width=\"943\" height=\"312\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetArgTypesDebugger.png 943w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetArgTypesDebugger-300x99.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/GetArgTypesDebugger-768x254.png 768w\" sizes=\"(max-width: 943px) 100vw, 943px\" \/><\/a><\/p>\n<p>Now this is where the ah hah moment is! We are calling <code>GetArgTypes<\/code> which returns a <code>Type[]<\/code> based on the <code>object[]<\/code> that is passed in. We start by allocating an array that is the same size as the <code>object[]<\/code>, but if the <code>object[]<\/code> is length 0 then we allocate a new 0 length array. In this case we can easily fix this by checking the parameter size and returning early in the case that there are no arguments to get the type from.<\/p>\n<pre class=\"prettyprint language-cs language-csharp\"><code class=\"language-cs language-csharp\">[MethodImpl(MethodImplOptions.AggressiveInlining)]\r\nprivate static Type[] GetArgTypes(object?[] args)\r\n{\r\n\tif (args.Length == 0)\r\n\t{\r\n\t\treturn Array.Empty&lt;Type&gt;();\r\n\t}\r\n\r\n\tvar argTypes = new Type[args.Length];\r\n\tfor (var i = 0; i &lt; args.Length; i++)\r\n\t{\r\n\t\targTypes[i] = args[i]?.GetType() ?? typeof(object);\r\n\t}\r\n\r\n\treturn argTypes;\r\n}<\/code><\/pre>\n<p>After making this change, we can rerun our benchmarks, remember measure\/change\/measure, and we get the following:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAfter.png\"><img decoding=\"async\" class=\"alignnone size-full wp-image-251574\" src=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAfter.png\" alt=\"Benchmark run after local changes showing performance improvement\" width=\"1235\" height=\"345\" srcset=\"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAfter.png 1235w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAfter-300x84.png 300w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAfter-1024x286.png 1024w, https:\/\/devblogs.microsoft.com\/visualstudio\/wp-content\/uploads\/sites\/4\/2024\/11\/BenchmarkAfter-768x215.png 768w\" sizes=\"(max-width: 1235px) 100vw, 1235px\" \/><\/a><\/p>\n<p>We effectively cut down on the allocated memory by ~14%! While this may not seem like a huge win, this scales with the number of records. For CSV files with lots of records this is a big win, especially in a library that is already very fast and heavily optimized.<\/p>\n<h2>Let us know what you think<\/h2>\n<p>In summary, we were able to take a real-world project, add a benchmark to it, use the Visual Studio profiler, and make a meaningful contribution in just a single blog post. By creating a benchmarking suite, it is easy to isolate specific code that you want to improve by measure\/change\/measure and see the impact of your performance optimizations. We\u2019d love to hear what you think! <div  class=\"d-flex justify-content-left\"><a class=\"cta_button_link btn-primary mb-24\" href=\"https:\/\/www.surveymonkey.com\/r\/TRB7LHF \" target=\"_blank\">Share your thoughts<\/a><\/div><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In Visual Studio 17.13 Preview we have released our updated BenchmarkDotNet diagnosers, allowing you to use more of the tools in the performance profiler to analyze benchmarks. With this change it is super quick to dig into CPU usage and allocations of benchmarks making the measure, change, measure cycle quick and efficient. Benchmarking a real [&hellip;]<\/p>\n","protected":false},"author":54885,"featured_media":252014,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[6868,155],"tags":[9,6839,6803],"class_list":["post-251545","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-performance","category-visual-studio","tag-debug","tag-developer-productivity","tag-visual-studio-profiler"],"acf":[],"blog_post_summary":"<p>In Visual Studio 17.13 Preview we have released our updated BenchmarkDotNet diagnosers, allowing you to use more of the tools in the performance profiler to analyze benchmarks. With this change it is super quick to dig into CPU usage and allocations of benchmarks making the measure, change, measure cycle quick and efficient. Benchmarking a real [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/251545","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/users\/54885"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/comments?post=251545"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/posts\/251545\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media\/252014"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/media?parent=251545"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/categories?post=251545"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/visualstudio\/wp-json\/wp\/v2\/tags?post=251545"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}