Git Product home page Git Product logo

python-dvg-ringbuffer's Introduction

https://static.pepy.tech/personalized-badge/dvg-ringbuffer?period=month&units=international_system&left_color=gray&right_color=blue&left_text=%E2%86%93%20per%20month https://img.shields.io/pypi/v/dvg-ringbuffer https://img.shields.io/pypi/pyversions/dvg-ringbuffer

DvG_RingBuffer

Provides a numpy ring buffer at a fixed memory address to allow for significantly sped up numpy, sigpy, numba & pyFFTW calculations.

Installation:

pip install dvg-ringbuffer

Based on:

https://pypi.org/project/numpy_ringbuffer/ by Eric Wieser.

DvG_RingBuffer can be used as a drop-in replacement for numpy_ringbuffer and provides several optimizations and extra features, but requires Python 3.

If and only if the ring buffer is completely full, will it return its array data as a contiguous C-style numpy array at a single fixed memory address per ring buffer instance. It does so by unwrapping the discontiguous ring buffer array into a second extra unwrap buffer that is a private member of the ring buffer class. This is advantegeous for other accelerated computations by, e.g., numpy, sigpy, numba & pyFFTW, that benefit from being fed with contiguous arrays at the same memory address each time again, such that compiler optimizations and data planning are made possible.

When the ring buffer is not completely full, it will return its data as a contiguous C-style numpy array, but at different memory addresses. This is how the original numpy-buffer always operates.

Commonly, collections.deque() is used to act as a ring buffer. The benefits of a deque is that it is thread safe and fast (enough) for most situations. However, there is an overhead whenever the deque -- a list-like container -- needs to be transformed into a numpy array. Because DvG_RingBuffer already returns numpy arrays it will outperform a collections.deque() easily, tested to be a factor of ~60.

Warning

  • This ring buffer is not thread safe. You'll have to implement your own mutex locks when using this ring buffer in multithreaded operations.
  • The data array that is returned by a full ring buffer is a pass by reference of the unwrap buffer. It is not a copy! Hence, changing values in the returned data array is identical to changing values in the unwrap buffer.

API

class RingBuffer(capacity, dtype=np.float64, allow_overwrite=True)

Create a new ring buffer with the given capacity and element type.

Args:
capacity (int):
The maximum capacity of the ring buffer
dtype (data-type, optional):

Desired type of buffer elements. Use a type like (float, 2) to produce a buffer with shape (capacity, 2).

Default: np.float64

allow_overwrite (bool, optional):

If False, throw an IndexError when trying to append to an already full buffer.

Default: True

Methods

  • clear()

  • append(value)

    Append a single value to the ring buffer.

    rb = RingBuffer(3, dtype=np.int)  #  []
    rb.append(1)                      #  [1]
    rb.append(2)                      #  [1, 2]
    rb.append(3)                      #  [1, 2, 3]
    rb.append(4)                      #  [2, 3, 4]
  • appendleft(value)

    Append a single value to the ring buffer from the left side.

    rb = RingBuffer(3, dtype=np.int)  #  []
    rb.appendleft(1)                  #  [1]
    rb.appendleft(2)                  #  [2, 1]
    rb.appendleft(3)                  #  [3, 2, 1]
    rb.appendleft(4)                  #  [4, 3, 2]
  • extend(values)

    Extend the ring buffer with a list of values.

    rb = RingBuffer(3, dtype=np.int)  #  []
    rb.extend([1])                    #  [1]
    rb.extend([2, 3])                 #  [1, 2, 3]
    rb.extend([4, 5, 6, 7])           #  [5, 6, 7]
  • extendleft(values)

    Extend the ring buffer with a list of values from the left side.

    rb = RingBuffer(3, dtype=np.int)  #  []
    rb.extendleft([1])                #  [1]
    rb.extendleft([3, 2])             #  [3, 2, 1]
    rb.extendleft([7, 6, 5, 4])       #  [7, 6, 5]
  • pop()

    Remove the right-most item from the ring buffer and return it.

  • popleft()

    Remove the left-most item from the ring buffer and return it.

Properties

  • is_full
  • unwrap_address
  • current_address
  • dtype
  • shape
  • maxlen

Indexing & slicing

  • [] including negative indices and slicing

    from dvg_ringbuffer import RingBuffer
    
    rb = RingBuffer(4, dtype=np.int)  # --> rb[:] = array([], dtype=int32)
    rb.extend([1, 2, 3, 4, 5])        # --> rb[:] = array([2, 3, 4, 5])
    x = rb[0]                         # --> x = 2
    x = rb[-1]                        # --> x = 5
    x = rb[:3]                        # --> x = array([2, 3, 4])
    x = rb[np.array([0, 2, -1])]      # --> x = array([2, 4, 5])
    
    rb = RingBuffer(5, dtype=(np.int, 2))  # --> rb[:] = array([], shape=(0, 2), dtype=int32)
    rb.append([1, 2])                      # --> rb[:] = array([[1, 2]])
    rb.append([3, 4])                      # --> rb[:] = array([[1, 2], [3, 4]])
    rb.append([5, 6])                      # --> rb[:] = array([[1, 2], [3, 4], [5, 6]])
    x = rb[0]                              # --> x = array([1, 2])
    x = rb[0, :]                           # --> x = array([1, 2])
    x = rb[:, 0]                           # --> x = array([1, 3, 5])

python-dvg-ringbuffer's People

Contributors

dennis-van-gils avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

python-dvg-ringbuffer's Issues

[Question] Why special case for dtype=np.float64 in __init__ and clear ?

Hi @Dennis-van-Gils.

Amazing ring buffer !
I am studying your code and don't understand why do you have a special treatment for np.float64.

It is only in the __init__ and clear methods.

I would guess you are doing that to optimize your array creation (and reset) with np.nan which should be faster than setting the whole array to zeros.

But I failed to understand why it is only for dtype=np.float64.

Could you explain please ?

[Enhancement] Rework

Hi again @Dennis-van-Gils .

I fully studied your code and it inspired me a lot. Thank you.

I rewrote the ring buffer.
The key idea I use is to use only one numpy array buffer, but of size 2 N and write input in two places, (in append it's at [self._idx_R] and [(self._idx_R + self._N) % self._2N]) and to fix the pointers like you do too (but a bit differently).

I also added the mixin NDArrayOperatorsMixin to have the extra operator special methods (link)

I tested my implementation manually and with your tests suit (some tests has to be change since I change some error classes and the representation) and it seems to be correct.

I added 2 new methods: pop_n(self, n: int) -> np.ndarray and popleft_n(self, n: int) -> np.ndarray which let you pop n items and return an array.

I tested the performance with your timeit test that I modified and those are the relative result on my machine:

Buffer Type Speed Factor with capacity = 20500 Speed Factor with capacity = 80000 Comment
deque 1 1 baseline, always 1
numpy_ringbuffer ~19.7 ~46.6 from Eric Wieser
dvg_ringbuffer ~25.3 ~70.6 from Dennis-van-Gils
gs_ringbuffer ~29.7 ~120.9 from Gil Shoshan

I modified your test to test also when the buffer is not fully filled.

My implementation is faster than your from what I tested.

I also tested with a very big capacity at 800000 and my implementation seems to time pretty constant while your get slower,
at this big capacity, my buffer was ~34 times faster than yours.

At small capacity (I tested 80), my implementation was a bit slower, yours was 1.1 times faster.

I might have some bugs (I hope not) so be sure to test it yourself if you want to use it.

Here the my implementation and the performance test files:
ringbuffer_v2.zip

You will find my implementation in the ringbuffer_v2.py file.

Hope it can be useful to you and thank you again for sharing your code.
I will take a look at https://github.com/Dennis-van-Gils/python-dvg-dump/blob/master/src/dvg_fftw_welchpowerspectrum.py as you suggested me.

Have a nice day.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.