如何在C#中快速从一个ushort数组中减去另一个ushort数组?

如何在C#中快速从一个ushort数组中减去另一个ushort数组?,c#,arrays,performance,subtraction,ushort,C#,Arrays,Performance,Subtraction,Ushort,我需要从ushort arrayB中具有相同长度的对应索引值中快速减去ushort arrayA中的每个值 另外,如果差值为负,我需要存储一个零,而不是负差值 (确切地说,长度=327680,因为我正在从另一个相同大小的图像中减去一个640x512图像) 下面的代码目前需要~20ms,如果可能的话,我希望将其降低到~5ms以下。不安全代码是可以的,但请提供一个例子,因为我不太擅长编写不安全代码 谢谢大家! public ushort[] Buffer { get; set; } public

我需要从ushort arrayB中具有相同长度的对应索引值中快速减去ushort arrayA中的每个值

另外,如果差值为负,我需要存储一个零,而不是负差值

(确切地说,长度=327680,因为我正在从另一个相同大小的图像中减去一个640x512图像)

下面的代码目前需要~20ms,如果可能的话,我希望将其降低到~5ms以下。不安全代码是可以的,但请提供一个例子,因为我不太擅长编写不安全代码

谢谢大家!

public ushort[] Buffer { get; set; }

public void SubtractBackgroundFromBuffer(ushort[] backgroundBuffer)
{
    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    sw.Start();

    int bufferLength = Buffer.Length;

    for (int index = 0; index < bufferLength; index++)
    {
        int difference = Buffer[index] - backgroundBuffer[index];

        if (difference >= 0)
            Buffer[index] = (ushort)difference;
        else
            Buffer[index] = 0;
    }

    Debug.WriteLine("SubtractBackgroundFromBuffer(ms): " + sw.Elapsed.TotalMilliseconds.ToString("N2"));
}
您可以尝试:

更新:我已经尝试过了,我发现在你的情况下有一个最小的差异,但是当数组变大时,差异也会变大


在实际执行减法之前,您可以先检查结果是否为负数,从而获得较小的性能提升。这样,如果结果为负数,则无需执行减法运算。例如:

if (Buffer[index] > backgroundBuffer[index])
    Buffer[index] = (ushort)(Buffer[index] - backgroundBuffer[index]);
else
    Buffer[index] = 0;
一些基准

  • SubtractBackgroundFromBuffer:
    这是问题的原始方法
  • subtractbackgroundfromtbufferwithcalcopt:
    这是一种原始的方法,它补充了TTat提高计算速度的思想
  • SubtractBackgroundFromBufferParallelFor:
    从Selman22的答案中找出解决方案
  • SubtractBackgroundFromBufferBlockParallelFor:
    我的答案。与3类似,但将处理分解为4096个值的块
  • SubtractBackgroundFromBufferPartitionedParallelForEach:
    Geoff的第一个答案
  • SubtractBackgroundFromBufferPartitionedParallelForEachHack:
    Geoff的第二个答案
  • 更新

    public static void SubtractBackgroundFromBuffer(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            int difference = Buffer[index] - backgroundBuffer[index];
    
            if (difference >= 0)
                Buffer[index] = (ushort)difference;
            else
                Buffer[index] = 0;
        }
    }
    
    public static void SubtractBackgroundFromBufferWithCalcOpt(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            if (Buffer[index] < backgroundBuffer[index])
            {
                Buffer[index] = 0;
            }
            else
            {
                Buffer[index] -= backgroundBuffer[index];
            }
        }
    }
    
    public static void SubtractBackgroundFromBufferParallelFor(ushort[] backgroundBuffer)
    {
        Parallel.For(0, Buffer.Length, (i) =>
        {
            int difference = Buffer[i] - backgroundBuffer[i];
            if (difference >= 0)
                Buffer[i] = (ushort)difference;
            else
                Buffer[i] = 0;
        });
    }        
    
    public static void SubtractBackgroundFromBufferBlockParallelFor(ushort[] backgroundBuffer)
    {
        int blockSize = 4096;
    
        Parallel.For(0, (int)Math.Ceiling(Buffer.Length / (double)blockSize), (j) =>
        {
            for (int i = j * blockSize; i < (j + 1) * blockSize; i++)
            {
                int difference = Buffer[i] - backgroundBuffer[i];
    
                Buffer[i] = (ushort)Math.Max(difference, 0);                    
            }
        });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEach(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
            {
                for (int i = range.Item1; i < range.Item2; ++i)
                {
                    if (Buffer[i] < backgroundBuffer[i])
                    {
                        Buffer[i] = 0;
                    }
                    else
                    {
                        Buffer[i] -= backgroundBuffer[i];
                    }
                }
            });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEachHack(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
        {
            for (int i = range.Item1; i < range.Item2; ++i)
            {
                unsafe
                {
                    var nonNegative = Buffer[i] > backgroundBuffer[i];
                    Buffer[i] = (ushort)((Buffer[i] - backgroundBuffer[i]) *
                        *((int*)(&nonNegative)));
                }
            }
        });
    }
    
    有趣的是,我可以通过使用(如Bruno Costa所建议的)从BufferBlockParallelfor中减去BackgroundFromBufferBlockParallelfor来获得一个小的速度提升(~6%)

    而不是

    if (difference >= 0)
        Buffer[i] = (ushort)difference;
    else
        Buffer[i] = 0;
    
    结果

    public static void SubtractBackgroundFromBuffer(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            int difference = Buffer[index] - backgroundBuffer[index];
    
            if (difference >= 0)
                Buffer[index] = (ushort)difference;
            else
                Buffer[index] = 0;
        }
    }
    
    public static void SubtractBackgroundFromBufferWithCalcOpt(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            if (Buffer[index] < backgroundBuffer[index])
            {
                Buffer[index] = 0;
            }
            else
            {
                Buffer[index] -= backgroundBuffer[index];
            }
        }
    }
    
    public static void SubtractBackgroundFromBufferParallelFor(ushort[] backgroundBuffer)
    {
        Parallel.For(0, Buffer.Length, (i) =>
        {
            int difference = Buffer[i] - backgroundBuffer[i];
            if (difference >= 0)
                Buffer[i] = (ushort)difference;
            else
                Buffer[i] = 0;
        });
    }        
    
    public static void SubtractBackgroundFromBufferBlockParallelFor(ushort[] backgroundBuffer)
    {
        int blockSize = 4096;
    
        Parallel.For(0, (int)Math.Ceiling(Buffer.Length / (double)blockSize), (j) =>
        {
            for (int i = j * blockSize; i < (j + 1) * blockSize; i++)
            {
                int difference = Buffer[i] - backgroundBuffer[i];
    
                Buffer[i] = (ushort)Math.Max(difference, 0);                    
            }
        });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEach(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
            {
                for (int i = range.Item1; i < range.Item2; ++i)
                {
                    if (Buffer[i] < backgroundBuffer[i])
                    {
                        Buffer[i] = 0;
                    }
                    else
                    {
                        Buffer[i] -= backgroundBuffer[i];
                    }
                }
            });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEachHack(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
        {
            for (int i = range.Item1; i < range.Item2; ++i)
            {
                unsafe
                {
                    var nonNegative = Buffer[i] > backgroundBuffer[i];
                    Buffer[i] = (ushort)((Buffer[i] - backgroundBuffer[i]) *
                        *((int*)(&nonNegative)));
                }
            }
        });
    }
    
    请注意,这是每次运行中1000次迭代的总时间

    SubtractBackgroundFromBuffer(ms):                                 2,062.23 
    SubtractBackgroundFromBufferWithCalcOpt(ms):                      2,245.42
    SubtractBackgroundFromBufferParallelFor(ms):                      4,021.58
    SubtractBackgroundFromBufferBlockParallelFor(ms):                   769.74
    SubtractBackgroundFromBufferPartitionedParallelForEach(ms):         827.48
    SubtractBackgroundFromBufferPartitionedParallelForEachHack(ms):     539.60
    
    因此,从这些结果来看,最好的方法是将计算优化结合起来,以获得较小的增益,并利用
    Parallel.for
    对图像块进行操作。当然,您的里程数会有所不同,并行代码的性能对您运行的CPU非常敏感

    测试线束

    public static void SubtractBackgroundFromBuffer(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            int difference = Buffer[index] - backgroundBuffer[index];
    
            if (difference >= 0)
                Buffer[index] = (ushort)difference;
            else
                Buffer[index] = 0;
        }
    }
    
    public static void SubtractBackgroundFromBufferWithCalcOpt(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            if (Buffer[index] < backgroundBuffer[index])
            {
                Buffer[index] = 0;
            }
            else
            {
                Buffer[index] -= backgroundBuffer[index];
            }
        }
    }
    
    public static void SubtractBackgroundFromBufferParallelFor(ushort[] backgroundBuffer)
    {
        Parallel.For(0, Buffer.Length, (i) =>
        {
            int difference = Buffer[i] - backgroundBuffer[i];
            if (difference >= 0)
                Buffer[i] = (ushort)difference;
            else
                Buffer[i] = 0;
        });
    }        
    
    public static void SubtractBackgroundFromBufferBlockParallelFor(ushort[] backgroundBuffer)
    {
        int blockSize = 4096;
    
        Parallel.For(0, (int)Math.Ceiling(Buffer.Length / (double)blockSize), (j) =>
        {
            for (int i = j * blockSize; i < (j + 1) * blockSize; i++)
            {
                int difference = Buffer[i] - backgroundBuffer[i];
    
                Buffer[i] = (ushort)Math.Max(difference, 0);                    
            }
        });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEach(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
            {
                for (int i = range.Item1; i < range.Item2; ++i)
                {
                    if (Buffer[i] < backgroundBuffer[i])
                    {
                        Buffer[i] = 0;
                    }
                    else
                    {
                        Buffer[i] -= backgroundBuffer[i];
                    }
                }
            });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEachHack(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
        {
            for (int i = range.Item1; i < range.Item2; ++i)
            {
                unsafe
                {
                    var nonNegative = Buffer[i] > backgroundBuffer[i];
                    Buffer[i] = (ushort)((Buffer[i] - backgroundBuffer[i]) *
                        *((int*)(&nonNegative)));
                }
            }
        });
    }
    
    我在发布模式下为每个方法运行了这个。我以这种方式启动和停止秒表,以确保只测量处理时间

    System.Diagnostics.Stopwatch sw = new System.Diagnostics.Stopwatch();
    ushort[] bgImg = GenerateRandomBuffer(327680, 818687447);
    
    for (int i = 0; i < 1000; i++)
    {
        Buffer = GenerateRandomBuffer(327680, 128011992);                
    
        sw.Start();
        SubtractBackgroundFromBuffer(bgImg);
        sw.Stop();
    }
    
    Console.WriteLine("SubtractBackgroundFromBuffer(ms): " + sw.Elapsed.TotalMilliseconds.ToString("N2"));
    
    
    public static ushort[] GenerateRandomBuffer(int size, int randomSeed)
    {
        ushort[] buffer = new ushort[327680];
        Random random = new Random(randomSeed);
    
        for (int i = 0; i < size; i++)
        {
            buffer[i] = (ushort)random.Next(ushort.MinValue, ushort.MaxValue);
        }
    
        return buffer;
    }
    
    System.Diagnostics.Stopwatch sw=新的System.Diagnostics.Stopwatch();
    ushort[]bgImg=GenerateRandomBuffer(327680818687447);
    对于(int i=0;i<1000;i++)
    {
    Buffer=GenerateRandomBuffer(327680128011992);
    sw.Start();
    从缓冲区中减去背景(bgImg);
    sw.Stop();
    }
    Console.WriteLine(“SubtractBackgroundFromBuffer(ms):”+sw.appeased.total毫秒.ToString(“N2”);
    公共静态ushort[]GenerateRandomBuffer(int-size,int-randomSeed)
    {
    ushort[]buffer=新的ushort[327680];
    随机随机=新随机(随机种子);
    对于(int i=0;i
    方法

    public static void SubtractBackgroundFromBuffer(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            int difference = Buffer[index] - backgroundBuffer[index];
    
            if (difference >= 0)
                Buffer[index] = (ushort)difference;
            else
                Buffer[index] = 0;
        }
    }
    
    public static void SubtractBackgroundFromBufferWithCalcOpt(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            if (Buffer[index] < backgroundBuffer[index])
            {
                Buffer[index] = 0;
            }
            else
            {
                Buffer[index] -= backgroundBuffer[index];
            }
        }
    }
    
    public static void SubtractBackgroundFromBufferParallelFor(ushort[] backgroundBuffer)
    {
        Parallel.For(0, Buffer.Length, (i) =>
        {
            int difference = Buffer[i] - backgroundBuffer[i];
            if (difference >= 0)
                Buffer[i] = (ushort)difference;
            else
                Buffer[i] = 0;
        });
    }        
    
    public static void SubtractBackgroundFromBufferBlockParallelFor(ushort[] backgroundBuffer)
    {
        int blockSize = 4096;
    
        Parallel.For(0, (int)Math.Ceiling(Buffer.Length / (double)blockSize), (j) =>
        {
            for (int i = j * blockSize; i < (j + 1) * blockSize; i++)
            {
                int difference = Buffer[i] - backgroundBuffer[i];
    
                Buffer[i] = (ushort)Math.Max(difference, 0);                    
            }
        });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEach(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
            {
                for (int i = range.Item1; i < range.Item2; ++i)
                {
                    if (Buffer[i] < backgroundBuffer[i])
                    {
                        Buffer[i] = 0;
                    }
                    else
                    {
                        Buffer[i] -= backgroundBuffer[i];
                    }
                }
            });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEachHack(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
        {
            for (int i = range.Item1; i < range.Item2; ++i)
            {
                unsafe
                {
                    var nonNegative = Buffer[i] > backgroundBuffer[i];
                    Buffer[i] = (ushort)((Buffer[i] - backgroundBuffer[i]) *
                        *((int*)(&nonNegative)));
                }
            }
        });
    }
    
    publicstaticvoid减去backgroundfrombuffer(ushort[]backgroundBuffer)
    {
    int bufferLength=Buffer.Length;
    for(int index=0;index=0)
    缓冲区[索引]=(ushort)差异;
    其他的
    缓冲区[索引]=0;
    }
    }
    公共静态void减去backgroundfromBufferwithCalCopt(ushort[]backgroundBuffer)
    {
    int bufferLength=Buffer.Length;
    for(int index=0;index
    {
    int差分=缓冲区[i]-背景缓冲区[i];
    如果(差异>=0)
    缓冲区[i]=(ushort)差;
    其他的
    缓冲区[i]=0;
    });
    }        
    public static void SubtractBackgroundFromBufferBlockParallelFor(ushort[]backgroundBuffer)
    {
    int blockSize=4096;
    对于(0,(int)数学上限(Buffer.Length/(double)blockSize),(j)=>
    {
    对于(int i=j*blockSize;i<(j+1)*blockSize;i++)
    {
    int差分=缓冲区[i]-背景缓冲区[i];
    缓冲区[i]=(ushort)数学最大值(差,0);
    }
    });
    }
    公共静态void从BufferPartitionedParallelforeach中减去Background(ushort[]backgroundBuffer)
    {
    Parallel.ForEach(Partitioner.Create(0,Buffer.Length),range=>
    {
    对于(int i=range.Item1;i
    {
    对于(int i=range.Item1;i背景缓冲区[i];
    缓冲区[i]=(ushort)((缓冲区[i]-背景缓冲区[i])*
    *((int*)(&非负));
    }
    }
    });
    }
    
    这是一个有趣的问题

    只执行
    public static void SubtractBackgroundFromBuffer(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            int difference = Buffer[index] - backgroundBuffer[index];
    
            if (difference >= 0)
                Buffer[index] = (ushort)difference;
            else
                Buffer[index] = 0;
        }
    }
    
    public static void SubtractBackgroundFromBufferWithCalcOpt(ushort[] backgroundBuffer)
    {
        int bufferLength = Buffer.Length;
    
        for (int index = 0; index < bufferLength; index++)
        {
            if (Buffer[index] < backgroundBuffer[index])
            {
                Buffer[index] = 0;
            }
            else
            {
                Buffer[index] -= backgroundBuffer[index];
            }
        }
    }
    
    public static void SubtractBackgroundFromBufferParallelFor(ushort[] backgroundBuffer)
    {
        Parallel.For(0, Buffer.Length, (i) =>
        {
            int difference = Buffer[i] - backgroundBuffer[i];
            if (difference >= 0)
                Buffer[i] = (ushort)difference;
            else
                Buffer[i] = 0;
        });
    }        
    
    public static void SubtractBackgroundFromBufferBlockParallelFor(ushort[] backgroundBuffer)
    {
        int blockSize = 4096;
    
        Parallel.For(0, (int)Math.Ceiling(Buffer.Length / (double)blockSize), (j) =>
        {
            for (int i = j * blockSize; i < (j + 1) * blockSize; i++)
            {
                int difference = Buffer[i] - backgroundBuffer[i];
    
                Buffer[i] = (ushort)Math.Max(difference, 0);                    
            }
        });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEach(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
            {
                for (int i = range.Item1; i < range.Item2; ++i)
                {
                    if (Buffer[i] < backgroundBuffer[i])
                    {
                        Buffer[i] = 0;
                    }
                    else
                    {
                        Buffer[i] -= backgroundBuffer[i];
                    }
                }
            });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEachHack(ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
        {
            for (int i = range.Item1; i < range.Item2; ++i)
            {
                unsafe
                {
                    var nonNegative = Buffer[i] > backgroundBuffer[i];
                    Buffer[i] = (ushort)((Buffer[i] - backgroundBuffer[i]) *
                        *((int*)(&nonNegative)));
                }
            }
        });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEach(
        ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
            {
                for (int i = range.Item1; i < range.Item2; ++i)
                {
                    if (Buffer[i] < backgroundBuffer[i])
                    {
                        Buffer[i] = 0;
                    }
                    else
                    {
                        Buffer[i] -= backgroundBuffer[i];
                    }
                }
            });
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEachHack(
        ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
            {
                for (int i = range.Item1; i < range.Item2; ++i)
                {
                    unsafe
                    {
                        var nonNegative = Buffer[i] > backgroundBuffer[i];
                        Buffer[i] = (ushort)((Buffer[i] - backgroundBuffer[i]) *
                            *((int*)(&nonNegative)));
                    }
                }
            });
    }
    
    UInt16 MyCppLib::Maths::SafeSubtraction(UInt16 minuend, UInt16 subtrahend)
    {
        return (UInt16)((minuend - subtrahend) * (minuend > subtrahend));
    }
    
    public static void SubtractBackgroundFromBufferPartitionedParallelForEachCpp(
        ushort[] backgroundBuffer)
    {
        Parallel.ForEach(Partitioner.Create(0, Buffer.Length), range =>
        {
            for (int i = range.Item1; i < range.Item2; ++i)
            {
                Buffer[i] = 
                    MyCppLib.Maths.SafeSubtraction(Buffer[i], backgroundBuffer[i]);
            }
        });
    }
    
    1. SubtractBackgroundFromBuffer(ms):                               2,021.37
    2. SubtractBackgroundFromBufferWithCalcOpt(ms):                    2,125.80
    3. SubtractBackgroundFromBufferParallelFor(ms):                    3,431.58
    4. SubtractBackgroundFromBufferBlockParallelFor(ms):               1,401.36
    5. SubtractBackgroundFromBufferPartitionedParallelForEach(ms):     1,197.76
    6. SubtractBackgroundFromBufferPartitionedParallelForEachHack(ms):   742.72
    7. SubtractBackgroundFromBufferPartitionedParallelForEachCpp(ms):    499.27
    
    1. SubtractBackgroundFromBuffer(ms):                                 773.50
    2. SubtractBackgroundFromBufferWithCalcOpt(ms):                      915.91
    3. SubtractBackgroundFromBufferParallelFor(ms):                    2,458.36
    4. SubtractBackgroundFromBufferBlockParallelFor(ms):                 663.76
    5. SubtractBackgroundFromBufferPartitionedParallelForEach(ms):       658.05
    6. SubtractBackgroundFromBufferPartitionedParallelForEachHack(ms):   762.11
    7. SubtractBackgroundFromBufferPartitionedParallelForEachCpp(ms):    494.12
    
    Buffer = Buffer.Zip<ushort, ushort, ushort>(backgroundBuffer, (x, y) =>
    {
        return (ushort)Math.Max(0, x - y);
    }).ToArray();
    
    Enumerable.Range(0, Buffer.Length).AsParalell().ForAll(i =>
        {
             unsafe
            {
                var nonNegative = Buffer[i] > backgroundBuffer[i];
                Buffer[i] = (ushort)((Buffer[i] - backgroundBuffer[i]) *
                    *((int*)(&nonNegative)));
            }
        });