Skip to content

Commit fcaff7d

Browse files
Add ringbuffer memory benchmark (#278)
Signed-off-by: Mathias L. Baumann <[email protected]>
2 parents 0bbaf03 + 4962fc5 commit fcaff7d

File tree

1 file changed

+91
-0
lines changed

1 file changed

+91
-0
lines changed
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# License: MIT
2+
# Copyright © 2023 Frequenz Energy-as-a-Service GmbH
3+
4+
"""Memory allocation benchmark for the ringbuffer."""
5+
6+
from __future__ import annotations
7+
8+
import argparse
9+
import tracemalloc
10+
from datetime import datetime, timedelta
11+
12+
import numpy as np
13+
14+
from frequenz.sdk.timeseries import Sample
15+
from frequenz.sdk.timeseries._ringbuffer import OrderedRingBuffer
16+
17+
FIVE_MINUTES = timedelta(minutes=5)
18+
19+
RINGBUFFER_LENGTH = 4_000
20+
# Number of iterations to run the benchmark
21+
ITERATIONS = 100
22+
23+
24+
def parse_args() -> tuple[int, int, int]:
25+
"""Parse command line arguments."""
26+
parser = argparse.ArgumentParser()
27+
parser.add_argument(
28+
"--size",
29+
type=int,
30+
default=RINGBUFFER_LENGTH,
31+
help="Size of the ringbuffer to memory benchmark",
32+
)
33+
parser.add_argument(
34+
"--iterations",
35+
type=int,
36+
default=ITERATIONS,
37+
help="Number of iterations to run the benchmark",
38+
)
39+
parser.add_argument(
40+
"--gap-size",
41+
type=int,
42+
default=0,
43+
help="Number of samples to skip between each update",
44+
)
45+
args = parser.parse_args()
46+
return args.size, args.iterations, args.gap_size
47+
48+
49+
def main(ringbuffer_len: int, iterations: int, gap_size: int) -> None:
50+
"""Run the benchmark.
51+
52+
Args:
53+
ringbuffer_len: Size of the ringbuffer to dump/load
54+
iterations: Number of iterations to run the benchmark
55+
gap_size: Number of samples to skip between each update
56+
57+
"""
58+
# Trace memory allocations
59+
tracemalloc.start()
60+
61+
ringbuffer = OrderedRingBuffer(
62+
np.arange(0, ringbuffer_len, dtype=np.float64), FIVE_MINUTES
63+
)
64+
65+
# Snapshot memory allocations after ringbuffer creation
66+
snapshot_init = tracemalloc.take_snapshot()
67+
68+
print(f"size: {ringbuffer_len}")
69+
print(f"iterations: {iterations}")
70+
print(f"gap_size: {gap_size}")
71+
72+
for i in range(0, ringbuffer_len * iterations, gap_size + 1):
73+
ringbuffer.update(
74+
Sample(datetime.fromtimestamp(200 + i * FIVE_MINUTES.total_seconds()), i)
75+
)
76+
77+
# Snapshot memory allocations after ringbuffer update
78+
snapshot_update = tracemalloc.take_snapshot()
79+
80+
# Allocation diff between ringbuffer creation and update
81+
top_stats = snapshot_update.compare_to(snapshot_init, "lineno")
82+
83+
# Print the top 10 memory allocations
84+
print("Allocations since ringbuffer creation:")
85+
print("[ Top 10 ]")
86+
for stat in top_stats[:10]:
87+
print(stat)
88+
89+
90+
if __name__ == "__main__":
91+
main(*parse_args())

0 commit comments

Comments
 (0)