【问题标题】:Fastest way to move unique array elements to the front and slice the array [closed]将唯一数组元素移动到前面并切片数组的最快方法[关闭]
【发布时间】:2018-02-18 15:52:47
【问题描述】:

我有一个函数,它获取一个指向大小为 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]

【问题讨论】:

  • 您能否提供一个或两个应该产生的输入和结果示例。 (特别是定义什么不是唯一元素)。
  • @Richard input: [1, 3, 3, 4] -> [1, 4, 3] 或 [1, 3, 4] (顺序无关紧要),输入 [2323, 4321, 11, 11] -> [2323, 4321, 11]
  • 总是四个元素?只是一堆ifs 怎么样?不会有那么多不同的输入...
  • @Haukinger 是的,总是 4。也许 ifs 带有某种 Sorting Network,针对长度为 4 的数组进行了优化

标签: c# arrays performance pointers unsafe


【解决方案1】:

这是我想出的(当问题是关于整数数组时,但我确信它可以修改为处理ReadOnlySpan&lt;int&gt;(不管是什么......))。
在我的电脑上测试,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();
}

【讨论】:

  • 在使用 ReadOnlySpan 优化您的答案后,我将执行时间缩短了两次并删除了不必要的分配
  • 很高兴为您提供帮助 :-)
  • 您能分享一下您是如何优化它的吗?
  • @321X ptp 已将他的最终解决方案作为answer 分享给他自己的问题。
  • 啊,我明白了!我的错!我不得不向下滚动... doh
【解决方案2】:

对于数组 [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)

...你明白了...只是在我的脑海中写下,所以最好对所有可能的输入进行单元测试:-)

【讨论】:

  • 几乎没问题,它没有得到所有情况 -> 示例:预期 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
【解决方案3】:

@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);
    }

【讨论】:

    猜你喜欢
    • 2013-12-21
    • 1970-01-01
    • 2014-03-06
    • 2021-05-15
    • 1970-01-01
    • 1970-01-01
    • 2015-07-27
    • 2011-01-23
    相关资源
    最近更新 更多