簡體   English   中英

Array.Sort() 分配大量 memory

[英]Array.Sort() allocating a large amount of memory

我正在處理以 60fps 運行的可視化。 該可視化的一部分是根據 position 對屏幕上的項目進行排序。我正在使用

Array.Sort<T>(T[] array, int index, int length, IComparer<T> comparer)

每秒分配近 1MB 的Comparison<T> ,這會導致 GC 頻繁運行,從而導致幀速率中斷。

我已經嘗試了Array.Sort的幾種變體,它們都在分配,包括接受Comparison<T>的變體(這也是不夠的,因為它缺少indexlength參數)。

有什么方法可以在不分配大量 memory 的情況下對 C# (.NET 5) 中的數組進行排序?

更新:這是一個復制品,

using System;
using System.Collections.Generic;

namespace New_folder
{
    public class EmptyClass
    {
        // Empty
    }

    public class EmptyClassComparer : IComparer<EmptyClass>
    {
        public int Compare(EmptyClass x, EmptyClass y)
        {
            return 0;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            EmptyClass[] emptyClasses = new EmptyClass[100];
            for (int i = 0; i < 100; i++)
            {
                emptyClasses[i] = new EmptyClass();
            }
            EmptyClassComparer emptyClassComparer = new EmptyClassComparer();
            while (true)
            {
                Array.Sort(emptyClasses, 0, 50, emptyClassComparer);
            }
        }
    }
}

以及在舊機器上 30 秒后的分配,

比較分配

我能夠重現您的觀察結果。 在我的機器上以及在 do.netfiddle.net 上,每個Array.Sort操作分配 64 個字節:

var random = new Random(0);
var array = Enumerable.Range(1, 12).OrderBy(_ => random.Next()).ToArray();
Console.WriteLine($"Before Sort: [{String.Join(", ", array)}]");
var comparer = Comparer<int>.Create((x, y) => y - x); // Descending
const int loops = 1_000_000;
var mem0 = GC.GetTotalAllocatedBytes(true);
for (int i = 0; i < loops; i++)
{
    Array.Sort(array, 1, 10, comparer);
}
var mem1 = GC.GetTotalAllocatedBytes(true);
Console.WriteLine($"After Sort:  [{String.Join(", ", array)}]");
Console.WriteLine($"Allocated:   {(mem1 - mem0) / (double)loops:#,0} bytes per loop");

Output:

Before Sort: [5, 10, 11, 8, 12, 4, 6, 1, 3, 2, 7, 9]
After Sort:  [5, 12, 11, 10, 8, 7, 6, 4, 3, 2, 1, 9]
Allocated:   64 bytes per loop

現場演示

在我看來,可以在 .NET 標准庫中進行優化。 作為解決方法,您可以使用下面的Sort擴展方法,它不分配。 盡管它接受Comparison<T>而不是IComparer<T>

public static void Sort<T>(this T[] array, int index, int length,
    Comparison<T> comparison)
{
    Span<T> span = new(array, index, length);
    MemoryExtensions.Sort(span, comparison);
}

使用示例。 和以前一樣,有這兩個變化:

Comparison<int> comparison = (x, y) => y - x; // Descending
//...
array.Sort(1, 10, comparison);

Output:

Before Sort: [5, 10, 11, 8, 12, 4, 6, 1, 3, 2, 7, 9]
After Sort:  [5, 12, 11, 10, 8, 7, 6, 4, 3, 2, 1, 9]
Allocated:   0 bytes per loop

現場演示

好像也快了一點。


更新:在 GitHub 上開了一個關於這個問題的新帖子,我得到了以下反饋:

分配跟蹤顯示分配是委托Comparison<int>

分配在這里

由於缺少價值委托,恐怕我們現在無能為力。

我的建議是使用Comparison<T>而不是IComparer<T> ,並通過 lambda 或字段緩存委托。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM