C# 将唯一阵列元素移动到前端并切片阵列的最快方法

C# 将唯一阵列元素移动到前端并切片阵列的最快方法,c#,arrays,performance,pointers,unsafe,C#,Arrays,Performance,Pointers,Unsafe,我有一个函数,它获取一个指向大小为4(始终)的数组的指针,并且必须只分割唯一的元素并返回它们。这个函数每秒被调用100k次,这就是为什么它需要快速,并且不需要对GC施加太大压力(这就是为什么缓冲区变量是指针-堆栈alloc) private const int BufferLength=4; 私有静态不安全ReadOnlySpan SliceUnique(int*buffer) { //TODO:将唯一元素移到前面 //并获取最后一个唯一索引 返回新的ReadOnlySpan(缓冲区,lastU

我有一个函数,它获取一个指向大小为4(始终)的数组的指针,并且必须只分割唯一的元素并返回它们。这个函数每秒被调用100k次,这就是为什么它需要快速,并且不需要对GC施加太大压力(这就是为什么缓冲区变量是指针-堆栈alloc)

private const int BufferLength=4;
私有静态不安全ReadOnlySpan SliceUnique(int*buffer)
{
//TODO:将唯一元素移到前面
//并获取最后一个唯一索引
返回新的ReadOnlySpan(缓冲区,lastUniqueIndex+1);
}
//例如:
//输入:[1,3,2,1]|[1,4,4,4]
//输出:[1,3,2](顺序无关紧要)|[1,4]

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

if (a==b)
    if (b==c)
        if (c==d)
            return (a,1)
        else
            return (a,d,2)
    else
        if (c==d)
            return (a,c,2)
        else
            if (a==d)
                return (a,c,2)
            else
                return (a,c,d,3)
else
    if (b==c)
        if (c==d)
            return (a,b,2)
        else
            if (a==d)
                return (a,b,2)
            else   
                return (a,b,d,3)
    else
        if (c==d)
            if (a==c)
                return (a,b,2)
            else
                return (a,b,c,3)
        else
            if (a==c)
                if (b==d)
                    return (a,b,2)
                else
                    return (a,b,d,3)
            else
                if (b==d)
                    return (a,b,c,3)
                else
                    if (a==d)
                        return (a,b,c,3)
                    else
                        return (a,b,c,d,4)

。。。你明白了。。。刚刚写下了我的想法,所以最好对所有可能的输入进行单元测试:-)

这是我的想法(当问题是关于int数组时,但我确信它可以修改为处理
ReadOnlySpan
(不管是什么…)
在我的计算机上测试,1000000个数组(包括创建数组)的平均执行时间为195毫秒:

测试代码:

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();
}
static void Main(字符串[]args)
{
长和=0;
var stopwatch=new System.Diagnostics.stopwatch();
变量次数=1000*1000;
对于(int i=0;i<100;i++)
{
stopwatch.Restart();
对于(int j=0;j{1}”、string.Join(“,”,a)、string.Join(“,”,r));
}
双平均值=总和/100;
WriteLine(“平均执行时间(100次执行)为{0}”,平均值);
Console.ReadKey();
}

@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);
    }
private static unsafe ReadOnlySpan SliceUniqueZoharPeledOptimized(int*buffer)
{
bool remove1=缓冲区[1]==缓冲区[0];
bool remove2=缓冲区[2]==缓冲区[1]| |缓冲区[2]==缓冲区[0];
bool remove3=缓冲区[3]==缓冲区[2]| |缓冲区[3]==缓冲区[1]| |缓冲区[3]==缓冲区[0];
如果(删除1和删除2和删除3)
{
返回新的ReadOnlySpan(缓冲区,1);
}
如果(删除1和删除2)
{
缓冲区[1]=缓冲区[3];
返回新的ReadOnlySpan(缓冲区,2);
}
如果(删除1和删除3)
{
缓冲区[1]=缓冲区[2];
返回新的ReadOnlySpan(缓冲区,2);
}
如果(移除2和移除3)
{
返回新的ReadOnlySpan(缓冲区,2);
}
如果(删除1)
{
缓冲区[1]=缓冲区[3];
返回新的ReadOnlySpan(缓冲区,3);
}
如果(删除2)
{
缓冲区[2]=缓冲区[3];
返回新的ReadOnlySpan(缓冲区,3);
}
如果(删除3)
{
返回新的ReadOnlySpan(缓冲区,3);
}
返回新的ReadOnlySpan(缓冲区,4);
}

您能否提供一两个应该产生的输入和结果示例。(特别是定义哪些元素不是唯一的)。@Richard input:[1,3,3,4]->[1,4,3]或[1,3,4](顺序不重要),input[2323234321,11,11]->[2323234321,11]始终是四个元素吗?如果只是一堆
if
s呢?不会有那么多不同的输入…@Haukinger yes总是4。也许如果对长度为4的数组进行某种优化,它不会得到所有情况->示例:预期3,结果:4,3_4_3_2预期2,结果:4,4_2_4_2预期3,结果:4,3_4_2_3预期3,结果:4,3_1_4_1预期3,结果:4,1_3_4_1预期3,结果:4,3_4_2_3预期3,结果:4,3_2_1_3预期3,结果:4,2_4_3_2在使用ReadOnlySpan选择您的答案后,我已将执行时间缩短为两个,并删除了不必要的分配很高兴提供帮助:-)您能分享一下您是如何优化它的吗?@321X ptp已经分享了他自己问题的最终解决方案。啊,是的,我明白了!我的错!我不得不向下滚动。。。doh
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();
}
    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);
    }