Skip to content

Commit 982498e

Browse files
committed
Fix bandwidth figures and a new, more comprehensive, benchmark
1 parent fb33afa commit 982498e

File tree

2 files changed

+148
-17
lines changed

2 files changed

+148
-17
lines changed

bench/ndarray/compute_dists.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
# Benchmark for comparing compute speeds of Blosc2 and Numexpr.
1010
# One can use different distributions of data:
11-
# ones, arange, linspace, or random
11+
# constant, arange, linspace, or random
1212
# The expression can be any valid Numexpr expression.
1313

1414
import blosc2
@@ -17,11 +17,11 @@
1717
import numexpr as ne
1818

1919
# Bench params
20-
N = 10_000
21-
step = 1000
20+
N = 30_000
21+
step = 3000
2222
dtype = np.dtype(np.float64)
2323
persistent = False
24-
dist = "linspace" # "ones" or "linspace" or "arange" or "random"
24+
dist = "constant" # "arange" or "linspace" or "constant" or "random"
2525
expr = "(a - b)"
2626
#expr = "sum(a - b)"
2727
#expr = "cos(a)**2 + sin(b)**2 - 1"
@@ -47,15 +47,15 @@
4747
for i in range(step, N + step, step):
4848
shape = (i, i)
4949
# shape = (i * i,)
50-
if dist == "ones":
50+
if dist == "constant":
5151
a = blosc2.ones(shape, dtype=dtype, urlpath=urlpath['a'])
52-
b = blosc2.ones(shape, dtype=dtype, urlpath=urlpath['b'])
52+
b = blosc2.full(shape, 2, dtype=dtype, urlpath=urlpath['b'])
5353
elif dist == "arange":
54-
a = blosc2.arange(0, i**2, dtype=dtype, shape=shape, urlpath=urlpath['a'])
55-
b = blosc2.arange(0, i**2, dtype=dtype, shape=shape, urlpath=urlpath['b'])
54+
a = blosc2.arange(0, i * i, dtype=dtype, shape=shape, urlpath=urlpath['a'])
55+
b = blosc2.arange(i * i, 2* i * i, dtype=dtype, shape=shape, urlpath=urlpath['b'])
5656
elif dist == "linspace":
5757
a = blosc2.linspace(0, 1, dtype=dtype, shape=shape, urlpath=urlpath['a'])
58-
b = blosc2.linspace(0, 1, dtype=dtype, shape=shape, urlpath=urlpath['b'])
58+
b = blosc2.linspace(1, 2, dtype=dtype, shape=shape, urlpath=urlpath['b'])
5959
elif dist == "random":
6060
t0 = time()
6161
_ = np.random.random(shape)
@@ -73,7 +73,7 @@
7373
t0 = time()
7474
c = blosc2.lazyexpr(expr).compute(urlpath=urlpath['c'])
7575
t = time() - t0
76-
ws_sizes.append((a.schunk.nbytes + b.schunk.nbytes) / 2**30)
76+
ws_sizes.append((a.schunk.nbytes + b.schunk.nbytes + c.schunk.nbytes) / 2**30)
7777
speed = ws_sizes[-1] / t
7878
print(f"Time to compute a - b: {t:.5f} s -- {speed:.2f} GB/s -- cratio: {c.schunk.cratio:.1f}x")
7979
#print(f"result: {c[()]}")
@@ -86,15 +86,15 @@
8686
for i in range(step, N + step, step):
8787
shape = (i, i)
8888
# shape = (i * i,)
89-
if dist == "ones":
89+
if dist == "constant":
9090
a = np.ones(shape, dtype=dtype)
91-
b = np.ones(shape, dtype=dtype)
91+
b = np.full(shape, 2, dtype=dtype)
9292
elif dist == "arange":
93-
a = np.arange(0, i**2, dtype=dtype).reshape(shape)
94-
b = np.arange(0, i**2, dtype=dtype).reshape(shape)
93+
a = np.arange(0, i * i, dtype=dtype).reshape(shape)
94+
b = np.arange(i * i, 2 * i * i, dtype=dtype).reshape(shape)
9595
elif dist == "linspace":
96-
a = np.linspace(0, 1, num=i**2, dtype=dtype).reshape(shape)
97-
b = np.linspace(0, 1, num=i**2, dtype=dtype).reshape(shape)
96+
a = np.linspace(0, 1, num=i * i, dtype=dtype).reshape(shape)
97+
b = np.linspace(1, 2, num=i * i, dtype=dtype).reshape(shape)
9898
elif dist == "random":
9999
a = np.random.random(shape)
100100
b = np.random.random(shape)
@@ -104,7 +104,7 @@
104104
t0 = time()
105105
c = ne.evaluate(expr)
106106
t = time() - t0
107-
ws_size = (a.nbytes + b.nbytes) / 2**30
107+
ws_size = (a.nbytes + b.nbytes + c.nbytes) / 2**30
108108
speed = ws_size / t
109109
print(f"Time to compute with Numexpr: {t:.5f} s - {speed:.2f} GB/s")
110110
#print(f"result: {c}")

bench/ndarray/compute_dists2.py

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#######################################################################
2+
# Copyright (c) 2019-present, Blosc Development Team <[email protected]>
3+
# All rights reserved.
4+
#
5+
# This source code is licensed under a BSD-style license (found in the
6+
# LICENSE file in the root directory of this source tree)
7+
#######################################################################
8+
9+
# Benchmark for comparing compute speeds of Blosc2 and Numexpr.
10+
# This version compares across different distributions of data:
11+
# constant, arange, linspace, or random
12+
# The expression can be any valid Numexpr expression.
13+
14+
import blosc2
15+
from time import time
16+
import numpy as np
17+
import numexpr as ne
18+
import matplotlib.pyplot as plt
19+
import seaborn as sns
20+
21+
# Bench params
22+
N = 10_000
23+
step = 3000
24+
dtype = np.dtype(np.float64)
25+
persistent = False
26+
distributions = ["constant", "arange", "linspace", "random"]
27+
expr = "(a - b)"
28+
#expr = "sum(a - b)"
29+
#expr = "cos(a)**2 + sin(b)**2 - 1"
30+
#expr = "sum(cos(a)**2 + sin(b)**2 - 1)"
31+
32+
# Set default compression params
33+
cparams = blosc2.CParams(clevel=1, codec=blosc2.Codec.BLOSCLZ)
34+
blosc2.cparams_dflts["codec"] = cparams.codec
35+
blosc2.cparams_dflts["clevel"] = cparams.clevel
36+
# Set default storage params
37+
storage = blosc2.Storage(contiguous=True, mode="w")
38+
blosc2.storage_dflts["contiguous"] = storage.contiguous
39+
blosc2.storage_dflts["mode"] = storage.mode
40+
41+
# Create dictionaries to store results for each distribution
42+
blosc2_speeds = {dist: [] for dist in distributions}
43+
numexpr_speeds = {dist: [] for dist in distributions}
44+
ws_sizes = []
45+
46+
# Generate working set sizes once
47+
sizes = list(range(step, N + step, step))
48+
for i in sizes:
49+
ws_sizes.append((i * i * 3 * np.dtype(dtype).itemsize) / 2**30) # Approximate size in GB
50+
51+
# Loop through different distributions for benchmarking
52+
for dist in distributions:
53+
print(f"\nBenchmarking {dist} distribution...")
54+
55+
# Evaluate using Blosc2
56+
for i in sizes:
57+
shape = (i, i)
58+
urlpath = {name: None for name in ("a", "b", "c")}
59+
60+
if dist == "constant":
61+
a = blosc2.ones(shape, dtype=dtype, urlpath=urlpath['a'])
62+
b = blosc2.full(shape, 2, dtype=dtype, urlpath=urlpath['b'])
63+
elif dist == "arange":
64+
a = blosc2.arange(0, i * i, dtype=dtype, shape=shape, urlpath=urlpath['a'])
65+
b = blosc2.arange(i * i, 2* i * i, dtype=dtype, shape=shape, urlpath=urlpath['b'])
66+
elif dist == "linspace":
67+
a = blosc2.linspace(0, 1, dtype=dtype, shape=shape, urlpath=urlpath['a'])
68+
b = blosc2.linspace(1, 2, dtype=dtype, shape=shape, urlpath=urlpath['b'])
69+
elif dist == "random":
70+
_ = np.random.random(shape)
71+
a = blosc2.fromiter(np.nditer(_), dtype=dtype, shape=shape, urlpath=urlpath['a'])
72+
# b = a.copy(urlpath=urlpath['b']) # faster, but output is not random
73+
_ = np.random.random(shape)
74+
b = blosc2.fromiter(np.nditer(_), dtype=dtype, shape=shape, urlpath=urlpath['b'])
75+
76+
t0 = time()
77+
c = blosc2.lazyexpr(expr).compute(urlpath=urlpath['c'])
78+
t = time() - t0
79+
speed = (a.schunk.nbytes + b.schunk.nbytes + c.schunk.nbytes) / 2**30 / t
80+
print(f"Blosc2 - {dist} - Size {i}x{i}: {speed:.2f} GB/s - cratio: {c.schunk.cratio:.1f}x")
81+
blosc2_speeds[dist].append(speed)
82+
83+
# Evaluate using Numexpr
84+
for i in sizes:
85+
shape = (i, i)
86+
87+
if dist == "constant":
88+
a = np.ones(shape, dtype=dtype)
89+
b = np.full(shape, 2, dtype=dtype)
90+
elif dist == "arange":
91+
a = np.arange(0, i * i, dtype=dtype).reshape(shape)
92+
b = np.arange(i * i, 2 * i * i, dtype=dtype).reshape(shape)
93+
elif dist == "linspace":
94+
a = np.linspace(0, 1, num=i * i, dtype=dtype).reshape(shape)
95+
b = np.linspace(1, 2, num=i * i, dtype=dtype).reshape(shape)
96+
elif dist == "random":
97+
a = np.random.random(shape)
98+
b = np.random.random(shape)
99+
100+
t0 = time()
101+
c = ne.evaluate(expr)
102+
t = time() - t0
103+
speed = (a.nbytes + b.nbytes + c.nbytes) / 2**30 / t
104+
print(f"Numexpr - {dist} - Size {i}x{i}: {speed:.2f} GB/s")
105+
numexpr_speeds[dist].append(speed)
106+
107+
# Create a figure with four subplots (2x2 grid)
108+
sns.set_theme(style="whitegrid")
109+
fig, axes = plt.subplots(2, 2, figsize=(14, 10), sharex=True)
110+
111+
# Flatten axes for easier iteration
112+
axes = axes.flatten()
113+
114+
# Plot each distribution in its own subplot
115+
for i, dist in enumerate(distributions):
116+
axes[i].plot(ws_sizes, blosc2_speeds[dist], marker='o', linestyle='-', label="Blosc2")
117+
axes[i].plot(ws_sizes, numexpr_speeds[dist], marker='s', linestyle='--', label="Numexpr")
118+
axes[i].set_title(f"{dist.capitalize()} Distribution")
119+
axes[i].set_ylabel("Speed (GB/s)")
120+
axes[i].grid(True)
121+
axes[i].legend()
122+
if i >= 2: # Add x-label only to bottom subplots
123+
axes[i].set_xlabel("Working set size (GB)")
124+
125+
# Add a shared title
126+
fig.suptitle(f"Blosc2 vs Numexpr Performance Across Different Data Distributions ({expr=})", fontsize=16)
127+
plt.tight_layout(rect=[0, 0, 1, 0.96]) # Adjust the rect parameter to make room for the suptitle
128+
129+
# Save the unified plot with subplots
130+
plt.savefig("blosc2_vs_numexpr_subplots.png", dpi=300, bbox_inches='tight')
131+
plt.show()

0 commit comments

Comments
 (0)