{"id":54282,"date":"2025-12-01T05:25:32","date_gmt":"2025-12-01T05:25:32","guid":{"rendered":"https:\/\/www.devopsschool.com\/blog\/?p=54282"},"modified":"2025-12-01T05:25:32","modified_gmt":"2025-12-01T05:25:32","slug":"dotnet-memory-optimization-in-net-with-valuetask","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/blog\/dotnet-memory-optimization-in-net-with-valuetask\/","title":{"rendered":"DOTNET: Memory Optimization in .NET with ValueTask"},"content":{"rendered":"\n<p>Here\u2019s a <strong>single, self-contained console app<\/strong> that lets you <em>experience<\/em> the difference between:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\udeab <strong>Without <code>ValueTask<\/code><\/strong> \u2013 using <code>async Task&lt;int><\/code> (allocates a <code>Task<\/code> even on fast sync path)<\/li>\n\n\n\n<li>\u2705 <strong>With <code>ValueTask<\/code><\/strong> \u2013 returning synchronously without allocating when value is cached<\/li>\n<\/ul>\n\n\n\n<p>You\u2019ll see timing + GC stats for both in the same run.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">1\ufe0f\u20e3 Full Code \u2013 <code>Program.cs<\/code><\/h2>\n\n\n\n<script src=\"https:\/\/gist.github.com\/devops-school\/5e001d4382b6ca68b6ffd60eeb6085ca.js\"><\/script>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">\nThis is <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">strong<\/span>&gt;<\/span>100% self-contained<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">strong<\/span>&gt;<\/span> \u2013 no extra packages needed.<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">2\ufe0f\u20e3 Optional \u2013 Minimal <code>.csproj<\/code><\/h2>\n\n\n\n<p>If you want to control the target framework explicitly, create <code>ValueTaskDemo.csproj<\/code>:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Project<\/span> <span class=\"hljs-attr\">Sdk<\/span>=<span class=\"hljs-string\">\"Microsoft.NET.Sdk\"<\/span>&gt;<\/span>\n\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PropertyGroup<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">OutputType<\/span>&gt;<\/span>Exe<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">OutputType<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TargetFramework<\/span>&gt;<\/span>net8.0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">TargetFramework<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ImplicitUsings<\/span>&gt;<\/span>enable<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ImplicitUsings<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Nullable<\/span>&gt;<\/span>enable<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Nullable<\/span>&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">PropertyGroup<\/span>&gt;<\/span>\n\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Project<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can switch to <code>net9.0<\/code> or <code>net10.0<\/code> if you\u2019re targeting .NET 9\/10 SDK.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">3\ufe0f\u20e3 Step-by-Step: How to Run It<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Step 1 \u2013 Create a new console project<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">dotnet <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">console<\/span> -n ValueTaskDemo\ncd ValueTaskDemo\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Step 2 \u2013 Replace <code>Program.cs<\/code><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Open <code>Program.cs<\/code> in your editor<\/li>\n\n\n\n<li>Delete everything<\/li>\n\n\n\n<li>Paste the full code from section <strong>1\ufe0f\u20e3<\/strong><\/li>\n<\/ul>\n\n\n\n<p>(Optionally replace the generated <code>.csproj<\/code> with the file in <strong>2\ufe0f\u20e3<\/strong>.)<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3 \u2013 Build and run in <strong>Release<\/strong> mode<\/h3>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs\">dotnet run -c Release\n<\/code><\/span><\/pre>\n\n\n<p>You\u2019ll see output similar to:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\">=========================================\n   ValueTask Demo \u2013 Task vs ValueTask    \n=========================================\n\nIterations : 5,000,000\nCache size : 1,000\nScenario   : All calls hit the FAST (cached) path.\n\nWarming up (small runs)...\n--- Warmup \u2013 async Task<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int<\/span>&gt;<\/span> ---\nIterations        : 500,000\nTime Elapsed      : 80 ms\nGC Gen0           : 10\nGC Gen1           : 0\nGC Gen2           : 0\nManaged Memory \u0394  : 3.20 MB\nChecksum (ignore) : 499500000\n\n--- Warmup \u2013 ValueTask<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int<\/span>&gt;<\/span> ---\nIterations        : 500,000\nTime Elapsed      : 45 ms\nGC Gen0           : 3\nGC Gen1           : 0\nGC Gen2           : 0\nManaged Memory \u0394  : 0.70 MB\nChecksum (ignore) : 499500000\n\n=========== REAL TESTS (Release) ==========\n\n--- WITHOUT ValueTask \u2013 async Task<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int<\/span>&gt;<\/span> ---\nIterations        : 5,000,000\nTime Elapsed      : 800 ms\nGC Gen0           : 100\nGC Gen1           : 5\nGC Gen2           : 0\nManaged Memory \u0394  : 30.50 MB\nChecksum (ignore) : 4995000000\n\n--- WITH ValueTask \u2013 ValueTask<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">int<\/span>&gt;<\/span> ---\nIterations        : 5,000,000\nTime Elapsed      : 430 ms\nGC Gen0           : 15\nGC Gen1           : 0\nGC Gen2           : 0\nManaged Memory \u0394  : 5.10 MB\nChecksum (ignore) : 4995000000\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>(Exact numbers will vary per machine, but the <strong>pattern<\/strong> should be similar.)<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">4\ufe0f\u20e3 How to \u201cExperience\u201d and Interpret the Results<\/h2>\n\n\n\n<p>Focus on these for each scenario:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u23f1 <code>Time Elapsed<\/code><\/li>\n\n\n\n<li>\ud83d\uddd1 <code>GC Gen0 \/ Gen1 \/ Gen2<\/code><\/li>\n\n\n\n<li>\ud83d\udcbe <code>Managed Memory \u0394<\/code><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udd34 Scenario 1 \u2013 WITHOUT <code>ValueTask<\/code> (async Task)<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">public <span class=\"hljs-keyword\">async<\/span> Task&lt;int&gt; GetValueAsync(int key)\n{\n    <span class=\"hljs-keyword\">if<\/span> (_cache.TryGetValue(key, out <span class=\"hljs-keyword\">var<\/span> value))\n    {\n        <span class=\"hljs-keyword\">return<\/span> value; <span class=\"hljs-comment\">\/\/ looks sync, but method is async<\/span>\n    }\n\n    <span class=\"hljs-keyword\">await<\/span> Task.Delay(<span class=\"hljs-number\">1<\/span>).ConfigureAwait(<span class=\"hljs-literal\">false<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> key;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Even when the value is <strong>immediately available in cache<\/strong>, the compiler:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Generates an <strong>async state machine<\/strong><\/li>\n\n\n\n<li>Allocates a <strong>Task object<\/strong> for each call<\/li>\n<\/ul>\n\n\n\n<p>In a tight loop with millions of calls, you\u2019ll see:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Higher <code>Time Elapsed<\/code><\/li>\n\n\n\n<li>More <code>GC Gen0<\/code> collections<\/li>\n\n\n\n<li>Higher <code>Managed Memory \u0394<\/code> (more allocations)<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udfe2 Scenario 2 \u2013 WITH <code>ValueTask<\/code> (ValueTask)<\/h3>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">public<\/span> ValueTask&lt;int&gt; GetValueAsync(int key)\n{\n    <span class=\"hljs-keyword\">if<\/span> (_cache.TryGetValue(key, out <span class=\"hljs-keyword\">var<\/span> value))\n    {\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> ValueTask&lt;int&gt;(value); <span class=\"hljs-comment\">\/\/ completes synchronously, no Task allocation<\/span>\n    }\n\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">new<\/span> ValueTask&lt;int&gt;(SlowPathAsync(key)); <span class=\"hljs-comment\">\/\/ real Task only on slow path<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>For the <strong>fast path<\/strong> (cache hit), we:\n<ul class=\"wp-block-list\">\n<li>Do <strong>not<\/strong> create a <code>Task<\/code><\/li>\n\n\n\n<li>Do <strong>not<\/strong> create a state machine<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>We only use a real <code>Task&lt;int><\/code> on the <strong>slow path<\/strong> (<code>SlowPathAsync<\/code>), which we\u2019re not hitting in this benchmark.<\/li>\n<\/ul>\n\n\n\n<p>In the output, you should see:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Lower elapsed time<\/strong> (less allocation + less GC work)<\/li>\n\n\n\n<li><strong>Much fewer Gen0 collections<\/strong><\/li>\n\n\n\n<li><strong>Smaller Managed Memory \u0394<\/strong><\/li>\n<\/ul>\n\n\n\n<p>This is <strong>exactly<\/strong> the use case where <code>ValueTask<\/code> shines:<br>A method that is <em>often synchronous<\/em> but must expose an async API.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">5\ufe0f\u20e3 Tweaks to Make the Effect More Visible<\/h2>\n\n\n\n<p>You can tune:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>iterations<\/code> (e.g., <code>10_000_000<\/code> if your machine is strong)<\/li>\n\n\n\n<li><code>cacheSize<\/code> (smaller cache keeps hit ratio at 100%)<\/li>\n<\/ul>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> int iterations = <span class=\"hljs-number\">10<\/span>_000_000;\n<span class=\"hljs-keyword\">const<\/span> int cacheSize = <span class=\"hljs-number\">100<\/span>;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This increases the number of fast-path calls and <strong>magnifies the Task vs ValueTask difference<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Here\u2019s a single, self-contained console app that lets you experience the difference between: You\u2019ll see timing + GC stats for both in the same run. 1\ufe0f\u20e3 Full Code \u2013 Program.cs&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_joinchat":[],"footnotes":""},"categories":[11138],"tags":[],"class_list":["post-54282","post","type-post","status-publish","format-standard","hentry","category-best-tools"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54282","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/comments?post=54282"}],"version-history":[{"count":1,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54282\/revisions"}],"predecessor-version":[{"id":54283,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/posts\/54282\/revisions\/54283"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/media?parent=54282"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/categories?post=54282"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/blog\/wp-json\/wp\/v2\/tags?post=54282"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}