7

ログ文字列を循環バッファに保持するアプリケーションがあります。ログがいっぱいになると、新しい挿入ごとに、ガベージ コレクションのために古い文字列が解放され、ジェネレーション 2 メモリに保存されます。したがって、最終的には第 2 世代の GC が発生しますが、これは避けたいと思います。

文字列を構造体にマーシャリングしようとしました。驚いたことに、私はまだジェネレーション 2 GC:s を取得しています。構造体はまだ文字列への参照を保持しているようです。以下のコンソール アプリを完成させます。どんな助けでも感謝します。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    class Program
    {

        [StructLayout(LayoutKind.Sequential)]
        public struct FixedString
        {
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
            private string str;

            public FixedString(string str)
            {
                this.str = str;
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct UTF8PackedString
        {
            private int length;

            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
            private byte[] str;

            public UTF8PackedString(int length)
            {
                this.length = length;
                str = new byte[length];
            }

            public static implicit operator UTF8PackedString(string str)
            {
                var obj = new UTF8PackedString(Encoding.UTF8.GetByteCount(str));
                var bytes = Encoding.UTF8.GetBytes(str);
                Array.Copy(bytes, obj.str, obj.length);
                return obj;
            }
        }

        const int BufferSize = 1000000;
        const int LoopCount = 10000000;

        static void Main(string[] args)
        {
            Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
                "Type".PadRight(20), "Time", "GC(0)", "GC(1)", "GC(2)");
            Console.WriteLine();
            for (int i = 0; i < 5; i++)
            {
                TestPerformance<string>(s => s);
                TestPerformance<FixedString>(s => new FixedString(s));
                TestPerformance<UTF8PackedString>(s => s);
                Console.WriteLine();
            }
            Console.ReadKey();
        }

        private static void TestPerformance<T>(Func<string, T> func)
        {
            var buffer = new T[BufferSize];
            GC.Collect(2);
            Stopwatch stopWatch = new Stopwatch();
            var initialCollectionCounts = new int[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
            stopWatch.Reset();
            stopWatch.Start();
            for (int i = 0; i < LoopCount; i++)
                buffer[i % BufferSize] = func(i.ToString());
            stopWatch.Stop();
            Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
                typeof(T).Name.PadRight(20),
                stopWatch.ElapsedMilliseconds,
                (GC.CollectionCount(0) - initialCollectionCounts[0]),
                (GC.CollectionCount(1) - initialCollectionCounts[1]),
                (GC.CollectionCount(2) - initialCollectionCounts[2])
            );
        }
    }
}

編集:必要な作業を行う UnsafeFixedString でコードを更新しました:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication
{
    class Program
    {
        public unsafe struct UnsafeFixedString
        {
            private int length;

            private fixed char str[256];

            public UnsafeFixedString(int length)
            {
                this.length = length;
            }

            public static implicit operator UnsafeFixedString(string str)
            {
                var obj = new UnsafeFixedString(str.Length);
                for (int i = 0; i < str.Length; i++)
                    obj.str[i] = str[i];                
                return obj;
            }
        }

        const int BufferSize = 1000000;
        const int LoopCount = 10000000;

        static void Main(string[] args)
        {
            Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
                "Type".PadRight(20), "Time", "GC(0)", "GC(1)", "GC(2)");
            Console.WriteLine();
            for (int i = 0; i < 5; i++)
            {
                TestPerformance(s => s);
                TestPerformance<UnsafeFixedString>(s => s);
                Console.WriteLine();
            }
            Console.ReadKey();
        }

        private static void TestPerformance<T>(Func<string, T> func)
        {
            var buffer = new T[BufferSize];
            GC.Collect(2);
            Stopwatch stopWatch = new Stopwatch();
            var initialCollectionCounts = new int[] { GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2) };
            stopWatch.Reset();
            stopWatch.Start();
            for (int i = 0; i < LoopCount; i++)
                buffer[i % BufferSize] = func(String.Format("{0}", i));
            stopWatch.Stop();
            Console.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}",
                typeof(T).Name.PadRight(20),
                stopWatch.ElapsedMilliseconds,
                (GC.CollectionCount(0) - initialCollectionCounts[0]),
                (GC.CollectionCount(1) - initialCollectionCounts[1]),
                (GC.CollectionCount(2) - initialCollectionCounts[2])
            );
        }
    }
}

私のコンピューターの出力は次のとおりです。

Type                    Time    GC(0)   GC(1)   GC(2)

String                  5746    160     71      19
UnsafeFixedString       5345    418     0       0
4

4 に答える 4