/ concept-collection / benchcompress
Sign in
concept-collection / benchcompress
monitor
Jeremy Magland <jmagland@flatironinstitute.org> committed commit 3a8e918a59c4 parent 04ad0dc Browse files
4 changed files+383−0
benchcompress/src/benchcompress/datasets/seismic/explore_seismic.pymodified+67−0View file
@@ -59,3 +59,70 @@ plt.plot(X[:, 1])
5959 plt.figure(figsize=(10, 5))
6060 plt.plot(X[:, 1000])
6161 # %%
62+# Extract the exponent part of the float32 array X as uint8
63+X_exponent = ((X.view(np.uint32) >> 23) & 0xFF).astype(np.uint8)
64+print(X_exponent)
65+
66+# %%
67+import simple_ans
68+
69+a = simple_ans.ans_encode(X_exponent.ravel())
70+# %%
71+vals, counts = np.unique(X.ravel(), return_counts=True)
72+print(len(vals))
73+print(len(X.ravel()))
74+# plt.figure(figsize=(10, 5))
75+# plt.plot(vals, '.')
76+
77+# %%
78+v = len(a.bitstream) + a.symbol_counts.nbytes + a.symbol_values.nbytes
79+compression_ratio = len(X_exponent.tobytes()) / v
80+print(compression_ratio)
81+
82+print(4 / (v / X_exponent.size + 3))
83+
84+steps = np.exp(np.arange(20))
85+tests = []
86+for step in steps:
87+ X_quantized = np.round(X / step).astype(np.int32)
88+ resid = X / step - X_quantized
89+ resid_delta = np.diff(np.diff(resid.ravel()))
90+ v = np.median(np.abs(resid_delta))
91+ tests.append(v)
92+
93+plt.figure(figsize=(10, 5))
94+plt.plot(steps, tests)
95+plt.semilogx()
96+
97+# %%
98+q_step = 10000
99+X_quantized = np.round(X / q_step).astype(np.int32)
100+
101+# %%
102+plt.figure(figsize=(10, 5))
103+plt.plot(X_quantized[0])
104+plt.figure(figsize=(10, 5))
105+plt.plot(X[0])
106+# %%
107+import simple_ans
108+
109+a = simple_ans.ans_encode(X_quantized.ravel())
110+v = len(a.bitstream) + a.symbol_counts.nbytes + a.symbol_values.nbytes
111+print(len(X.tobytes()) / v)
112+# %%
113+import zstandard as zstd
114+
115+cctx = zstd.ZstdCompressor(level=13)
116+compressed = cctx.compress(X_quantized.tobytes())
117+v_zstd = len(compressed)
118+print(len(X.tobytes()) / v_zstd)
119+# %%
120+X_quantized_diff = np.diff(X_quantized.ravel())
121+a = simple_ans.ans_encode(X_quantized_diff)
122+v = len(a.bitstream) + a.symbol_counts.nbytes + a.symbol_values.nbytes
123+print(len(X.tobytes()) / v)
124+# %%
125+compressed = cctx.compress(X_quantized_diff.tobytes())
126+v_zstd = len(compressed)
127+print(len(X.tobytes()) / v_zstd)
128+# %%
benchcompress/src/benchcompress/run_benchmarks.pymodified+67−0View file
@@ -2,6 +2,7 @@ import time
22 import json
33 import os
44 from typing import Dict, Any, Tuple, List, Optional
5+from datetime import datetime
56 import numpy as np
67 from statistics import median
78 from .algorithms import algorithms
@@ -56,6 +57,39 @@ def is_compatible(algorithm_tags: List[str], dataset_tags: List[str]) -> bool:
5657 return True
5758
5859
60+def upload_benchmark_status(
61+ memobin_api_key: str,
62+ current_dataset: str,
63+ current_algorithm: str,
64+ completed_benchmarks: List[Dict[str, Any]],
65+ total_benchmarks: int,
66+ start_time: float,
67+) -> None:
68+ """Upload current benchmark status to memobin.
69+
70+ Args:
71+ memobin_api_key: API key for memobin authentication
72+ current_dataset: Name of the current dataset being processed
73+ current_algorithm: Name of the current algorithm being tested
74+ completed_benchmarks: List of completed benchmark results
75+ total_benchmarks: Total number of benchmarks to run
76+ start_time: Timestamp when the benchmark run started
77+ """
78+ status = {
79+ "current_dataset": current_dataset,
80+ "current_algorithm": current_algorithm,
81+ "completed_count": len(completed_benchmarks),
82+ "total_count": total_benchmarks,
83+ "progress_percentage": (len(completed_benchmarks) / total_benchmarks) * 100,
84+ "elapsed_time": time.time() - start_time,
85+ "last_update": datetime.now().isoformat(),
86+ "completed_benchmarks": completed_benchmarks,
87+ }
88+
89+ status_url = "https://tempory.net/f/memobin/benchmark_status/current.json"
90+ upload_to_memobin(status, status_url, memobin_api_key)
91+
92+
5993 def run_benchmarks(
6094 cache_dir: str = ".benchmark_cache",
6195 verbose: bool = True,
@@ -83,6 +117,8 @@ def run_benchmarks(
83117
84118 os.makedirs(cache_dir, exist_ok=True)
85119
120+ start_time = time.time()
121+ last_status_upload = 0 # Track last status upload time
86122 results = []
87123 print("\nRunning benchmarks for all dataset-algorithm combinations...")
88124
@@ -92,7 +128,18 @@ def run_benchmarks(
92128 selected_algorithms if selected_algorithms is not None else algorithms
93129 )
94130
131+ # Calculate total number of benchmarks
132+ total_benchmarks = sum(
133+ 1
134+ for dataset in datasets_to_run
135+ for algorithm in algorithms_to_run
136+ if is_compatible(algorithm.get("tags", []), dataset.get("tags", []))
137+ )
138+
95139 # Run benchmarks for each dataset and algorithm combination
140+ memobin_api_key = os.environ.get("MEMOBIN_API_KEY")
141+ upload_enabled = os.environ.get("UPLOAD_TO_MEMOBIN") == "1"
142+
96143 for dataset in datasets_to_run:
97144 dataset_tags = dataset.get("tags", [])
98145 print(f"\n*** Dataset: {dataset['name']} (tags: {dataset_tags}) ***")
@@ -114,6 +161,26 @@ def run_benchmarks(
114161
115162 print(f"\nTesting algorithm: {alg_name} (tags: {alg_tags})")
116163
164+ # Upload current status to memobin if enabled (once per minute)
165+ current_time = time.time()
166+ if (
167+ memobin_api_key
168+ and upload_enabled
169+ and (current_time - last_status_upload >= 60)
170+ ): # Check if 60 seconds have passed
171+ try:
172+ upload_benchmark_status(
173+ memobin_api_key,
174+ dataset["name"],
175+ alg_name,
176+ results,
177+ total_benchmarks,
178+ start_time,
179+ )
180+ last_status_upload = current_time # Update last upload time
181+ except Exception as e:
182+ print(f" Warning: Failed to upload status to memobin: {str(e)}")
183+
117184 # Check if we can use cached result (unless force flag is set)
118185 test_dir = os.path.join(cache_dir, dataset["name"], alg_name)
119186 metadata_file = os.path.join(test_dir, "metadata.json")
web-ui/src/App.tsxmodified+12−0View file
@@ -6,6 +6,7 @@ import "./components/AppHeader.css";
66 import Home from "./pages/Home";
77 import About from "./pages/About";
88 import Paper from "./pages/Paper";
9+import Monitor from "./pages/Monitor";
910 import { BenchmarkData } from "./types";
1011
1112 function App() {
@@ -123,6 +124,16 @@ function App() {
123124 >
124125 Paper
125126 </Link>
127+ <Link
128+ to="/monitor"
129+ style={{
130+ color: "#0066cc",
131+ textDecoration: "none",
132+ fontWeight: "500",
133+ }}
134+ >
135+ Monitor
136+ </Link>
126137 <Link
127138 to="/about"
128139 style={{
@@ -162,6 +173,7 @@ function App() {
162173 />
163174 <Route path="/about" element={<About />} />
164175 <Route path="/paper" element={<Paper />} />
176+ <Route path="/monitor" element={<Monitor />} />
165177 </Routes>
166178 )}
167179 </main>
web-ui/src/pages/Monitor.tsxadded+237−0View file
@@ -0,0 +1,237 @@
1+import { useEffect, useState } from "react";
2+import axios from "axios";
3+
4+interface BenchmarkStatus {
5+ current_dataset: string;
6+ current_algorithm: string;
7+ completed_count: number;
8+ total_count: number;
9+ progress_percentage: number;
10+ elapsed_time: number;
11+ last_update: string;
12+ completed_benchmarks: Array<{
13+ dataset: string;
14+ algorithm: string;
15+ compression_ratio: number;
16+ encode_time: number;
17+ decode_time: number;
18+ }>;
19+}
20+
21+export default function Monitor() {
22+ const [status, setStatus] = useState<BenchmarkStatus | null>(null);
23+ const [error, setError] = useState<string | null>(null);
24+ const [loading, setLoading] = useState(true);
25+
26+ useEffect(() => {
27+ const fetchStatus = async () => {
28+ try {
29+ const response = await axios.get(
30+ "https://tempory.net/f/memobin/benchmark_status/current.json",
31+ );
32+ setStatus(response.data);
33+ setError(null);
34+ } catch (error) {
35+ const message =
36+ error instanceof Error ? error.message : "Failed to fetch status";
37+ setError(message);
38+ console.error("Error fetching benchmark status:", error);
39+ } finally {
40+ setLoading(false);
41+ }
42+ };
43+
44+ // Fetch immediately and then every 30 seconds
45+ fetchStatus();
46+ const interval = setInterval(fetchStatus, 30000);
47+
48+ return () => clearInterval(interval);
49+ }, []);
50+
51+ if (loading) {
52+ return <div>Loading benchmark status...</div>;
53+ }
54+
55+ if (error) {
56+ return <div>Error: {error}</div>;
57+ }
58+
59+ if (!status) {
60+ return <div>No active benchmark run found.</div>;
61+ }
62+
63+ const formatTime = (seconds: number) => {
64+ const hours = Math.floor(seconds / 3600);
65+ const minutes = Math.floor((seconds % 3600) / 60);
66+ const remainingSeconds = Math.floor(seconds % 60);
67+ return `${hours}h ${minutes}m ${remainingSeconds}s`;
68+ };
69+
70+ return (
71+ <div style={{ padding: "20px" }}>
72+ <h1>Benchmark Progress</h1>
73+
74+ <div style={{ marginBottom: "20px" }}>
75+ <h2>Current Status</h2>
76+ <div
77+ style={{
78+ border: "1px solid #eee",
79+ padding: "20px",
80+ borderRadius: "8px",
81+ backgroundColor: "#f9f9f9",
82+ }}
83+ >
84+ <p>
85+ <strong>Current Dataset:</strong> {status.current_dataset}
86+ </p>
87+ <p>
88+ <strong>Current Algorithm:</strong> {status.current_algorithm}
89+ </p>
90+ <p>
91+ <strong>Progress:</strong> {status.completed_count} /{" "}
92+ {status.total_count} ({status.progress_percentage.toFixed(1)}%)
93+ </p>
94+ <p>
95+ <strong>Elapsed Time:</strong> {formatTime(status.elapsed_time)}
96+ </p>
97+ <p>
98+ <strong>Last Update:</strong>{" "}
99+ {new Date(status.last_update).toLocaleString()}
100+ </p>
101+
102+ <div style={{ marginTop: "10px" }}>
103+ <div
104+ style={{
105+ width: "100%",
106+ height: "20px",
107+ backgroundColor: "#eee",
108+ borderRadius: "10px",
109+ overflow: "hidden",
110+ }}
111+ >
112+ <div
113+ style={{
114+ width: `${status.progress_percentage}%`,
115+ height: "100%",
116+ backgroundColor: "#4CAF50",
117+ transition: "width 0.5s ease-in-out",
118+ }}
119+ />
120+ </div>
121+ </div>
122+ </div>
123+ </div>
124+
125+ <div>
126+ <h2>Completed Benchmarks</h2>
127+ <div style={{ overflowX: "auto" }}>
128+ <table
129+ style={{
130+ width: "100%",
131+ borderCollapse: "collapse",
132+ marginTop: "10px",
133+ }}
134+ >
135+ <thead>
136+ <tr style={{ backgroundColor: "#f5f5f5" }}>
137+ <th
138+ style={{
139+ padding: "12px",
140+ textAlign: "left",
141+ borderBottom: "2px solid #ddd",
142+ }}
143+ >
144+ Dataset
145+ </th>
146+ <th
147+ style={{
148+ padding: "12px",
149+ textAlign: "left",
150+ borderBottom: "2px solid #ddd",
151+ }}
152+ >
153+ Algorithm
154+ </th>
155+ <th
156+ style={{
157+ padding: "12px",
158+ textAlign: "right",
159+ borderBottom: "2px solid #ddd",
160+ }}
161+ >
162+ Compression Ratio
163+ </th>
164+ <th
165+ style={{
166+ padding: "12px",
167+ textAlign: "right",
168+ borderBottom: "2px solid #ddd",
169+ }}
170+ >
171+ Encode Time (ms)
172+ </th>
173+ <th
174+ style={{
175+ padding: "12px",
176+ textAlign: "right",
177+ borderBottom: "2px solid #ddd",
178+ }}
179+ >
180+ Decode Time (ms)
181+ </th>
182+ </tr>
183+ </thead>
184+ <tbody>
185+ {status.completed_benchmarks.map((benchmark, index) => (
186+ <tr
187+ key={index}
188+ style={{
189+ backgroundColor: index % 2 === 0 ? "white" : "#fafafa",
190+ }}
191+ >
192+ <td
193+ style={{ padding: "12px", borderBottom: "1px solid #ddd" }}
194+ >
195+ {benchmark.dataset}
196+ </td>
197+ <td
198+ style={{ padding: "12px", borderBottom: "1px solid #ddd" }}
199+ >
200+ {benchmark.algorithm}
201+ </td>
202+ <td
203+ style={{
204+ padding: "12px",
205+ textAlign: "right",
206+ borderBottom: "1px solid #ddd",
207+ }}
208+ >
209+ {benchmark.compression_ratio.toFixed(2)}x
210+ </td>
211+ <td
212+ style={{
213+ padding: "12px",
214+ textAlign: "right",
215+ borderBottom: "1px solid #ddd",
216+ }}
217+ >
218+ {(benchmark.encode_time * 1000).toFixed(2)}
219+ </td>
220+ <td
221+ style={{
222+ padding: "12px",
223+ textAlign: "right",
224+ borderBottom: "1px solid #ddd",
225+ }}
226+ >
227+ {(benchmark.decode_time * 1000).toFixed(2)}
228+ </td>
229+ </tr>
230+ ))}
231+ </tbody>
232+ </table>
233+ </div>
234+ </div>
235+ </div>
236+ );
237+}
moveopenescclose