簡體   English   中英

兩種方式切割陣列的最快方法

[英]Fastest way to chop array in two pieces

我有一個數組,說:

var arr1 = new [] { 1, 2, 3, 4, 5, 6 };

現在,當我的數組大小超過5時,我想將當前數組的大小調整為3,並創建一個包含上3個值的新數組,因此在執行此操作之后:

arr1 = new [] { 1, 2, 3 };
newArr = new [] { 4, 5, 6 };

最快的方法是什么? 我想我將不得不調查非管理角落,但沒有任何線索。


更多信息:

  • 陣列必須能夠在沒有大的性能命中的情況下進行調整
  • 這些數組只包含Int32
  • 數組的目的是在我的源數組中對數字進行分組,而不必對整個列表進行排序

簡而言之:我想拆分以下輸入數組:

int[] arr = new int[] { 1, 3, 4, 29, 31, 33, 35, 36, 37 };

arr1 =  1, 3, 4
arr2 =  29, 31, 33, 35, 36, 37

但由於數組大小為3時達到理想速度,因此應將arr2拆分為2個大小均勻的陣列。

注意

我知道數組在內存中的實現非常幼稚(好吧,至少在C中,你可以操作數組中的項目數,以便數組調整大小)。 此外,Win32 API中的某處還有一個memory move功能。 所以我想這會是最快的:

  1. 更改arr1所以它只包含3個項目
  2. 創建大小為3的新數組arr2
  3. 將不在arr1的字節重新導入到arr2

我不確定有什么比創建空數組更好,然后使用Array.Copy 我至少希望在內部進行優化:)

int[] firstChunk = new int[3];
int[] secondChunk = new int[3];
Array.Copy(arr1, 0, firstChunk, 0, 3);
Array.Copy(arr1, 3, secondChunk, 0, 3);

老實說,對於非常小的數組,方法調用的開銷可能大於僅僅顯式分配元素 - 但我認為實際上你將使用稍大一些的:)

您可能還會考慮實際拆分數組,而是使用ArraySegment來分離數組的“塊”。 或者也許使用List<T>開始......如果沒有更多的上下文,很難知道。

如果速度真的很關鍵,那么使用指針的非托管代碼可能是最快的方法 - 但我肯定會檢查你是否真的需要去冒險進入不安全的代碼。

你在找這樣的東西嗎?

static unsafe void DoIt(int* ptr)
{
    Console.WriteLine(ptr[0]);
    Console.WriteLine(ptr[1]);
    Console.WriteLine(ptr[2]);
}

static unsafe void Main()
{
    var bytes = new byte[1024];
    new Random().NextBytes(bytes);

    fixed (byte* p = bytes)
    {
        for (int i = 0; i < bytes.Length; i += sizeof(int))
        {
            DoIt((int*)(p + i));
        }
    }

    Console.ReadKey();
}

這樣就可以完全避免創建新的數組( 無法調整大小,甚至不能使用不安全的代碼!),只需將指針傳遞給數組即可讀取前三個整數的方法。

如果你的數組總是包含6個項目怎么樣:

var newarr1 = new []{oldarr[0], oldarr[1],oldarr[2]};
var newarr2 = new []{oldarr[3], oldarr[4],oldarr[5]};

從內存中讀取很快。

由於數組不是在C#中動態調整大小,這意味着您的第一個數組必須具有最小長度5或最大長度6,具體取決於您的實現。 然后,每次需要拆分時,您將不得不動態創建3個新的靜態大小的數組。 只有在每次拆分后,您的數組項才會按自然順序排列,除非您將每個新數組的長度設置為5或6,並且僅添加到最新數組。 這種方法意味着每個新陣列也會有2-3個額外的指針。

除非你在編譯應用程序之前有一個已知數量的項目進入你的數組,否則你還必須為動態創建的數組提供某種形式的持有者,這意味着你將不得不擁有一個數組數組(一個鋸齒狀的陣列)。 由於您的鋸齒狀數組也是靜態大小的,因此您需要能夠在實例化每個新動態創建的數組時動態重新創建和調整大小。

我想說將這些項目復制到新陣列中是您最不擔心的問題。 你正在尋找一些非常大的性能命中以及數組大小的增長。


更新:所以,如果這是我絕對需要...

public class MyArrayClass
{
    private int[][] _master = new int[10][];
    private int[] _current = new int[3];
    private int _currentCount, _masterCount;

    public void Add(int number)
    {
        _current[_currentCount] = number;
        _currentCount += 1;
        if (_currentCount == _current.Length)
        {
            Array.Copy(_current,0,_master[_masterCount],0,3);
            _currentCount = 0;
            _current = new int[3];
            _masterCount += 1;
            if (_masterCount == _master.Length)
            {
                int[][] newMaster = new int[_master.Length + 10][];
                Array.Copy(_master, 0, newMaster, 0, _master.Length);
                _master = newMaster;
            }
        }
    }

    public int[][] GetMyArray()
    {
        return _master;
    }

    public int[] GetMinorArray(int index)
    {
        return _master[index];
    }

    public int GetItem(int MasterIndex, int MinorIndex)
    {
        return _master[MasterIndex][MinorIndex];
    }
}

注意:這可能不是完美的代碼,這是一種實現方式的可怕方式,我絕不會在生產代碼中這樣做。

強制性LINQ解決方案:

if(arr1.Length > 5)
{
   var newArr = arr1.Skip(arr1.Length / 2).ToArray();
   arr1 = arr1.Take(arr1.Length / 2).ToArray();
}

LINQ比你想象的要快; 這將基本上受到框架在IEnumerable中旋轉的能力的限制(在陣列上非常快)。 這應該在大致線性的時間內執行,並且可以接受任何初始大小的arr1。

暫無
暫無

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

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