tabmat 4.3.0__cp315-cp315-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tabmat/__init__.py +40 -0
- tabmat/benchmark/__init__.py +0 -0
- tabmat/benchmark/generate_matrices.py +144 -0
- tabmat/benchmark/main.py +383 -0
- tabmat/benchmark/memory_tools.py +48 -0
- tabmat/benchmark/visualize_benchmarks.py +217 -0
- tabmat/categorical_matrix.py +980 -0
- tabmat/constructor.py +406 -0
- tabmat/constructor_util.py +49 -0
- tabmat/dense_matrix.py +347 -0
- tabmat/ext/__init__.py +0 -0
- tabmat/ext/categorical.cp315-win_amd64.pyd +0 -0
- tabmat/ext/dense.cp315-win_amd64.pyd +0 -0
- tabmat/ext/sparse.cp315-win_amd64.pyd +0 -0
- tabmat/ext/split.cp315-win_amd64.pyd +0 -0
- tabmat/formula.py +812 -0
- tabmat/matrix_base.py +258 -0
- tabmat/sparse_matrix.py +407 -0
- tabmat/split_matrix.py +554 -0
- tabmat/standardized_mat.py +378 -0
- tabmat/util.py +115 -0
- tabmat-4.3.0.dist-info/DELVEWHEEL +2 -0
- tabmat-4.3.0.dist-info/METADATA +101 -0
- tabmat-4.3.0.dist-info/RECORD +29 -0
- tabmat-4.3.0.dist-info/WHEEL +5 -0
- tabmat-4.3.0.dist-info/licenses/LICENSE +11 -0
- tabmat-4.3.0.dist-info/top_level.txt +1 -0
- tabmat.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
- tabmat.libs/vcomp140-f96f3a14d88d8846f31f3ab38a490304.dll +0 -0
tabmat/__init__.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""""" # start delvewheel patch
|
|
2
|
+
def _delvewheel_patch_1_13_1():
|
|
3
|
+
import os
|
|
4
|
+
if os.path.isdir(libs_dir := os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir, 'tabmat.libs'))):
|
|
5
|
+
os.add_dll_directory(libs_dir)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_delvewheel_patch_1_13_1()
|
|
9
|
+
del _delvewheel_patch_1_13_1
|
|
10
|
+
# end delvewheel patch
|
|
11
|
+
|
|
12
|
+
import importlib.metadata
|
|
13
|
+
|
|
14
|
+
from .categorical_matrix import CategoricalMatrix
|
|
15
|
+
from .constructor import from_csc, from_df, from_formula, from_pandas
|
|
16
|
+
from .dense_matrix import DenseMatrix
|
|
17
|
+
from .matrix_base import MatrixBase
|
|
18
|
+
from .sparse_matrix import SparseMatrix
|
|
19
|
+
from .split_matrix import SplitMatrix, as_tabmat, hstack
|
|
20
|
+
from .standardized_mat import StandardizedMatrix
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
__version__ = importlib.metadata.distribution(__name__).version
|
|
24
|
+
except Exception:
|
|
25
|
+
__version__ = "unknown"
|
|
26
|
+
|
|
27
|
+
__all__ = [
|
|
28
|
+
"DenseMatrix",
|
|
29
|
+
"MatrixBase",
|
|
30
|
+
"StandardizedMatrix",
|
|
31
|
+
"SparseMatrix",
|
|
32
|
+
"SplitMatrix",
|
|
33
|
+
"CategoricalMatrix",
|
|
34
|
+
"from_csc",
|
|
35
|
+
"from_formula",
|
|
36
|
+
"from_pandas",
|
|
37
|
+
"from_df",
|
|
38
|
+
"as_tabmat",
|
|
39
|
+
"hstack",
|
|
40
|
+
]
|
|
File without changes
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pickle
|
|
3
|
+
|
|
4
|
+
import click
|
|
5
|
+
import numpy as np
|
|
6
|
+
from scipy import sparse as sps
|
|
7
|
+
|
|
8
|
+
import tabmat as tm
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def make_dense_matrices(n_rows: int, n_cols: int) -> dict:
|
|
12
|
+
"""Make dense matrices for benchmarks."""
|
|
13
|
+
dense_matrices = {"numpy_C": np.random.random((n_rows, n_cols))}
|
|
14
|
+
dense_matrices["numpy_F"] = dense_matrices["numpy_C"].copy(order="F")
|
|
15
|
+
assert dense_matrices["numpy_F"].flags["F_CONTIGUOUS"]
|
|
16
|
+
dense_matrices["tabmat"] = tm.DenseMatrix(dense_matrices["numpy_C"]) # type: ignore
|
|
17
|
+
return dense_matrices
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def make_cat_matrix(n_rows: int, n_cats: int) -> tm.CategoricalMatrix:
|
|
21
|
+
"""Make categorical matrix for benchmarks."""
|
|
22
|
+
mat = tm.CategoricalMatrix(np.random.choice(np.arange(n_cats, dtype=int), n_rows))
|
|
23
|
+
return mat
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def make_cat_matrix_all_formats(n_rows: int, n_cats: int) -> dict:
|
|
27
|
+
"""Make categorical matrix with all formats for benchmarks."""
|
|
28
|
+
mat = make_cat_matrix(n_rows, n_cats)
|
|
29
|
+
d = {
|
|
30
|
+
"tabmat": mat,
|
|
31
|
+
"scipy.sparse csr": mat.tocsr(),
|
|
32
|
+
}
|
|
33
|
+
d["scipy.sparse csc"] = d["scipy.sparse csr"].tocsc()
|
|
34
|
+
return d
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def make_cat_matrices(n_rows: int, n_cat_cols_1: int, n_cat_cols_2: int) -> dict:
|
|
38
|
+
"""Make two categorical matrices for benchmarks."""
|
|
39
|
+
two_cat_matrices = {
|
|
40
|
+
"tabmat": tm.SplitMatrix(
|
|
41
|
+
[
|
|
42
|
+
make_cat_matrix(n_rows, n_cat_cols_1),
|
|
43
|
+
make_cat_matrix(n_rows, n_cat_cols_2),
|
|
44
|
+
]
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
two_cat_matrices["scipy.sparse csr"] = sps.hstack(
|
|
48
|
+
[elt.tocsr() for elt in two_cat_matrices["tabmat"].matrices]
|
|
49
|
+
)
|
|
50
|
+
two_cat_matrices["scipy.sparse csc"] = two_cat_matrices["scipy.sparse csr"].tocsc() # type: ignore
|
|
51
|
+
return two_cat_matrices
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def make_dense_cat_matrices(
|
|
55
|
+
n_rows: int, n_dense_cols: int, n_cats_1: int, n_cats_2: int
|
|
56
|
+
) -> dict:
|
|
57
|
+
"""Make dense categorical matrices for benchmarks."""
|
|
58
|
+
dense_block = np.random.random((n_rows, n_dense_cols))
|
|
59
|
+
two_cat_matrices = [
|
|
60
|
+
make_cat_matrix(n_rows, n_cats_1),
|
|
61
|
+
make_cat_matrix(n_rows, n_cats_2),
|
|
62
|
+
]
|
|
63
|
+
dense_cat_matrices = {
|
|
64
|
+
"tabmat": tm.SplitMatrix(two_cat_matrices + [tm.DenseMatrix(dense_block)]),
|
|
65
|
+
"scipy.sparse csr": sps.hstack(
|
|
66
|
+
[elt.tocsr() for elt in two_cat_matrices] + [sps.csr_matrix(dense_block)]
|
|
67
|
+
),
|
|
68
|
+
}
|
|
69
|
+
dense_cat_matrices["scipy.sparse csc"] = dense_cat_matrices[
|
|
70
|
+
"scipy.sparse csr"
|
|
71
|
+
].tocsc()
|
|
72
|
+
return dense_cat_matrices
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def make_sparse_matrices(n_rows: int, n_cols: int) -> dict:
|
|
76
|
+
"""Make sparse matrices for benchmarks."""
|
|
77
|
+
mat = sps.random(n_rows, n_cols).tocsc()
|
|
78
|
+
matrices = {
|
|
79
|
+
"scipy.sparse csc": mat,
|
|
80
|
+
"scipy.sparse csr": mat.tocsr(),
|
|
81
|
+
"tabmat": tm.SparseMatrix(mat),
|
|
82
|
+
}
|
|
83
|
+
return matrices
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _get_matrix_path(name):
|
|
87
|
+
return f"benchmark/data/{name}_data.pkl"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def get_all_benchmark_matrices():
|
|
91
|
+
"""Get all matrices used in benchmarks."""
|
|
92
|
+
return {
|
|
93
|
+
"dense": lambda: make_dense_matrices(int(4e6), 10),
|
|
94
|
+
"sparse": lambda: make_sparse_matrices(int(4e5), int(1e2)),
|
|
95
|
+
"sparse_narrow": lambda: make_sparse_matrices(int(3e6), 3),
|
|
96
|
+
"sparse_wide": lambda: make_sparse_matrices(int(4e4), int(1e4)),
|
|
97
|
+
"one_cat": lambda: make_cat_matrix_all_formats(int(1e6), int(1e5)),
|
|
98
|
+
"two_cat": lambda: make_cat_matrices(int(1e6), int(1e3), int(1e3)),
|
|
99
|
+
"dense_cat": lambda: make_dense_cat_matrices(int(3e6), 5, int(1e3), int(1e3)),
|
|
100
|
+
"dense_smallcat": lambda: make_dense_cat_matrices(int(3e6), 5, 10, int(1e3)),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
# TODO: duplication with glm_benchmarks
|
|
105
|
+
def get_comma_sep_names(xs: str):
|
|
106
|
+
"""Return comma separated names from names in input string."""
|
|
107
|
+
return [x.strip() for x in xs.split(",")]
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def get_matrix_names():
|
|
111
|
+
"""Return names for benchmark_matrices."""
|
|
112
|
+
return ",".join(get_all_benchmark_matrices().keys())
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@click.command()
|
|
116
|
+
@click.option(
|
|
117
|
+
"--matrix_name",
|
|
118
|
+
type=str,
|
|
119
|
+
help=(
|
|
120
|
+
f"Specify a comma-separated list of matrices you want to build. "
|
|
121
|
+
f"Leaving this blank will default to building all matrices. "
|
|
122
|
+
f"Matrix options: {get_matrix_names()}"
|
|
123
|
+
),
|
|
124
|
+
)
|
|
125
|
+
def generate_matrices(matrix_name: str) -> None:
|
|
126
|
+
"""Generate example matrices for benchmarks."""
|
|
127
|
+
all_benchmark_matrices = get_all_benchmark_matrices()
|
|
128
|
+
|
|
129
|
+
if matrix_name is None:
|
|
130
|
+
benchmark_matrices = list(all_benchmark_matrices.keys())
|
|
131
|
+
else:
|
|
132
|
+
benchmark_matrices = get_comma_sep_names(matrix_name)
|
|
133
|
+
|
|
134
|
+
for name in benchmark_matrices:
|
|
135
|
+
f = all_benchmark_matrices[name]
|
|
136
|
+
mats = f()
|
|
137
|
+
save_path = _get_matrix_path(name)
|
|
138
|
+
os.makedirs(os.path.dirname(save_path), exist_ok=True)
|
|
139
|
+
with open(save_path, "wb") as fname:
|
|
140
|
+
pickle.dump(mats, fname)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
if __name__ == "__main__":
|
|
144
|
+
generate_matrices()
|
tabmat/benchmark/main.py
ADDED
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
import pickle
|
|
2
|
+
import time
|
|
3
|
+
from typing import Union
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
import numpy as np
|
|
7
|
+
import pandas as pd
|
|
8
|
+
from scipy import sparse as sps
|
|
9
|
+
|
|
10
|
+
import tabmat as tm
|
|
11
|
+
from tabmat.benchmark.generate_matrices import (
|
|
12
|
+
get_all_benchmark_matrices,
|
|
13
|
+
get_comma_sep_names,
|
|
14
|
+
get_matrix_names,
|
|
15
|
+
make_cat_matrices,
|
|
16
|
+
make_cat_matrix_all_formats,
|
|
17
|
+
make_dense_cat_matrices,
|
|
18
|
+
make_dense_matrices,
|
|
19
|
+
make_sparse_matrices,
|
|
20
|
+
)
|
|
21
|
+
from tabmat.benchmark.memory_tools import track_peak_mem
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _sandwich(mat: Union[tm.MatrixBase, np.ndarray, sps.csc_matrix], vec: np.ndarray):
|
|
25
|
+
if isinstance(mat, (tm.MatrixBase, tm.StandardizedMatrix)):
|
|
26
|
+
mat.sandwich(vec)
|
|
27
|
+
elif isinstance(mat, np.ndarray):
|
|
28
|
+
(mat * vec[:, None]).T @ mat
|
|
29
|
+
else:
|
|
30
|
+
mat.T @ sps.diags(vec) @ mat
|
|
31
|
+
return
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _transpose_matvec(
|
|
35
|
+
mat: Union[tm.MatrixBase, np.ndarray, sps.csc_matrix], vec: np.ndarray
|
|
36
|
+
):
|
|
37
|
+
if isinstance(mat, (tm.MatrixBase, tm.StandardizedMatrix)):
|
|
38
|
+
return mat.transpose_matvec(vec)
|
|
39
|
+
else:
|
|
40
|
+
return mat.T.dot(vec)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _matvec(mat, vec: np.ndarray) -> np.ndarray:
|
|
44
|
+
if isinstance(mat, (tm.MatrixBase, tm.StandardizedMatrix)):
|
|
45
|
+
return mat.matvec(vec) # type: ignore
|
|
46
|
+
else:
|
|
47
|
+
return mat.dot(vec)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _lvec_setup(matrices):
|
|
51
|
+
return (np.random.random(next(iter(matrices.values())).shape[0]),)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _rvec_setup(matrices):
|
|
55
|
+
return (np.random.random(next(iter(matrices.values())).shape[1]),)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
ops = {
|
|
59
|
+
"matvec": (_rvec_setup, _matvec),
|
|
60
|
+
"transpose-matvec": (_lvec_setup, _transpose_matvec),
|
|
61
|
+
"sandwich": (_lvec_setup, _sandwich),
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_op_names():
|
|
66
|
+
"""Get names of operations."""
|
|
67
|
+
return ",".join(ops.keys())
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def run_one_benchmark_set(
|
|
71
|
+
matrices: dict[
|
|
72
|
+
str, Union[tm.MatrixBase, tm.StandardizedMatrix, np.ndarray, sps.spmatrix]
|
|
73
|
+
],
|
|
74
|
+
include_baseline: bool,
|
|
75
|
+
name: str,
|
|
76
|
+
standardized: bool,
|
|
77
|
+
ops_to_run,
|
|
78
|
+
n_iterations: int,
|
|
79
|
+
bench_memory: bool,
|
|
80
|
+
) -> pd.DataFrame:
|
|
81
|
+
"""Run a single round of benchmarks."""
|
|
82
|
+
if not include_baseline:
|
|
83
|
+
for k in list(matrices.keys()):
|
|
84
|
+
if k != "tabmat":
|
|
85
|
+
del matrices[k]
|
|
86
|
+
|
|
87
|
+
if standardized:
|
|
88
|
+
|
|
89
|
+
def _to_standardized_mat(mat):
|
|
90
|
+
if isinstance(mat, tm.MatrixBase):
|
|
91
|
+
return tm.StandardizedMatrix(mat, np.zeros(mat.shape[1]))
|
|
92
|
+
print(
|
|
93
|
+
f"""For benchmarking a {type(mat)}, the baseline matrix will not
|
|
94
|
+
be standardized."""
|
|
95
|
+
)
|
|
96
|
+
return mat
|
|
97
|
+
|
|
98
|
+
matrices = {k: _to_standardized_mat(v) for k, v in matrices.items()}
|
|
99
|
+
|
|
100
|
+
times = pd.DataFrame(
|
|
101
|
+
index=pd.MultiIndex.from_product(
|
|
102
|
+
[ops_to_run, matrices.keys()],
|
|
103
|
+
names=["operation", "storage"],
|
|
104
|
+
),
|
|
105
|
+
columns=["memory", "time"],
|
|
106
|
+
).reset_index()
|
|
107
|
+
|
|
108
|
+
for i, row in times.iterrows():
|
|
109
|
+
mat_ = matrices[row["storage"]]
|
|
110
|
+
setup_fnc, op_fnc = ops[row["operation"]]
|
|
111
|
+
setup_data = setup_fnc(matrices)
|
|
112
|
+
runtimes = []
|
|
113
|
+
peak_mems = []
|
|
114
|
+
for _ in range(n_iterations):
|
|
115
|
+
start = time.time()
|
|
116
|
+
if bench_memory:
|
|
117
|
+
peak_mem = track_peak_mem(op_fnc, mat_, *setup_data)
|
|
118
|
+
else:
|
|
119
|
+
op_fnc(mat_, *setup_data)
|
|
120
|
+
peak_mem = 0
|
|
121
|
+
end = time.time()
|
|
122
|
+
peak_mems.append(peak_mem)
|
|
123
|
+
runtimes.append(end - start)
|
|
124
|
+
|
|
125
|
+
# We want to get a consistent measure of runtime so we take the
|
|
126
|
+
# minimum. Any increase in runtime is due to warmup or other
|
|
127
|
+
# processes running at the same time.
|
|
128
|
+
times.loc[i, "time"] = np.min(runtimes)
|
|
129
|
+
|
|
130
|
+
# On the other hand, we want the maximum memory usage because this
|
|
131
|
+
# metric is isolated to our current python process. Any lower
|
|
132
|
+
# values will be because the highest memory usage was "missed" by
|
|
133
|
+
# the tracker
|
|
134
|
+
times.loc[i, "memory"] = np.max(peak_mems)
|
|
135
|
+
|
|
136
|
+
times["design"] = name
|
|
137
|
+
return times
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@click.command()
|
|
141
|
+
@click.option(
|
|
142
|
+
"--operation_name",
|
|
143
|
+
type=str,
|
|
144
|
+
help=(
|
|
145
|
+
f"Specify a comma-separated list of operations you want to run. Leaving this "
|
|
146
|
+
f"blank will default to running all operations. Operation options: "
|
|
147
|
+
f"{get_op_names()}"
|
|
148
|
+
),
|
|
149
|
+
)
|
|
150
|
+
@click.option(
|
|
151
|
+
"--matrix_name",
|
|
152
|
+
type=str,
|
|
153
|
+
help=(
|
|
154
|
+
f"Specify a comma-separated list of matrices you want to run or specify. "
|
|
155
|
+
f"Leaving this blank will default to running all predefined matrices. "
|
|
156
|
+
f"Matrix options: {get_matrix_names()} OR custom. If custom, specify details "
|
|
157
|
+
f"using additional custom matrix options. See --dense, --sparse, --one_cat, "
|
|
158
|
+
f"--two_cat, and --dense_cat options for more details"
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
@click.option(
|
|
162
|
+
"--dense",
|
|
163
|
+
nargs=2,
|
|
164
|
+
multiple=True,
|
|
165
|
+
help=(
|
|
166
|
+
"Specify n_rows, n_cols for custom dense matrix. "
|
|
167
|
+
"Only used if 'custom' included in matrix_name."
|
|
168
|
+
),
|
|
169
|
+
default=None,
|
|
170
|
+
)
|
|
171
|
+
@click.option(
|
|
172
|
+
"--sparse",
|
|
173
|
+
nargs=2,
|
|
174
|
+
multiple=True,
|
|
175
|
+
help=(
|
|
176
|
+
"Specify n_rows, n_cols for custom sparse matrix. "
|
|
177
|
+
"Only used if 'custom' included in matrix_name."
|
|
178
|
+
),
|
|
179
|
+
default=None,
|
|
180
|
+
)
|
|
181
|
+
@click.option(
|
|
182
|
+
"--one_cat",
|
|
183
|
+
nargs=2,
|
|
184
|
+
multiple=True,
|
|
185
|
+
help=(
|
|
186
|
+
"Specify n_rows, n_cols for custom one_cat matrix. "
|
|
187
|
+
"Only used if 'custom' included in matrix_name."
|
|
188
|
+
),
|
|
189
|
+
default=None,
|
|
190
|
+
)
|
|
191
|
+
@click.option(
|
|
192
|
+
"--two_cat",
|
|
193
|
+
nargs=3,
|
|
194
|
+
multiple=True,
|
|
195
|
+
help=(
|
|
196
|
+
"Specify n_rows, n_cols for custom two_cat matrix. "
|
|
197
|
+
"Only used if 'custom' included in matrix_name."
|
|
198
|
+
),
|
|
199
|
+
default=None,
|
|
200
|
+
)
|
|
201
|
+
@click.option(
|
|
202
|
+
"--dense_cat",
|
|
203
|
+
nargs=4,
|
|
204
|
+
multiple=True,
|
|
205
|
+
help=(
|
|
206
|
+
"Specify n_rows, n_cols for custom dense_cat matrix. "
|
|
207
|
+
"Only used if 'custom' included in matrix_name."
|
|
208
|
+
),
|
|
209
|
+
default=None,
|
|
210
|
+
)
|
|
211
|
+
@click.option(
|
|
212
|
+
"--bench_memory",
|
|
213
|
+
type=bool,
|
|
214
|
+
is_flag=True,
|
|
215
|
+
help=(
|
|
216
|
+
"Should we benchmark memory usage with tracemalloc. Turning this on will make "
|
|
217
|
+
"the runtime benchmarks less useful due to memory benchmarking overhead. "
|
|
218
|
+
"Also, when memory benchmarking is on, debuggers like pdb and ipdb seem to "
|
|
219
|
+
"fail."
|
|
220
|
+
),
|
|
221
|
+
default=False,
|
|
222
|
+
)
|
|
223
|
+
@click.option(
|
|
224
|
+
"--n_iterations",
|
|
225
|
+
type=int,
|
|
226
|
+
help=(
|
|
227
|
+
"How many times to re-run the benchmark. The maximum memory usage and minimum "
|
|
228
|
+
"runtime will be reported. Higher numbers of iterations reduce noise. This "
|
|
229
|
+
"defaults to 100 unless memory benchmarking is turned on in which case it will "
|
|
230
|
+
"be 1."
|
|
231
|
+
),
|
|
232
|
+
default=None,
|
|
233
|
+
)
|
|
234
|
+
@click.option(
|
|
235
|
+
"--include_baseline",
|
|
236
|
+
type=bool,
|
|
237
|
+
is_flag=True,
|
|
238
|
+
help="Should we include a numpy/scipy baseline performance benchmark.",
|
|
239
|
+
default=False,
|
|
240
|
+
)
|
|
241
|
+
@click.option(
|
|
242
|
+
"--standardized",
|
|
243
|
+
type=bool,
|
|
244
|
+
is_flag=True,
|
|
245
|
+
help="Should we test with a tabmat.StandardizedMatrix?",
|
|
246
|
+
default=False,
|
|
247
|
+
)
|
|
248
|
+
def run_all_benchmarks(
|
|
249
|
+
operation_name: str,
|
|
250
|
+
matrix_name: str,
|
|
251
|
+
dense: list,
|
|
252
|
+
sparse: list,
|
|
253
|
+
one_cat: list,
|
|
254
|
+
two_cat: list,
|
|
255
|
+
dense_cat: list,
|
|
256
|
+
bench_memory: bool,
|
|
257
|
+
n_iterations: int,
|
|
258
|
+
include_baseline: bool,
|
|
259
|
+
standardized: bool,
|
|
260
|
+
):
|
|
261
|
+
"""
|
|
262
|
+
Usage examples.
|
|
263
|
+
|
|
264
|
+
python benchmark/main.py --operation_name matvec,transpose-matvec --matrix_name sparse --include_baseline\n
|
|
265
|
+
operation storage memory time
|
|
266
|
+
|
|
267
|
+
0 matvec scipy.sparse csc 0 0.00129819\n
|
|
268
|
+
1 matvec scipy.sparse csr 0 0.00266385\n
|
|
269
|
+
2 matvec tabmat 0 0.00199628\n
|
|
270
|
+
3 transpose-matvec scipy.sparse csc 0 0.000838518\n
|
|
271
|
+
4 transpose-matvec scipy.sparse csr 0 0.00239468\n
|
|
272
|
+
5 transpose-matvec tabmat 0 0.000296116\n
|
|
273
|
+
|
|
274
|
+
python benchmark/main.py --operation_name sandwich --matrix_name dense_cat --bench_memory\n
|
|
275
|
+
|
|
276
|
+
operation storage memory time\n
|
|
277
|
+
0 sandwich tabmat 52244505 0.159682\n
|
|
278
|
+
|
|
279
|
+
python benchmark/main.py --operation_name matvec --matrix_name custom --sparse 3e6 1 --sparse 3e6 10 --dense 10 10\n
|
|
280
|
+
operation storage memory time design \n
|
|
281
|
+
0 matvec tabmat 0 0.000006 dense, #rows:10, #cols:10 \n
|
|
282
|
+
operation storage memory time design \n
|
|
283
|
+
0 matvec tabmat 0 0.046355 sparse, #rows:3000000, #cols:1 \n
|
|
284
|
+
operation storage memory time design \n
|
|
285
|
+
0 matvec tabmat 0 0.048141 sparse, #rows:3000000, #cols:10\n
|
|
286
|
+
""" # noqa
|
|
287
|
+
if n_iterations is None:
|
|
288
|
+
if bench_memory:
|
|
289
|
+
n_iterations = 1
|
|
290
|
+
else:
|
|
291
|
+
n_iterations = 100
|
|
292
|
+
|
|
293
|
+
if operation_name is None:
|
|
294
|
+
ops_to_run = list(ops.keys())
|
|
295
|
+
else:
|
|
296
|
+
ops_to_run = get_comma_sep_names(operation_name)
|
|
297
|
+
|
|
298
|
+
all_benchmark_matrices = get_all_benchmark_matrices()
|
|
299
|
+
|
|
300
|
+
benchmark_matrices = {}
|
|
301
|
+
if matrix_name is None:
|
|
302
|
+
for k in all_benchmark_matrices.keys():
|
|
303
|
+
with open(f"benchmark/data/{k}_data.pkl", "rb") as f:
|
|
304
|
+
benchmark_matrices[k] = pickle.load(f)
|
|
305
|
+
|
|
306
|
+
elif "custom" in matrix_name:
|
|
307
|
+
if dense:
|
|
308
|
+
for params in dense:
|
|
309
|
+
n_rows, n_cols = (int(float(x)) for x in params)
|
|
310
|
+
benchmark_matrices[f"dense, #rows:{n_rows}, #cols:{n_cols}"] = (
|
|
311
|
+
make_dense_matrices(n_rows, n_cols)
|
|
312
|
+
)
|
|
313
|
+
if sparse:
|
|
314
|
+
for params in sparse:
|
|
315
|
+
n_rows, n_cols = (int(float(x)) for x in params)
|
|
316
|
+
benchmark_matrices[f"sparse, #rows:{n_rows}, #cols:{n_cols}"] = (
|
|
317
|
+
make_sparse_matrices(n_rows, n_cols)
|
|
318
|
+
)
|
|
319
|
+
if one_cat:
|
|
320
|
+
for params in one_cat:
|
|
321
|
+
n_rows, n_cats = (int(float(x)) for x in params)
|
|
322
|
+
benchmark_matrices[f"one_cat, #rows:{n_rows}, #cats:{n_cats}"] = (
|
|
323
|
+
make_cat_matrix_all_formats(n_rows, n_cats)
|
|
324
|
+
)
|
|
325
|
+
if two_cat:
|
|
326
|
+
for params in two_cat:
|
|
327
|
+
n_rows, n_cat_cols_1, n_cat_cols_2 = (int(float(x)) for x in params)
|
|
328
|
+
benchmark_matrices[
|
|
329
|
+
f"two_cat #rows:{n_rows}, #cats_1:{n_cat_cols_1}, "
|
|
330
|
+
f"#cats_2:{n_cat_cols_2}"
|
|
331
|
+
] = make_cat_matrices(n_rows, n_cat_cols_1, n_cat_cols_2)
|
|
332
|
+
if dense_cat:
|
|
333
|
+
for params in dense_cat:
|
|
334
|
+
n_rows, n_dense_cols, n_cat_cols_1, n_cat_cols_2 = (
|
|
335
|
+
int(float(x)) for x in params
|
|
336
|
+
)
|
|
337
|
+
benchmark_matrices[
|
|
338
|
+
f"dense_cat #rows:{n_rows}, #dense:{n_dense_cols}, "
|
|
339
|
+
f" cats_1:{n_cat_cols_1}, #cats_2:{n_cat_cols_2}"
|
|
340
|
+
] = make_dense_cat_matrices(
|
|
341
|
+
n_rows, n_dense_cols, n_cat_cols_1, n_cat_cols_2
|
|
342
|
+
)
|
|
343
|
+
else:
|
|
344
|
+
for k in get_comma_sep_names(matrix_name):
|
|
345
|
+
with open(f"benchmark/data/{k}_data.pkl", "rb") as f:
|
|
346
|
+
benchmark_matrices[k] = pickle.load(f)
|
|
347
|
+
|
|
348
|
+
for name, matrices in benchmark_matrices.items():
|
|
349
|
+
time_bench = run_one_benchmark_set(
|
|
350
|
+
matrices,
|
|
351
|
+
include_baseline,
|
|
352
|
+
name,
|
|
353
|
+
standardized,
|
|
354
|
+
ops_to_run,
|
|
355
|
+
n_iterations,
|
|
356
|
+
False,
|
|
357
|
+
)
|
|
358
|
+
|
|
359
|
+
if bench_memory:
|
|
360
|
+
memory_bench = run_one_benchmark_set(
|
|
361
|
+
matrices,
|
|
362
|
+
include_baseline,
|
|
363
|
+
name,
|
|
364
|
+
standardized,
|
|
365
|
+
ops_to_run,
|
|
366
|
+
1,
|
|
367
|
+
True,
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
full_bench = pd.merge(
|
|
371
|
+
memory_bench[["operation", "storage", "memory"]],
|
|
372
|
+
time_bench[["operation", "storage", "time", "design"]],
|
|
373
|
+
on=["operation", "storage"],
|
|
374
|
+
)
|
|
375
|
+
else:
|
|
376
|
+
full_bench = time_bench[["operation", "storage", "time", "design"]]
|
|
377
|
+
print(full_bench)
|
|
378
|
+
|
|
379
|
+
full_bench.to_csv(f"benchmark/data/{name}_bench.csv", index=False)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
if __name__ == "__main__":
|
|
383
|
+
run_all_benchmarks()
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import time
|
|
2
|
+
import tracemalloc
|
|
3
|
+
from threading import Thread
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class MemoryPoller:
|
|
7
|
+
"""
|
|
8
|
+
Example usage.
|
|
9
|
+
|
|
10
|
+
with MemoryPoller() as mp:
|
|
11
|
+
do some stuff here
|
|
12
|
+
print('initial memory usage', mp.initial_memory)
|
|
13
|
+
print('max memory usage', mp.max_memory)
|
|
14
|
+
excess_memory_used = mp.max_memory - mp.initial_memory
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def _poll_max_memory_usage(self):
|
|
18
|
+
while not self.stop_polling:
|
|
19
|
+
self.snapshots.append(tracemalloc.take_snapshot())
|
|
20
|
+
time.sleep(1e-3)
|
|
21
|
+
|
|
22
|
+
def __enter__(self):
|
|
23
|
+
tracemalloc.start()
|
|
24
|
+
self.stop_polling = False
|
|
25
|
+
self.snapshots = [tracemalloc.take_snapshot()]
|
|
26
|
+
self.t = Thread(target=self._poll_max_memory_usage)
|
|
27
|
+
self.t.start()
|
|
28
|
+
return self
|
|
29
|
+
|
|
30
|
+
def __exit__(self, *excargs):
|
|
31
|
+
self.stop_polling = True
|
|
32
|
+
self.t.join()
|
|
33
|
+
self.final_usage, self.peak_usage = tracemalloc.get_traced_memory()
|
|
34
|
+
tracemalloc.stop()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def track_peak_mem(f, *args, **kwargs):
|
|
38
|
+
"""Track peak memory. Used in benchmarks to track memory used during matrix
|
|
39
|
+
operations.
|
|
40
|
+
"""
|
|
41
|
+
with MemoryPoller() as mp:
|
|
42
|
+
f(*args, **kwargs)
|
|
43
|
+
for s in mp.snapshots:
|
|
44
|
+
top_stats = s.statistics("lineno")
|
|
45
|
+
print("[ Top 4 ]")
|
|
46
|
+
for stat in top_stats[:4]:
|
|
47
|
+
print(stat)
|
|
48
|
+
return mp.peak_usage
|