【问题标题】:Circular buffer in MATLAB, **without** copying old dataMATLAB 中的循环缓冲区,**无需**复制旧数据
【发布时间】:2014-01-20 07:47:41
【问题描述】:

这里有一些关于如何在 MATLAB 中制作循环缓冲区的好帖子(例如 this one)。但是从查看它们来看,我认为它们不适合我的应用程序,因为我正在寻找的是 MATLAB 中的循环缓冲区解决方案,它不涉及任何旧数据的复制。

举个简单的例子,假设我一次处理 50 个样本,每次迭代读取 10 个样本。我将首先运行 5 次迭代,填满我的缓冲区,最后处理我的 50 个样本。所以我的缓冲区将是

[B1 B2 B3 B4 B5]

,其中每个“B”是一个包含 10 个样本的块。

现在,我阅读了接下来的 10 个样本,将它们称为 B6。我希望我的缓冲区现在看起来像:

[B2 B3 B4 B5 B6]

问题是 - 我不想每次都复制旧数据 B2、B3、B4、B5,因为这样会变得很耗时。 (我有非常大的数据集)。

我想知道是否有一种方法可以在不复制任何“旧”数据的情况下做到这一点。谢谢。

【问题讨论】:

    标签: matlab optimization buffer circular-buffer processing-efficiency


    【解决方案1】:

    我刚刚上传了一个快速循环缓冲区的解决方案,它不会复制旧数据

    http://www.mathworks.com/matlabcentral/fileexchange/47025-circvbuf-m

    这个循环缓冲区的主要思想是恒定和快速的性能 并在程序中使用缓冲区时避免复制操作:

    % create a circular vector buffer
        bufferSz = 1000;
        vectorLen= 7;
        cvbuf = circVBuf(int64(bufferSz),int64(vectorLen));
    
    % fill buffer with 99 vectors
        vecs = zeros(99,vectorLen,'double');
        cvbuf.append(vecs);
    
    % loop over lastly appended vectors of the circVBuf:
        new = cvbuf.new;
        lst = cvbuf.lst;
        for ix=new:lst
           vec(:) = cvbuf.raw(:,ix);
        end
    
    % or direct array operation on lastly appended vectors in the buffer (no copy => fast)
        new = cvbuf.new;
        lst = cvbuf.lst;
        mean = mean(cvbuf.raw(3:7,new:lst));
    

    查看截图,这个循环缓冲区在缓冲区大的情况下有优势,但每次追加的数据量很小,因为与简单的副本相比,circVBuf 的性能不依赖于缓冲区大小缓冲区。

    双缓冲根据在任何情况下要追加的数据来保证追加的预测时间。将来,本课程将为您提供双缓冲是或否的选择 - 如果您不需要保证时间,事情将会加速。

    【讨论】:

      【解决方案2】:

      我的想法是使用一个包含 5 个条目的单元格数组,并使用一个变量来索引应该在下一步中覆盖的子数组。例如。像

       a = {ones(10),2*ones(10),3*ones(10),4*ones(10),5*ones(10)};
       index = 1;
      

      在下一步中,您可以写入子数组:

       a{index} = 6*ones(10);
      

      并像

      一样增加索引
      index = index+1
      

      显然,某种限制:

      if(index > 5) % FIXED TYPO!!
         index = 1;
      end
      

      这适合你吗?

      编辑: 还有一件事要考虑的是条目的排序,因此这些条目总是会被一些条目移动,但取决于您继续使用数据的方式,您可以例如根据变量index改变数据的使用。

      EDIT2:我有另一个想法:在 MATLAB 中使用类怎么样。您可以使用句柄类来保存数据,从而使用缓冲区仅引用数据。这可能会使其更快一些,具体取决于您持有的数据(数据集有多大等)以及您的代码中有多少班次。参见例如这里:Matlab -- handle objects

      你可以使用一个简单的句柄类:

      classdef Foo < handle   
          properties (SetAccess = public, GetAccess = public)
              x
          end
      
          methods
              function obj = foo(x)
                  % constructor
                  obj.x = x;
              end 
          end       
      end
      

      将数据存储在其中:

      data = [1 2 3 4];
      foo = Foo(data);  % handle object
      

      然后只将对象引用存储在循环缓冲区中。在发布的链接中,答案显示分配 bar = foo 不会复制对象,但实际上只保留了引用:

      foo.x = [3 4]
      disp(bar.x)      % would be [3 4]
      

      但如上所述,由于 OOP 开销,我不知道这是否会更快。这可能取决于您的数据......这里有一些关于它的更多信息:http://www.matlabtips.com/how-to-point-at-in-matlab/

      【讨论】:

      • 嗯,这会让“a”永远变大,对吧?我不确定这如何解决问题...您如何将 a{2} 访问为 {6} 作为连续向量?
      • 永远是什么意思?
      • "a" 会以这种方式继续增长,我知道你在这里拥有它。
      • 为什么要增长?您只需使用引用 a 子数组的索引来覆盖现有条目,就像在 Steve 发布的解决方案中一样,该解决方案还使用预分配变量 data,在 for 循环中,该变量被部分覆盖每次迭代。
      • @bjoern 此代码不能用作if(index &lt; 5)。必须是if(index &gt; 5)
      【解决方案3】:

      快速实现循环缓冲区的一种方法是使用模数绕回前面。这将稍微修改您指定的数据顺序,但如果您只需将最旧的数据替换为最新的数据,而不是

      [B2 B3 B4 B5 B6]
      

      你得到

      [B6 B2 B3 B4 B5]
      

      通过使用这样的代码:

      bufferSize = 5;
      
      data = nan(bufferSize,1)';
      
      for ind = 1:bufferSize+2  
      
          data(mod(ind-1, bufferSize)+1) = ind
      
      end
      

      这适用于任意大小的数据。

      如果您不熟悉取模,mod 函数会有效地返回除法运算的余数。所以mod(3,5)返回3mod(6,5)返回1mod(7,5)返回2等等,直到你到达mod(10,5)再次等于0。这允许我们通过每次到达终点时回到起点来“环绕”向量。代码中的 +1-1 是因为 MATLAB 从 1 而不是 0 开始其向量索引,因此要正确计算数学,您必须在执行 mod 之前删除 1 然后将其重新添加以获取正确的索引。结果是,当您尝试将第 6 个元素写入向量时,它会将其写入向量中的第一个位置。

      【讨论】:

      • 谢谢,但我不确定我是否完全遵循您的代码。您能否更详细地扩展/解释它?谢谢。
      • @Learnaholic 当然,我在代码上添加了一些解释。这有帮助吗?
      • 是不是有点类似于我的方法只是省略了索引变量?我的意思是如上所述,条目的顺序也会错误!?
      • @bjoern 在进一步检查下面的代码之后,它们是相似的。不过,mod 命令可能更快且更紧凑。此外,此方法显式使用数组而不是元胞数组。这意味着您不必修改现有代码或调用 cell2mat 或等效代码——这将需要不需要的复制操作。
      • 是的,你是对的,它更紧凑,但一些用户可能更喜欢可读性更高的版本。但这就是问题的决定:-) 但是:您的代码是否也可以使用包含多个条目的缓冲区数据来处理循环缓冲区?甚至是不同长度的数据?当单元阵列可能更方便时就是这种情况。但据我所知,我们没有从提问者那里得到任何信息!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-11
      相关资源
      最近更新 更多