将唯一数组元素移动到前面并切割数组的最快方法

时间:2017-09-10 07:08:20

标签: c# arrays performance pointers unsafe

我有一个函数,它获取一个指向大小为4(总是)的数组的指针,并且必须只切片唯一元素并返回它们。这个函数每秒被调用100k次,这就是为什么它需要快速并且非常难以对GC施加压力(这就是为什么缓冲区变量是一个指针 - stackalloc)。

private const int BufferLength = 4;
private static unsafe ReadOnlySpan<int> SliceUnique(int* buffer)
{
   // TODO: move the unique elements to the front
   // and get the last unique index

   return new ReadOnlySpan<int>(buffer, lastUniqueIndex + 1);
}

// Example: 
// input: [1, 3, 2, 1]                      | [1, 4, 4, 4]
// output: [1, 3, 2] (order doesn't matter) | [1, 4]

3 个答案:

答案 0 :(得分:1)

对于数组[a,b,c,d]:

open(filename, 'a')

...你明白了......只是写在我的头顶,所以最好用所有可能的输入进行单元测试: - )

答案 1 :(得分:1)

以下是我提出的问题(当问题是关于整数数组的时候,但是我确定可以修改它以处理ReadOnlySpan<int>(无论是什么......)。登记/> 在我的计算机上测试,1,000,000个阵列的平均执行时间为195毫秒(包括创建阵列):

int[] RemoveDuplicates(int[] array)
{
    bool remove1 = array[1] == array[0];
    bool remove2 = array[2] == array[1] || array[2] == array[0];
    bool remove3 = array[3] == array[2] || array[3] == array[1] || array[3] == array[0];

    if (remove1 && remove2 && remove3)
        return new int[] { array[0] };
    if (remove1 && remove2)
        return new int[] { array[0], array[3] };
    if (remove1 && remove3)
        return new int[] { array[0], array[2] };
    if (remove2 && remove3)
        return new int[] { array[0], array[1] };
    if (remove1)
        return new int[] { array[0], array[2], array[3] };
    if (remove2)
        return new int[] { array[0], array[1], array[3] };
    if (remove3)
        return new int[] { array[0], array[1], array[2] };
    return new int[] { array[0], array[1], array[2], array[3] };
}

测试代码:

static void Main(string[] args)
{
    long sum = 0;
    var stopwatch = new System.Diagnostics.Stopwatch();
    var numberOfTimes = 1000 * 1000;
    for (int i = 0; i < 100; i++)
    {
        stopwatch.Restart();
        for (int j = 0; j < numberOfTimes; j++)
        {
            var a = new int[] { j % 2, j % 3, j % 4, j % 5 };
            var r = RemoveDuplicates(a);
        }
        stopwatch.Stop();
        sum += stopwatch.ElapsedMilliseconds;
        // Console.WriteLine(string.Format("{0} => {1}", string.Join(",", a), string.Join(",", r)));
    }
    double avg = sum / 100;
    Console.WriteLine("Average execution time (100 executions) is {0}", avg);
    Console.ReadKey();
}

答案 2 :(得分:0)

@ZoharPeled回答 - 使用ReadOnlySpan优化

    private static unsafe ReadOnlySpan<int> SliceUniqueZoharPeledOptimized(int* buffer)
    {
        bool remove1 = buffer[1] == buffer[0];
        bool remove2 = buffer[2] == buffer[1] || buffer[2] == buffer[0];
        bool remove3 = buffer[3] == buffer[2] || buffer[3] == buffer[1] || buffer[3] == buffer[0];

        if (remove1 && remove2 && remove3)
        {
            return new ReadOnlySpan<int>(buffer, 1);
        }

        if (remove1 && remove2)
        {
            buffer[1] = buffer[3];
            return new ReadOnlySpan<int>(buffer, 2);
        }

        if (remove1 && remove3)
        {
            buffer[1] = buffer[2];
            return new ReadOnlySpan<int>(buffer, 2);
        }

        if (remove2 && remove3)
        {
            return new ReadOnlySpan<int>(buffer, 2);
        }

        if (remove1)
        {
            buffer[1] = buffer[3];
            return new ReadOnlySpan<int>(buffer, 3);
        }

        if (remove2)
        {
            buffer[2] = buffer[3];
            return new ReadOnlySpan<int>(buffer, 3);
        }

        if (remove3)
        {
            return new ReadOnlySpan<int>(buffer, 3);
        }

        return new ReadOnlySpan<int>(buffer, 4);
    }