Skip to content

Commit 1b06eb9

Browse files
committed
Benchmark for computing with different numerical distributions
1 parent 53b626b commit 1b06eb9

File tree

1 file changed

+136
-0
lines changed

1 file changed

+136
-0
lines changed

bench/ndarray/compute_dists.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
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+
# One can use different distributions of data:
11+
# ones, 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+
19+
# Bench params
20+
N = 10_000
21+
step = 1000
22+
dtype = np.dtype(np.float64)
23+
persistent = False
24+
dist = "linspace" # "ones" or "linspace" or "arange" or "random"
25+
expr = "(a - b)"
26+
#expr = "sum(a - b)"
27+
#expr = "cos(a)**2 + sin(b)**2 - 1"
28+
#expr = "sum(cos(a)**2 + sin(b)**2 - 1)"
29+
30+
# Set default compression params
31+
cparams = blosc2.CParams(clevel=1, codec=blosc2.Codec.BLOSCLZ)
32+
blosc2.cparams_dflts["codec"] = cparams.codec
33+
blosc2.cparams_dflts["clevel"] = cparams.clevel
34+
# Set default storage params
35+
storage = blosc2.Storage(contiguous=True, mode="w")
36+
blosc2.storage_dflts["contiguous"] = storage.contiguous
37+
blosc2.storage_dflts["mode"] = storage.mode
38+
39+
urlpath = dict((aname, None) for aname in ("a", "b", "c"))
40+
if persistent:
41+
urlpath = dict((aname, f"{aname}.b2nd") for aname in ("a", "b", "c"))
42+
43+
btimes = []
44+
bspeeds = []
45+
ws_sizes = []
46+
rng = np.random.default_rng()
47+
for i in range(step, N + step, step):
48+
shape = (i, i)
49+
# shape = (i * i,)
50+
if dist == "ones":
51+
a = blosc2.ones(shape, dtype=dtype, urlpath=urlpath['a'])
52+
b = blosc2.ones(shape, dtype=dtype, urlpath=urlpath['b'])
53+
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'])
56+
elif dist == "linspace":
57+
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'])
59+
elif dist == "random":
60+
t0 = time()
61+
_ = np.random.random(shape)
62+
a = blosc2.fromiter(np.nditer(_), dtype=dtype, shape=shape, urlpath=urlpath['a'])
63+
b = blosc2.fromiter(np.nditer(_), dtype=dtype, shape=shape, urlpath=urlpath['b'])
64+
# This uses less memory, but it is 2x-3x slower
65+
# iter_ = (rng.random() for _ in range(i**2 * 2))
66+
# a = blosc2.fromiter(iter_, dtype=dtype, shape=shape, urlpath=urlpath['a'])
67+
# b = blosc2.fromiter(iter_, dtype=dtype, shape=shape, urlpath=urlpath['b'])
68+
t = time() - t0
69+
#print(f"Time to create data: {t:.5f} s - {a.schunk.nbytes/t / 1e9:.2f} GB/s")
70+
else:
71+
raise ValueError("Invalid distribution type")
72+
73+
t0 = time()
74+
c = blosc2.lazyexpr(expr).compute(urlpath=urlpath['c'])
75+
t = time() - t0
76+
ws_sizes.append((a.schunk.nbytes + b.schunk.nbytes) / 2**30)
77+
speed = ws_sizes[-1] / t
78+
print(f"Time to compute a - b: {t:.5f} s -- {speed:.2f} GB/s -- cratio: {c.schunk.cratio:.1f}x")
79+
#print(f"result: {c[()]}")
80+
btimes.append(t)
81+
bspeeds.append(speed)
82+
83+
# Evaluate using Numexpr compute engine
84+
ntimes = []
85+
nspeeds = []
86+
for i in range(step, N + step, step):
87+
shape = (i, i)
88+
# shape = (i * i,)
89+
if dist == "ones":
90+
a = np.ones(shape, dtype=dtype)
91+
b = np.ones(shape, dtype=dtype)
92+
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)
95+
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)
98+
elif dist == "random":
99+
a = np.random.random(shape)
100+
b = np.random.random(shape)
101+
else:
102+
raise ValueError("Invalid distribution type")
103+
104+
t0 = time()
105+
c = ne.evaluate(expr)
106+
t = time() - t0
107+
ws_size = (a.nbytes + b.nbytes) / 2**30
108+
speed = ws_size / t
109+
print(f"Time to compute with Numexpr: {t:.5f} s - {speed:.2f} GB/s")
110+
#print(f"result: {c}")
111+
ntimes.append(t)
112+
nspeeds.append(speed)
113+
114+
# Plot
115+
import matplotlib.pyplot as plt
116+
import matplotlib.ticker as ticker
117+
import seaborn as sns
118+
119+
sns.set_theme(style="whitegrid")
120+
plt.figure(figsize=(10, 6))
121+
plt.plot(ws_sizes, bspeeds, label="Blosc2", marker='o')
122+
plt.plot(ws_sizes, nspeeds, label="Numexpr", marker='o')
123+
# Set y-axis to start from 0
124+
plt.ylim(bottom=0)
125+
plt.xlabel("Working set (GB)")
126+
#plt.ylabel("Time (s)")
127+
plt.ylabel("Speed (GB/s)")
128+
plt.title(f"Blosc2 vs Numexpr performance -- {dist} distribution")
129+
plt.legend()
130+
#plt.gca().xaxis.set_major_locator(ticker.MaxNLocator(integer=True))
131+
#plt.gca().yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, _: f'{x:.2f}'))
132+
plt.grid()
133+
plt.show()
134+
# Save the figure
135+
plt.savefig("blosc2_vs_numexpr.png", dpi=300, bbox_inches='tight')
136+
plt.close()

0 commit comments

Comments
 (0)