コガネブログ

平日更新を目標に Unity や C#、Visual Studio、ReSharper などのゲーム開発アレコレを書いていきます

【C#】ゼロアロケーションの LINQ を使用できる「StructLinq」紹介

はじめに

「StructLinq」を Unity プロジェクトに導入することで
ゼロアロケーションの LINQ を使用できるようになります

検証環境

  • Unity 2021.2.7f1
  • Windows IL2CPP ビルド

検証結果

using System;
using System.Linq;
using StructLinq;
using UnityEngine;
using UnityEngine.Profiling;

public sealed class Example : MonoBehaviour
{
    private readonly CharacterMaster[] m_characterMasters = Enumerable
        .Range( 0, 1_000_000 )
        .Select( x => new CharacterMaster( x ) )
        .ToArray();

    private void Update()
    {
        if ( !Input.GetKeyDown( KeyCode.Space ) &&
             !Input.GetKeyDown( KeyCode.Escape ) )
        {
            return;
        }

        using ( new CustomSamplerScope( "#### Linq" ) )
        {
            m_characterMasters
                .Where( x => x.Id % 2 == 1 )
                .Select( x => x.Id * 2 )
                ;
        }

        using ( new CustomSamplerScope( "#### Struct Linq With Delegate" ) )
        {
            m_characterMasters
                .ToStructEnumerable()
                .Where( x => x.Id % 2 == 1 )
                .Select( x => x.Id * 2 )
                ;
        }

        using ( new CustomSamplerScope( "#### Struct Linq With Delegate Zero Alloc" ) )
        {
            m_characterMasters
                .ToStructEnumerable()
                .Where( x => x.Id % 2 == 1, x => x )
                .Select( x => x.Id * 2, x => x )
                ;
        }

        using ( new CustomSamplerScope( "#### Struct Linq Zero Alloc" ) )
        {
            var where  = new WherePredicate();
            var select = new SelectPredicate();

            m_characterMasters
                .ToStructEnumerable()
                .Where( ref where, x => x )
                .Select( ref select, x => x, x => x )
                ;
        }
    }

    private struct WherePredicate : IFunction<CharacterMaster, bool>
    {
        public bool Eval( CharacterMaster element )
        {
            return element.Id % 2 == 1;
        }
    }

    private struct SelectPredicate : IFunction<CharacterMaster, int>
    {
        public int Eval( CharacterMaster element )
        {
            return element.Id * 2;
        }
    }

    private readonly struct CustomSamplerScope : IDisposable
    {
        private readonly CustomSampler m_customSampler;

        public CustomSamplerScope( string name )
        {
            m_customSampler = CustomSampler.Create( name );
            m_customSampler.Begin();
        }

        public void Dispose()
        {
            m_customSampler.End();
        }
    }

    private sealed class CharacterMaster
    {
        public int Id { get; }

        public CharacterMaster( int id )
        {
            Id = id;
        }
    }
}

f:id:baba_s:20220125172028p:plain