清除數組數據的正確姿勢

因爲某段程序的需要,我需要將一個long數組,不斷地填充數據,然後用完了之後又要清空裏面的數據,以便再次填充。由於調用及其頻繁,所以我很在意清除數據的性能。

測試代碼

以下程序都是基於下面的測試代碼完成:

using System;
using System.Diagnostics;
using System.Numerics;

namespace FillTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var array = new long[2515];
            var Empty = new long[2515];

            Do(() =>
            {
                Fill(array, 0L);
            }
            , "Fill");

            Console.ReadLine();
        }

        static void Fill(long[] array,long value)
        {
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = value;
            }
        }

        static void Do(Action action,string name)
        {
            var wathch = Stopwatch.StartNew();
            for (int i = 0; i < 1000000; i++)
            {
                action();
            }
            
            wathch.Stop();
            Console.WriteLine(name + " 耗時:" + wathch.Elapsed.ToString());
        }
    }
}

通常的,最簡單的辦法就像上面的代碼那樣,來個循環就可以了。

在我的機器中(i5 [email protected] 、16GB、 Release、 .net core 2、 windows 10),Fill版本消耗1.3秒。

雙指令

我不確認此代碼執行時是否使用了SIMD指令,所以我又編寫了一個版本:

        static void Fill2(long[] array,long value)
        {
            var i = 0;
            var end = array.Length - 1;
            while (i < end)
            {
                array[i] = value;
                array[i + 1] = value;
                i += 2;
            }

            if((array.Length % 2) == 1){
                array[array.Length - 1] = value;
            }
        }

我居然發現,僅僅消耗了1.0秒,難道.net默認不會優化這個代碼?

Array.Fill

有人會問,你爲什麼不調用Array自帶的Fill方法呢?我嘗試調用了,遺憾的是,一樣是1.3秒,和我的第一個版本系統。看樣子寫核心代碼的人偷懶了。哈哈

CopyTo

網上其實能夠查到一些討巧的手法,例如使用一個靜態的數組,內部都是0,當需要填充某個數據時,將這個靜態數組複製到你的數組一樣起到賦值的作用。例如:

Empty.CopyTo(array, 0);

這個版本竟然直接達到 0.56秒,幾乎翻兩倍,的確是個好辦法。

SIMD

在我認爲CopyTo是最快的方法時,我突然感覺我的第二個實現,是不是沒有真正用到SIMD,因爲我的印象中,SIMD是非常非常快的,應該不止上升那麼一點點。

所以我搬出了Vector對象,我是這麼幹的。

        static void Fill3(long[] array,long value)
        {
            Vector<long> v = new Vector<long>(value);
            var i = 0;
            var end = array.Length - 3;

            while (i < end)
            {
                v.CopyTo(array, i);
                i += 4;
            }

            if ((array.Length % 4) == 3)
            {
                array[array.Length - 3] = value;
                array[array.Length - 2] = value;
                array[array.Length - 1] = value;
            }else if ((array.Length % 4) == 2)
            {
                array[array.Length - 2] = value;
                array[array.Length - 1] = value;
            }
            else if ((array.Length % 4) == 1)
            {
                array[array.Length - 1] = value;
            }
        }

速度提高到 0.36秒。

所以性能無界限,有沒有更快的辦法呢?

後續

一些其他的嘗試,但都不理想。

        static unsafe void Fill4(long[] array, long value)
        {
            fixed (Int64* destinationBase = array)
            {
                for (int g = 0; g < array.Length; g++)
                {
                    destinationBase[g] = value;
                }
            }
        }//1.2秒

        static void Fill5(long[] array, long value)
        {
            Vector<long> v = new Vector<long>(value);
            var i = 0;
            var end = array.Length - 3;

            while (i < end)
            {
                array[i + 3] = array[i + 2] =array[i + 1] = array[i] = value;
                //array[i+1] = value;
                //array[i+2] = value;
                //array[i+3] = value;

                i += 4;
            }

            if ((array.Length % 4) == 3)
            {
                array[array.Length - 3] = value;
                array[array.Length - 2] = value;
                array[array.Length - 1] = value;
            }
            else if ((array.Length % 4) == 2)
            {
                array[array.Length - 2] = value;
                array[array.Length - 1] = value;
            }
            else if ((array.Length % 4) == 1)
            {
                array[array.Length - 1] = value;
            }
        } //0.8秒

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章