<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged span</title>
<link>https://coderlegion.com/tag/span</link>
<description>Powered by Question2Answer</description>
<item>
<title>Your Game’s GC Spikes? Blame Yourself.</title>
<link>https://coderlegion.com/22521/your-games-gc-spikes-blame-yourself</link>
<description>
&lt;h3 id=&quot;mastering-allocation-free-data-processing-with-span-and-memory-in-unity&quot;&gt;Mastering Allocation-Free Data Processing with Span and Memory in Unity&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Introduction&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tired of unpredictable stuttering and frustrating garbage collection (GC) spikes in your high-performance Unity game? Those erratic frame drops often stem from subtle, repeated memory allocations, even within performance-critical systems. While Unity&amp;rsquo;s Job System and Burst Compiler aim for allocation-free execution, common C# patterns can inadvertently introduce GC pressure. This tutorial will guide you through mastering &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Memory&amp;lt;T&amp;gt;&lt;/code&gt;, two powerful C# features indispensable for achieving truly allocation-free, Burst-compatible code, particularly when working with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NativeArray&amp;lt;T&amp;gt;&lt;/code&gt; and unmanaged memory within Unity&amp;rsquo;s Data-Oriented Technology Stack (DOTS).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code Layout/Walkthrough: Eliminating GC Spikes with Zero-Allocation Views&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A frequent culprit for GC spikes is the unnecessary creation of temporary arrays or sub-arrays. Functions that need to process a segment of a larger data block often resort to methods like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array.Copy&lt;/code&gt; or LINQ extensions like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Skip().Take().ToArray()&lt;/code&gt;. While convenient, each of these operations allocates new memory on the heap. In a tight game loop or within high-frequency jobs, repeated allocations quickly accumulate, triggering the garbage collector and causing noticeable frame rate instability.&lt;/p&gt;

&lt;p&gt;Enter &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Memory&amp;lt;T&amp;gt;&lt;/code&gt;. These types provide a modern, allocation-free way to &lt;em&gt;reference&lt;/em&gt; existing contiguous blocks of memory, whether managed (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;byte[]&lt;/code&gt;) or unmanaged (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NativeArray&amp;lt;T&amp;gt;&lt;/code&gt; elements). They allow you to define a &amp;ldquo;view&amp;rdquo; or &amp;ldquo;slice&amp;rdquo; into a larger data structure without copying any data. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref struct&lt;/code&gt;, meaning it can only live on the stack and cannot be boxed or used in async methods, offering extreme performance for synchronous operations. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Memory&amp;lt;T&amp;gt;&lt;/code&gt; is a managed struct that wraps a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt;, allowing it to be used in scenarios where &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt;&amp;rsquo;s stack-only limitations are prohibitive (e.g., heap allocation, asynchronous operations, or when storing a reference).&lt;/p&gt;

&lt;p&gt;For Unity developers utilizing the Job System, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NativeArray&amp;lt;T&amp;gt;&lt;/code&gt; is fundamental. Slicing a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NativeArray&amp;lt;T&amp;gt;&lt;/code&gt; conventionally by creating a new &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NativeArray&amp;lt;T&amp;gt;&lt;/code&gt; would involve allocations and potential safety overhead. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt;, you can create a direct, zero-allocation view:&lt;/p&gt;

&lt;div class=&quot;language-csharp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Unity.Collections&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;Unity.Jobs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// For Span&amp;lt;T&amp;gt;&lt;/span&gt;

&lt;span class=&quot;c1&quot;&gt;// Define a job that processes data&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MyDataProcessorJob&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IJob&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ReadOnly&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NativeArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;NativeArray&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OutputData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Execute&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// 1. Create a Span&amp;lt;int&amp;gt; for a specific segment of InputData.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//    InputData.Slice() returns a NativeArray&amp;lt;int&amp;gt;, which has an implicit&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//    conversion to Span&amp;lt;int&amp;gt;. Crucially, no new int[] or NativeArray&amp;lt;int&amp;gt;&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//    is allocated for this view.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputSegment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// View elements from index 10, length 20&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 2. Create a Span&amp;lt;int&amp;gt; for a corresponding segment of OutputData.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;Span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outputSegment&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OutputData&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;c1&quot;&gt;// 3. Process the segments using an allocation-free pure function.&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;//    Passing Span&amp;lt;int&amp;gt; by 'ref' avoids copying the Span struct itself.&lt;/span&gt;
        &lt;span class=&quot;nf&quot;&gt;ProcessDataSlice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;inputSegment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;outputSegment&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// An example pure function that operates directly on memory segments&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// without any allocations. Ideal for Burst-compiled contexts.&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;input&quot;&amp;gt;A read-only view of the input data segment.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;/// &amp;lt;param name=&quot;output&quot;&amp;gt;A writable view of the output data segment.&amp;lt;/param&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;ProcessDataSlice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;c1&quot;&gt;// Ensure segments are of compatible length, or handle mismatch&lt;/span&gt;
        &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Math&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;Min&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;++)&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// Example complex processing&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this example, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;inputSegment&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;outputSegment&lt;/code&gt; are not copies of the underlying data. They are simply lightweight, stack-allocated references pointing to specific parts of the existing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;InputData&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;OutputData&lt;/code&gt; memory blocks. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ProcessDataSlice&lt;/code&gt; function is a &amp;ldquo;pure&amp;rdquo; function in this context &amp;ndash; it operates directly on these provided memory views without allocating anything itself. Passing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Span&amp;lt;int&amp;gt;&lt;/code&gt; by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ref&lt;/code&gt; further optimizes performance by preventing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Span&lt;/code&gt; struct from being copied on the stack during the function call. This pattern aligns perfectly with Burst&amp;rsquo;s optimization goals, leading to highly predictable and incredibly fast code execution within your jobs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Embracing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Span&amp;lt;T&amp;gt;&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Memory&amp;lt;T&amp;gt;&lt;/code&gt; is more than just a &amp;ldquo;nice-to-have&amp;rdquo; optimization; it&amp;rsquo;s a fundamental shift towards writing truly high-performance, allocation-free C# code in Unity. By referencing rather than copying memory segments, especially within your Job System architecture, you effectively eliminate a major source of garbage collection pressure. This directly translates to stable frame times, significantly reduced stuttering, and a consistently smoother player experience. Stop chasing ghost allocations and start leveraging these modern C# features to unlock your game&amp;rsquo;s full, predictable performance potential.&lt;/p&gt;
</description>
<category>Other</category>
<guid isPermaLink="true">https://coderlegion.com/22521/your-games-gc-spikes-blame-yourself</guid>
<pubDate>Wed, 15 Jul 2026 21:27:03 +0000</pubDate>
</item>
</channel>
</rss>