2012-01-18 25 views
6

Tôi đang phát triển một khách hàng sẽ nhận dữ liệu [EEG] trên tcp và ghi nó vào bộ đệm vòng. Tôi nghĩ rằng nó có thể rất thuận tiện để có bộ đệm như một ctypes hoặc mảng numpy bởi vì nó có thể tạo ra một 'xem' numpy đến bất kỳ vị trí của bộ đệm như vậy và đọc/ghi/xử lý dữ liệu mà không cần bất kỳ hoạt động sao chép. Hoặc là một ý tưởng tồi nói chung?bộ đệm vòng với các cụm/sê-ri

Tuy nhiên, tôi không thấy cách triển khai bộ đệm tròn có kích thước cố định theo cách này. Giả sử tôi đã tạo ra một đối tượng đệm tiếp giáp trong bộ nhớ. Cách tốt nhất để ghi dữ liệu khi kết thúc bộ đệm là gì?

Một cách có thể là bắt đầu ghi đè các byte (đã cũ) từ đầu khi con trỏ ghi đến phần cuối của mảng đệm. Tuy nhiên, gần ranh giới, khung nhìn gọn gàng của một số đoạn (để xử lý) không thể được tạo ra (hoặc có thể?) Trong trường hợp này, bởi vì một số vẫn có thể nằm ở cuối mảng đệm trong khi một phần khác đã có trong bắt đầu của nó. Tôi đã đọc không thể tạo ra những lát tròn như vậy. Làm thế nào để giải quyết điều này?

UPD: Cảm ơn mọi người vì câu trả lời. Trong trường hợp ai đó cũng phải đối mặt với cùng một vấn đề, here của mã cuối cùng tôi đã có.

+1

Tôi đã viết gói ['numpy_ringbuffer'] (https://pypi.python.org/pypi/numpy_ringbuffer) để giải quyết vấn đề này, cung cấp giao diện giống như 'deque' cho bộ đệm cơ bản – Eric

Trả lời

4

Nếu bạn cần cửa sổ N byte, hãy tạo bộ đệm 2 * N byte và ghi tất cả đầu vào vào hai vị trí: i % Ni % N + N, trong đó i là bộ đếm byte. Bằng cách đó bạn luôn có N byte liên tiếp trong bộ đệm.

data = 'Data to buffer' 
N = 4 
buf = 2*N*['\00'] 

for i,c in enumerate(data): 
    j = i % N 
    buf[j] = c 
    buf[j+N] = c 
    if i >= N-1: 
     print ''.join(buf[j+1:j+N+1]) 

in

Data 
ata 
ta t 
a to 
to 
to b 
o bu 
buf 
buff 
uffe 
ffer 
+0

yepp. Đây là những gì tôi đang cố gắng viết ngay bây giờ. Thay vì bộ đệm 2 * N, tôi có một chiều dài tùy ý + N và theo cùng một ý tưởng. Dù sao cũng cảm ơn bạn! – dmytro

+0

Điều này là tốt nếu hiệu suất không phải là một mối quan tâm ở tất cả; nhưng tôi nghi ngờ nó đã đưa ra ứng dụng của bạn. Có lẽ bạn nên sử dụng giải pháp vectorized cho vấn đề của mình –

2

Một cách có thể là bắt đầu ghi đè byte (đã cũ) từ đầu khi con trỏ ghi đến cuối mảng đệm.

Đó là tùy chọn duy nhất trong bộ đệm vòng có kích thước cố định.

Tôi đã đọc không thể tạo các lát tròn như vậy.

Đó là lý do tại sao tôi sẽ không thực hiện việc này với chế độ xem Tổng quan. Bạn có thể tạo một wrapper class xung quanh một ndarray thay vào đó, giữ bộ đệm/mảng, công suất và con trỏ (chỉ mục) đến điểm chèn. Nếu bạn muốn nhận được các nội dung như là một mảng NumPy, bạn sẽ phải tạo một bản sao như vậy:

buf = np.array([1,2,3,4]) 
indices = [3,0,1,2] 
contents = buf[indices] # copy 

Bạn vẫn có thể thiết lập giá trị các yếu tố tại chỗ nếu bạn thực hiện __setitem____setslice__.

+0

cảm ơn. Tuy nhiên, nếu đoạn đó phải được sao chép, thì tốt hơn không nên sử dụng collections.deque làm bộ đệm thay vào đó và sau đó thực hiện 'numpy.array (danh sách (itertools.islice (buf, chstart, chend)))' ? Hay chậm hơn nhiều? – dmytro

+0

Tôi muốn tránh sao chép vì làm cửa sổ trượt FFT trên dữ liệu đó sẽ có nghĩa là sao chép gần như cùng một đoạn dữ liệu mỗi lần một datapoint mới đến – dmytro

+0

@dmytro: bạn sẽ phải đo lường liệu 'deque' có nhanh hơn không. Tôi e rằng sẽ không dễ để tránh sao chép nếu bạn muốn lấy dữ liệu được lưu trữ trong bộ đệm vòng vào một mảng. –

-1

Một biến thể của câu trả lời @Janne Karila của, cho C nhưng không NumPy:
Nếu bộ đệm vòng là rất rộng, như N x 1G, sau đó thay vì tăng gấp đôi toàn bộ điều, tăng gấp đôi một mảng gồm 2 * N con trỏ tới các hàng của nó. Ví dụ: cho N = 3, khởi

bufp = { buf[0], buf[1], buf[2], buf[0], buf[1], buf[2] }; 

Sau đó, bạn ghi dữ liệu một lần duy nhất, và anyfunc(bufp[j:j+3]) thấy các hàng trong buf theo thứ tự thời gian.

2

Tôi nghĩ bạn cần phải lùi lại một bước từ tư duy kiểu C tại đây. Cập nhật ringbuffer cho mỗi lần chèn đơn sẽ không bao giờ hiệu quả. Một bộ đệm vòng về cơ bản là khác với giao diện khối bộ nhớ tiếp giáp mà nhu cầu mảng nhiều; bao gồm cả fft bạn đề cập bạn muốn làm.

Một giải pháp tự nhiên là hy sinh một chút bộ nhớ vì lợi ích của hiệu suất. Ví dụ, nếu số phần tử bạn cần giữ trong bộ đệm của bạn là N, hãy cấp phát một mảng N + 1024 (hoặc một số số hợp lý). Sau đó, bạn chỉ cần di chuyển các phần tử N xung quanh mỗi lần chèn 1024 và bạn luôn có chế độ xem liền kề của các phần tử N để hoạt động khi có sẵn trực tiếp.

EDIT: đây là đoạn mã thực hiện ở trên và phải mang lại hiệu suất tốt. Lưu ý rằng, bạn sẽ được khuyên nên nối thêm vào các khối, thay vì cho mỗi phần tử. Nếu không, lợi thế hiệu suất của việc sử dụng numpy sẽ nhanh chóng bị vô hiệu hóa, bất kể cách bạn triển khai ringbuffer.

import numpy as np 

class RingBuffer(object): 
    def __init__(self, size, padding=None): 
     self.size = size 
     self.padding = size if padding is None else padding 
     self.buffer = np.zeros(self.size+self.padding) 
     self.counter = 0 

    def append(self, data): 
     """this is an O(n) operation""" 
     data = data[-self.padding:] 
     n = len(data) 
     if self.remaining < n: self.compact() 
     self.buffer[self.counter+self.size:][:n] = data 
     self.counter += n 

    @property 
    def remaining(self): 
     return self.padding-self.counter 
    @property 
    def view(self): 
     """this is always an O(1) operation""" 
     return self.buffer[self.counter:][:self.size] 
    def compact(self): 
     """ 
     note: only when this function is called, is an O(size) performance hit incurred, 
     and this cost is amortized over the whole padding space 
     """ 
     print 'compacting' 
     self.buffer[:self.size] = self.view 
     self.counter = 0 

rb = RingBuffer(10) 
for i in range(4): 
    rb.append([1,2,3]) 
    print rb.view 

rb.append(np.arange(15)) 
print rb.view #test overflow 
+0

Cảm ơn Eelco. Vì vậy, một loạt các khung nhìn đơn giản là không hoạt động? Nhưng làm thế nào là phát hiện, và toàn bộ Aptr thay thế bằng một bản sao phẳng? Aptr [0] .flags có OWNDATA False, khó hiểu. – denis

+0

Tôi không chắc chắn ý bạn là gì bởi một loạt các khung nhìn; các khung nhìn rất dễ xây dựng khi đang di chuyển, vì vậy bạn không cần phải giữ chúng xung quanh trong một mảng. Điểm mấu chốt của vấn đề là, nếu bạn tìm cách sử dụng dữ liệu của bạn như một mảng numpy tại bất kỳ điểm nào, nó cần phải nằm trong bộ nhớ liền kề. Hoặc bạn phải đạt được hiệu suất O (N) mỗi lần bạn cần truy cập vào bộ đệm của bạn liên tục sau khi nối thêm hoặc bạn cần phân bổ thêm bộ nhớ, vì vậy bạn có thể trì hoãn và phân bổ các hoạt động đó theo yêu cầu để giữ bộ nhớ của bạn tiếp giáp. –