Круговой буфер в MATLAB, ** без ** копирования старых данных

Здесь есть несколько хороших сообщений (например, этот) на как сделать кольцевой буфер в MATLAB. Однако, глядя на них, я не верю, что они подходят моему приложению, потому что то, что я ищу, - это решение с круговым буфером в MATLAB, которое НЕ включает копирование старых данных.

В качестве простого примера допустим, что я обрабатываю 50 выборок за раз и читаю по 10 выборок на каждой итерации. Сначала я выполнял 5 итераций, заполнял буфер и, в конце концов, обрабатывал свои 50 образцов. Так что мой буфер будет

[B1 B2 B3 B4 B5]

, где каждая буква «B» представляет собой блок из 10 выборок.

Я прочитал следующие 10 образцов, назовите их B6. Я хочу, чтобы мой буфер теперь выглядел так:

[B2 B3 B4 B5 B6]

Загвоздка в том, что я НЕ хочу каждый раз копировать старые данные B2, B3, B4, B5, потому что со временем это становится дорого. (У меня очень большие наборы данных).

Мне интересно, есть ли способ сделать это без копирования «старых» данных. Спасибо.


person Spacey    schedule 02.01.2014    source источник


Ответы (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) возвращает3, mod(6,5) возвращает 1, mod(7,5) возвращает 2 и так далее, пока вы не дойдете до mod(10,5), что снова равно 0. Это позволяет нам «обернуть» вектор, возвращаясь к началу каждый раз, когда мы достигаем конца. +1 и -1 в коде связаны с тем, что MATLAB начинает свои векторные индексы с 1, а не с 0, поэтому, чтобы математика работала правильно, вам нужно удалить 1 перед выполнением mod, а затем добавить его обратно, чтобы получить правильный индекс. В результате, когда вы пытаетесь записать 6-й элемент в свой вектор, он идет и записывает его в 1-ю позицию в векторе.

person Steve    schedule 02.01.2014
comment
Спасибо, но я вообще не уверен, что следую вашему коду. Не могли бы вы расширить / пояснить его более подробно? Спасибо. - person Spacey; 02.01.2014
comment
@Learnaholic Конечно, я добавил немного пояснений по коду. Это помогает? - person Steve; 02.01.2014
comment
Разве это не похоже на мой подход, просто оставляя индексную переменную? Я имею в виду, что, как вы описали выше, записи также будут иметь неправильный порядок !? - person tim; 03.01.2014
comment
@bjoern После дальнейшего изучения вашего кода, приведенного ниже, они похожи. Однако команда mod может быть быстрее и компактнее. Кроме того, этот метод явно использует массив, а не массив ячеек. Это означает, что вам не нужно изменять существующий код или вызывать cell2mat или эквивалент --- что потребует нежелательной операции копирования. - person Steve; 03.01.2014
comment
Да, вы правы, он более компактный, но некоторые пользователи могут предпочесть более читаемую версию. Но это решение вопросов :-) Но: Может ли ваш код также обрабатывать кольцевой буфер, используя данные буфера, содержащие более одной записи? Или даже данные разной длины? Это был бы тот случай, когда массив ячеек мог бы быть более удобным. Но насколько я понимаю, у нас нет информации об этом от спрашивающего! - person tim; 03.01.2014
comment
@Steve Спасибо за разъяснения. Ваш пост очень хороший, но ... Мне нужно, чтобы данные были в правильном порядке, как показано в OP, например, [B1 B2 B3 B4 B5], затем [B2 B3 B4 B5 B6], затем [B3 B4 B5 B6 B7] и т. Д. Как добиться правильного заказа без копирования старых данных? (Я ценю ваш ответ и сохраню его, так как он может быть полезен для меня, если порядок не имеет значения). Спасибо. - person Spacey; 03.01.2014

Моя идея состояла бы в том, чтобы использовать массив ячеек с 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 - обрабатывать объекты

Вы можете использовать простой класс дескриптора:

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]

но, как уже говорилось, я не знаю, будет ли это быстрее из-за накладных расходов ООП. Это может быть в зависимости от ваших данных ... И вот еще немного информации об этом: http://www.matlabtips.com/how-to-point-at-in-matlab/

person tim    schedule 02.01.2014
comment
Умм, это бы навсегда увеличило размер, не так ли? Я не уверен, как это решает проблему ... как вы вообще можете получить доступ от {2} к {6} как к непрерывному вектору? - person Spacey; 02.01.2014
comment
Что вы имеете в виду под словом "навсегда"? - person tim; 02.01.2014
comment
Я так понимаю, он у вас здесь. - person Spacey; 02.01.2014
comment
Зачем расти? Вы просто перезаписываете существующие записи, используя индекс, относящийся к подмассиву a, как в решении, опубликованном Стивом, которое также использует предварительно выделенную переменную data, которая в цикле for частично перезаписывается на каждой итерации. - person tim; 03.01.2014
comment
@bjoern Этот код не работает как if(index < 5). Это должно быть if(index > 5) - person Steve; 03.01.2014
comment
bjoern, спасибо за код. К сожалению, я до сих пор не знаю, как это применимо. В конце концов, мне нужен вектор v = [B2 B3 B4 B5 B6]. Затем на следующей итерации мне нужно v = [B3 B4 B5 B5 B7] и т. Д. в этом порядке. Как это помогает достичь этого? Спасибо. - person Spacey; 03.01.2014
comment
Это то же самое, что и ответ Стива, он не копирует данные, но НЕ сохраняет порядок, что невозможно без копирования, как мне кажется. Как я уже говорил выше, я подумал, что, может быть, это все еще может быть полезно для вас, если вы все равно вызываете другую функцию со своими данными, вы можете просто переместить входные аргументы вызываемой функции. Но если это не так, и вам просто нужен буфер в виде упорядоченного списка значений, я чувствую, что вы ничего не можете сделать, вместо того, чтобы переупорядочить (таким образом, скопировав данные) :( То же самое относится и к другому ответу. К сожалению для меня: нет голосов за для того же подхода, что и ниже :( - person tim; 03.01.2014
comment
@bjoern Да, я думаю, нет реального способа без копирования данных и поддержания порядка. ›‹ - person Spacey; 03.01.2014
comment
@bjoern Я за тебя проголосовал. Кстати, это действительно ты на картинке ?? - person Spacey; 03.01.2014
comment
Было бы неплохо ;-) Итак, ответ на самом деле отрицательный ;-) - person tim; 03.01.2014

Я только что загрузил свое решение для быстрого кольцевого буфера, в который не копируются старые данные

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 НЕ зависит от размера буфера по сравнению с простым буфером копирования.

Двойная буферизация гарантирует прогнозируемое время для добавления в зависимости от данных, которые нужно добавить в любой ситуации. В будущем этот класс предоставит вам возможность выбора двойной буферизации: да или нет - работа ускорится, если вам не понадобится гарантированное время. введите описание изображения здесь

person Jens Henrik Göbbert    schedule 21.06.2014