Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple ringbuffer with tests #116

Conversation

mathias-baumann-frequenz
Copy link
Contributor

First draft, more tests to come, also performance test.
next step is testing the slicing parts.

Signed-off-by: Marenz mathias.baumann@frequenz.com

src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
Comment on lines 90 to 111
def write(self, value: T, index: int) -> None:
""""""
self.data[index] = value
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. You can also override __setitem__ :)
  2. Maybe check if index < len(self.data)?

src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
@github-actions github-actions bot added the part:tests Affects the unit, integration and performance (benchmarks) tests label Dec 14, 2022
@mathias-baumann-frequenz
Copy link
Contributor Author

I pushed an update that shows a basic working of the timestamp buffer. The numpy variant still has a problem that I am currently looking into.

More logic tests are to come as well as performance tests

@mathias-baumann-frequenz mathias-baumann-frequenz force-pushed the simple-ringbuffer branch 2 times, most recently from bc0a8ee to f3d0b72 Compare December 20, 2022 14:12
@mathias-baumann-frequenz
Copy link
Contributor Author

Array: ........................................
List:  ........................................
Time to fill 29 days with data:
Array: 0.09143387260000964 seconds
List:  0.08543843417501193 seconds
Diff:  0.005995438424997709

       =========================================
Array: ........................................
List:  ........................................
Filling 29 days and running average & mean on every day:
Array: 0.09528444537500036 seconds
List:  0.12714749602500888 seconds
Diff:  -0.03186305065000852

@mathias-baumann-frequenz mathias-baumann-frequenz force-pushed the simple-ringbuffer branch 2 times, most recently from 96de0fa to 2057ab9 Compare December 20, 2022 16:30
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of the comments are just me putting the pylint or mypy hat, so it is not as bad as it looks 😆

# Copyright © 2022 Frequenz Energy-as-a-Service GmbH

"""
Performance test for the `Ringbuffer` class

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please mirror the directory structure in src for benchmarks so it is easier to figure out which benchmark is benchmarking what. In this case it should be benchmarks/util/ringbuffer.py

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see power_distribution in the src dir so I assumed it would be more.. arbitrary..

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think at some point that one was top-level, we probably forgot to update the benchmarks structure when we moved it.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it is intentional but the benchmark disappeared from the PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah no, that was a bad renaming

benchmarks/benchmark_ringbuffer.py Outdated Show resolved Hide resolved
Comment on lines 67 to 68
(f"{basetime + timedelta(days=day)} average {np.average(minutes)}")
(f"{basetime + timedelta(days=day)} median {np.median(minutes)}")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are these strings laying around there?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just trying to prevent python from optimizing them away

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get it? You want to test the performance of building a formatted string?

benchmarks/benchmark_ringbuffer.py Outdated Show resolved Hide resolved


class RingBuffer(Generic[T]):
def __init__(self, container: Any):

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You probably want to have some restrictions on the container, like MutableSequence?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to, but I found no type that would fit so far. Will investigate again.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tests/utils/ringbuffer.py:19: error: Argument 1 to "RingBuffer" has incompatible type "ndarray[Any, dtype[f
loating[_64Bit]]]"; expected "MutableSequence[<nothing>]"  [arg-type]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you show how you changed the code to get that message?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just changed Any to MutableBuffer[T]

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, yeah, it looks like it's not even a Sequence (although it should), but probably won't get all the methods needed by a MutableSequence: numpy/numpy#2776.

So we have 2 options I can see:

  1. Use an union: MutableSquence[T] | np.ndarray, which is a bit ugly to have this special casing, why only np.ndarray and not anything else that looks like a duck?
  2. Use a Protocol: just look at all the methods and attributes you need a container to have an write a BufferContainer protocol.

IMHO 2. is the correct option, but it is a bit of work that might not be justified at this point, so I would leave Any and create an issue to improve this in the future.

src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
"""
return index % self.max_size()

def __getitem__(self, index_or_slice: int | slice) -> T | Sequence[T]:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before, better not provide this if the implementation is not correct IMHO.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it is correct insofar as that it provides direct access to the buffer..

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above, I think it is more clear to provide access to the underlying container and let people slice that directly instead.

Another comment is you can improve type checking by adding @overloads, to specify which subset of types are valid in those unions. Here is an example: https://github.com/frequenz-floss/frequenz-sdk-python/pull/130/files#diff-43d601005020a1afe419acdfad091a0e813ddd9a6f2e1d4e0db73c030ff8d599R135-R143

tests/utils/ringbuffer.py Outdated Show resolved Hide resolved
tests/utils/ringbuffer.py Outdated Show resolved Hide resolved
@leandro-lucarella-frequenz
Copy link
Contributor

Beware of damn GitHub compressing comments and hiding them, make sure to click when it says "N hidden conversations. Load more".

@leandro-lucarella-frequenz leandro-lucarella-frequenz added the part:core Affects the SDK core components (data structures, etc.) label Dec 21, 2022
@github-actions github-actions bot removed the part:core Affects the SDK core components (data structures, etc.) label Dec 22, 2022
@mathias-baumann-frequenz mathias-baumann-frequenz force-pushed the simple-ringbuffer branch 2 times, most recently from 0d16f7f to ffa7b7c Compare December 23, 2022 13:08
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There were a few pending unanswered comments from the previous review. I think all the remaining stuff is very minor. There are only 2 major topics:

  1. If, when and how do we need the sorted buffer.
  2. If we really need proper slicing (setting and getting). My impression is we don't need setting a slice. About getting, not sure, maybe, maybe it is enough with using islice and we save some complexity (which IMHO while implementing the resampling buffer was not at all minor, specially with the many cases if we include steps and negative values).

src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
src/frequenz/sdk/util/ringbuffer.py Outdated Show resolved Hide resolved
Signed-off-by: Marenz <mathias.baumann@frequenz.com>
@mathias-baumann-frequenz
Copy link
Contributor Author

Closing this to keep the branch around for the simple ring buffer, will create a new PR with only the sorted ring buffer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
part:tests Affects the unit, integration and performance (benchmarks) tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants