【问题标题】:Improving performance of for loop when decimating a buffer抽取缓冲区时提高 for 循环的性能
【发布时间】:2018-10-06 21:12:37
【问题描述】:

我正在使用信号处理库从硬件设备收集 16384 个双精度值。我收到的 16384 个值是低通滤波器的输出。我想将样本降低 10 倍。换句话说,我想在每 10 分中保留 1 分。所以通常它的工作原理是这样的:

  1. 当低通滤波器完成时,我从我的库中获得一个缓冲区。

  2. 然后我收集其中的 10 个缓冲区。

  3. 当所有 10 个缓冲区都收集到一个具有 10*16384 双倍的缓冲区中时,我会从缓冲区中循环收集每 10 个双倍。结果是一个缓冲区有 16384 个双精度数。这将用于其余的数据处理。

代码如下:

double[] rawData = new double[163840];
int bufferCount = 0;

private void genericReal2_ProcessData(object Sender, Mitov.SignalLab.ProcessRealNotifyArgs Args)
{
    var realbuffer = Args.InBuffer; //get the buffer of processed doubles

    var myData = realbuffer.ToArray(); //must be converted to an array since the data type is not quite an array of doubles.

    Array.Copy(myData, 0, rawData, bufferCount * 16384, 16384);

    bufferCount++;

    if (bufferCount == 10)
    {
        bufferCount = 0;

        int j = 0;

        for (int i = 0; i < 163840; i += 10) //this loop takes 20ms
        {
            realbuffer[j] = rawData[i];                   
            j++;
        }

        genericReal2.SendData(realbuffer); //send data off for further processing
    }
}

for 循环运行大约需要 20 毫秒,而其他所有循环大约需要 20 微秒。
那么,有没有什么方法可以在不使用 for 循环的情况下提高它的整体性能?

更新************************** 我已经确定循环中的所有处理时间都用于将 realbuffer 分配给 rawData。所以我将其更改如下:

    private void genericReal2_ProcessData(object Sender, Mitov.SignalLab.ProcessRealNotifyArgs Args)
    {
        double[] finalBuffer = new double[16384];

        var realbuffer = Args.InBuffer; //get the buffer of processed doubles

        var myData = realbuffer.ToArray(); //must be converted to an array since the data type is not quite an array of doubles.

        Array.Copy(myData, 0, rawData, bufferCount * 16384, 16384);

        bufferCount++;

        if (bufferCount == 10)
        {
            bufferCount = 0;

            int j = 0;

            for (int i = 0; i < 163840; i += 10)
            {
                finalBuffer[j] = rawData[i];
                j++;
            }

             var pointer= realbuffer.Read();
             //I can get a pointer to the realbuffer.
             //It stores 8 bytes for every double value
             how can I copy 16384 8 byte values from finalbuffer to realbuffer?


            genericReal2.SendData(realbuffer); //send data off for further processing
        }

【问题讨论】:

  • Mitov.SignalLab.ProcessRealNotifyArgs.InBuffer[j] = 可能很慢。如果它有一些FromArray 功能,它可能会更快,但我真的不知道它是否有。无论哪种方式,问题都不在这段代码中。
  • 您可以循环单个块而不是一次循环所有块。这将有助于稍微中断处理。或者.. 如果不需要 double 的大小,那么在 32 位环境中更改为浮点数会加快速度。另一种选择可能是 Parallel.For()

标签: c# performance for-loop


【解决方案1】:

解决了。我使用的库有一个函数,它会将 double[] 中的所有值分配给它的内部实际缓冲区

 realbuffer.Equals(finalBuffer);

这需要 50us... 感谢大家的帮助。

【讨论】:

    【解决方案2】:

    您实际上得到了所有东西,然后丢弃了不必要的东西。在第一步不复制的情况下删除那些怎么样。我的意思是,而不是使用 Array.Copy 只是复制所需的;

    int j = bufferCount * 16384;
    for (int i = 0; i < 16384; i += 10)
    {
        realbuffer[j++] = rawData[i];
    }
    

    通过这样做,您将不需要在 if 语句中运行循环。

    如果由于任何情况而无法更改代码的整体结构,则可以通过排序算法中使用的技术来提高循环性能。使用大小 163840 + 1 定义您的数组。为数组的最后一个位置分配一些值,例如 -1,它不会在接收到的数据内。并将循环更改如下,以减少循环执行中的比较次数;

    for (int i = 0; i < rawData[i+=10 ] !=-1; ) 
    {
        realbuffer[j] = rawData[i];
        j++;
    }
    

    希望对你有帮助。

    【讨论】:

    • 你的第一个语句不起作用,因为 16384 不能被 10 整除。我会尝试第二个。
    • 您可以使用数组中的填充元素使其可被 10 整除并检查性能。它为某些算法节省了大量的时间。您可以评估它是否值得。
    • 我试图从你的第二个陈述中弄清楚。无效。
    【解决方案3】:

    您可以尝试将每个缓冲区放入字典并在 for each 中处理它们。理论上这应该更快,因为数组将被并行处理。 20ms 已经够快了,为什么需要这么快?

    var buffers = new Dictionary<int, double[]>();
    

    然后像这样处理:

    var myData = realbuffer.ToArray();
    
    buffers.Add(bufferCount, myData);
    
    if (bufferCount == 10)
    {
       Parallel.ForEach(buffers, (buffer) =>
       {
          //process buffer independently
       });
    }
    

    【讨论】:

    • "20ms 非常快" - 因为任务不是这样。 “为什么需要这样的速度?” - 因为……需要?字典在这里有点矫枉过正,为什么要在适合数组的地方添加如此复杂的结构?并行 foreach 的开销会使该解决方案比顺序处理慢。第一次运行慢了大约 300 倍,所有其他运行大约慢了 3 倍(时间非常不稳定,但它从来没有比顺序更快)。顺便说一句,ForEach 中的偏移量计算真的很尴尬。
    • 我有一台快速的开发机器,但有很多用户可能没有。所以 20ms 可能会变成 40ms,以此类推。生成的数组被发送到快速傅立叶变换并最终绘制出来。帧速率将不再是实时的。
    • @AntonínLejsek - 字典是一个复杂的对象(哈哈),我并没有说它会更快 - 我认为值得一试。偏移很困难 - 不是真的......
    • 字典有十个变量来保持他的内部状态,其中包括一个整数数组、一个自定义结构数组和两个其他集合。与数组相比,它在另一个层次上的复杂性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-18
    • 1970-01-01
    • 2018-11-08
    • 1970-01-01
    • 2022-08-13
    • 1970-01-01
    相关资源
    最近更新 更多