fredmo commited on
Commit
5e23c4d
·
verified ·
1 Parent(s): d7df83d

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +558 -492
index.html CHANGED
@@ -1,583 +1,649 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <meta charset="UTF-8">
5
- <meta author="fredmo">
6
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
- <title>GPU Memory Configurator</title>
8
- <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
9
- <script>
10
- document.addEventListener('DOMContentLoaded', () => {
11
 
12
  // --- Data Definitions ---
13
- const gpuData = { /* ... Keep as is ... */
14
- 'L4': { name: 'L4', capacity: 24 },
15
- 'A100-40': { name: 'A100 40GB', capacity: 40 },
16
- 'A100-80': { name: 'A100 80GB', capacity: 80 },
17
- 'H100': { name: 'H100 80GB', capacity: 80 }
18
  };
19
 
20
- const modelData = { /* ... Remove promptBaseSize ... */
21
- 'gemma-1b': { name: 'Gemma 3 1B', baseModelSize: 3 }, // Removed kvCacheFactor, handled by utilization now
22
- 'gemma-4b': { name: 'Gemma 3 4B', baseModelSize: 8 },
23
- 'gemma-12b': { name: 'Gemma 3 12B', baseModelSize: 20 },
24
- 'gemma-27b': { name: 'Gemma 3 27B', baseModelSize: 45 }
25
  };
26
 
27
- const quantizationData = { /* ... Keep as is ... */
28
- 'fp32': { name: 'FP32', modifier: 1.0 },
29
- 'bf16': { name: 'BF16', modifier: 0.5 },
30
- 'int4': { name: 'INT4', modifier: 0.25 }
31
  };
32
 
33
- // NEW: GPU Memory Utilization Data
34
  const gpuMemoryUtilizationData = {
35
- 'util-0.5': { name: '50% KV Util', factor: 0.5 },
36
- 'util-0.7': { name: '70% KV Util', factor: 0.7 },
37
- 'util-0.9': { name: '90% KV Util', factor: 0.9 }
38
  };
39
 
40
  // --- State Variables ---
41
- let selectedGpuId = 'L4';
42
  let selectedModelId = 'gemma-1b';
43
  let selectedQuantizationId = 'fp32';
44
- let selectedUtilizationId = 'util-0.5'; // Default to 50%
45
 
46
  // --- DOM References ---
47
  const gpuSelectorDiv = document.getElementById('gpu-selector');
48
  const modelSelectorDiv = document.getElementById('model-selector');
49
  const quantizationSelectorDiv = document.getElementById('quantization-selector');
50
- const utilizationSelectorDiv = document.getElementById('utilization-selector'); // New
51
 
52
  const gpuCapacityLabel = document.getElementById('gpu-capacity-label');
53
  const visualizationArea = document.getElementById('gpu-visualization-area');
54
- // Removed promptBar reference
55
  const modelBar = document.getElementById('model-bar');
56
  const kvcacheBar = document.getElementById('kvcache-bar');
57
 
58
- // Removed promptUsageSpan reference
59
  const modelUsageSpan = document.getElementById('model-usage');
60
  const kvcacheUsageSpan = document.getElementById('kvcache-usage');
61
- const kvcachePromptInfoSpan = document.getElementById('kvcache-prompt-info'); // New span for info
62
  const totalUsageSpan = document.getElementById('total-usage');
63
  const fitStatusSpan = document.getElementById('fit-status');
64
 
65
  // --- Initialization ---
66
  function initializeControls() {
67
- createButtons(gpuData, gpuSelectorDiv, handleGpuSelect, selectedGpuId);
68
- createButtons(modelData, modelSelectorDiv, handleModelSelect, selectedModelId);
69
- createButtons(quantizationData, quantizationSelectorDiv, handleQuantizationSelect, selectedQuantizationId);
70
- createButtons(gpuMemoryUtilizationData, utilizationSelectorDiv, handleUtilizationSelect, selectedUtilizationId); // Create Util buttons
71
 
72
- updateCalculationAndDisplay(); // Initial calculation
73
  }
74
 
75
  function createButtons(data, container, clickHandler, activeId) {
76
- // Clear existing buttons first
77
- const existingButtons = container.querySelectorAll('button');
78
- existingButtons.forEach(btn => btn.remove());
79
-
80
- Object.keys(data).forEach(id => {
81
- const item = data[id];
82
- const button = document.createElement('button');
83
- button.textContent = item.name;
84
- button.dataset.id = id;
85
- button.addEventListener('click', () => clickHandler(id, container));
86
- if (id === activeId) {
87
- button.classList.add('active');
88
- }
89
- container.appendChild(button);
90
- });
91
  }
92
 
93
  // --- Event Handlers ---
94
  function handleGpuSelect(id, container) {
95
- selectedGpuId = id;
96
- updateActiveButton(container, id);
97
- updateCalculationAndDisplay();
98
  }
99
 
100
  function handleModelSelect(id, container) {
101
- selectedModelId = id;
102
- updateActiveButton(container, id);
103
- updateCalculationAndDisplay();
104
  }
105
 
106
  function handleQuantizationSelect(id, container) {
107
- selectedQuantizationId = id;
108
- updateActiveButton(container, id);
109
- updateCalculationAndDisplay();
110
  }
111
 
112
- function handleUtilizationSelect(id, container) { // New handler
113
- selectedUtilizationId = id;
114
- updateActiveButton(container, id);
115
- updateCalculationAndDisplay();
116
  }
117
 
118
  function updateActiveButton(container, activeId) {
119
- const buttons = container.querySelectorAll('button');
120
- buttons.forEach(button => {
121
- button.classList.toggle('active', button.dataset.id === activeId);
122
- });
123
  }
124
 
125
  // --- Calculation Logic ---
126
  function calculateSizes() {
127
- const gpu = gpuData[selectedGpuId];
128
- const model = modelData[selectedModelId];
129
- const quantization = quantizationData[selectedQuantizationId];
130
- const utilization = gpuMemoryUtilizationData[selectedUtilizationId]; // Get utilization info
131
-
132
- const quantModifier = quantization.modifier;
133
-
134
- // 1. Calculate Model Size (quantized)
135
- const modelSize = Math.max(1, Math.ceil(model.baseModelSize * quantModifier));
136
-
137
- // 2. Calculate Remaining Space
138
- const remainingSpace = gpu.capacity - modelSize;
139
-
140
- // 3. Calculate KV Cache Size based on utilization of *remaining* space
141
- let kvCacheSize = 0;
142
- if (remainingSpace > 0) {
143
- // Use floor to ensure KV Cache doesn't exceed the allocated percentage
144
- kvCacheSize = Math.floor(remainingSpace * utilization.factor);
145
- kvCacheSize = Math.max(0, kvCacheSize); // Ensure it's not negative if factor is weirdly small
146
- }
147
- // No minimum of 1 for KV cache - it can be 0 if no space remains
148
-
149
- // 4. Calculate Total Size
150
- const totalSize = modelSize + kvCacheSize;
151
-
152
- return {
153
- gpuCapacity: gpu.capacity,
154
- // promptSize removed
155
- modelSize,
156
- kvCacheSize,
157
- totalSize,
158
- fits: totalSize <= gpu.capacity && modelSize <= gpu.capacity // Also check if model itself fits
159
- };
160
  }
161
 
162
  // --- Display Logic ---
163
  function updateCalculationAndDisplay() {
164
- const sizes = calculateSizes();
165
-
166
- // Update Text Details
167
- // promptUsageSpan removed
168
- modelUsageSpan.textContent = sizes.modelSize;
169
- kvcacheUsageSpan.textContent = sizes.kvCacheSize;
170
- kvcachePromptInfoSpan.textContent = sizes.kvCacheSize > 0 ? "(incl. prompts)" : ""; // Add info text
171
- totalUsageSpan.textContent = sizes.totalSize;
172
- gpuCapacityLabel.textContent = `${sizes.gpuCapacity} Blocks`;
173
-
174
- // Update Status Message
175
- if (sizes.modelSize > sizes.gpuCapacity) {
176
- fitStatusSpan.textContent = "Model alone exceeds GPU capacity!";
177
- fitStatusSpan.className = 'status-no-fit status-error'; // Add extra error class
178
- } else if (sizes.fits) {
179
- fitStatusSpan.textContent = "Fits!";
180
- fitStatusSpan.className = 'status-fits';
181
- } else {
182
- fitStatusSpan.textContent = "Does Not Fit (Model + KV Cache)!";
183
- fitStatusSpan.className = 'status-no-fit';
184
- }
185
- // Update visualization border based on overall fit status
186
- visualizationArea.style.borderColor = sizes.fits ? 'var(--border-glow-color)' : 'var(--error-color)';
187
-
188
-
189
- // Update Visual Bars
190
- updateVisualBars(sizes);
191
  }
192
 
193
  function updateVisualBars(sizes) {
194
- const { modelSize, kvCacheSize, gpuCapacity } = sizes;
195
 
196
- // Calculate percentages relative to GPU capacity
197
- const modelPercent = (modelSize / gpuCapacity) * 100;
198
- const kvCachePercent = (kvCacheSize / gpuCapacity) * 100;
199
 
200
- // Calculate bottom offsets for stacking (Model is now at the bottom)
201
- const kvCacheBottomPercent = modelPercent;
202
 
203
- // Apply styles - Cap percentages and handle potential overflow
204
- modelBar.style.height = `${Math.min(100, modelPercent)}%`;
205
- modelBar.style.bottom = `0%`;
206
 
207
- kvcacheBar.style.height = `${Math.min(100 - modelPercent, kvCachePercent)}%`; // Height is capped by remaining space
208
- kvcacheBar.style.bottom = `${Math.min(100, modelPercent)}%`; // Starts above the model bar
209
 
210
- // Update data-label attributes
211
- modelBar.dataset.label = `Model (${modelSize})`;
212
- // Clarify KV cache label
213
- kvcacheBar.dataset.label = `KV Cache (${kvCacheSize}) ${sizes.kvCacheSize > 0 ? '[:: will contains your batched Prompts]' : ''}`;
214
  }
215
 
216
  // --- Start the app ---
217
  initializeControls();
218
 
219
- }); // End DOMContentLoaded
220
- </script>
221
- <style>
222
- /* --- Keep all existing styles from the previous version --- */
223
- :root {
224
- --background-color: #1a0a2d; /* Deep purple/blue */
225
- --primary-color: #ff00ff; /* Magenta/Pink */
226
- --secondary-color: #00ffff; /* Cyan */
227
- --accent-color: #f8f8f8; /* Bright White/Off-white */
228
- --border-glow-color: #7f00ff; /* Purple Glow */
229
-
230
- --prompt-color: #00ff00; /* Bright Green */
231
- --model-color: var(--secondary-color); /* Cyan */
232
- --kvcache-color: #ffff00; /* Yellow */
233
-
234
- --gpu-area-bg: #0a0514; /* Very dark for GPU viz */
235
- --control-bg: rgba(58, 26, 90, 0.5); /* Darker purple for controls */
236
-
237
- --error-color: #ff4136; /* Red for errors/no fit */
238
- --success-color: #39cccc; /* Teal/Cyan for success/fit */
239
-
240
- --glow-primary: 0 0 5px var(--primary-color), 0 0 10px var(--primary-color), 0 0 15px var(--primary-color);
241
- --glow-secondary: 0 0 5px var(--secondary-color), 0 0 10px var(--secondary-color), 0 0 15px var(--secondary-color);
242
- --glow-border: 0 0 8px var(--border-glow-color), 0 0 15px var(--border-glow-color);
243
- --text-glow: 0 0 3px var(--accent-color), 0 0 5px var(--primary-color);
244
- }
245
-
246
- body {
247
- background-color: var(--background-color);
248
- color: var(--accent-color);
249
- font-family: 'Orbitron', sans-serif;
250
- margin: 0;
251
- padding: 20px;
252
- display: flex;
253
- justify-content: center;
254
- align-items: flex-start;
255
- min-height: 100vh;
256
- background-image:
 
 
 
 
 
 
 
 
 
 
 
 
257
  linear-gradient(rgba(26, 10, 45, 0.9), rgba(26, 10, 45, 0.9)),
258
  /* Grid overlay */
259
  linear-gradient(var(--border-glow-color) 1px, transparent 1px),
260
  linear-gradient(90deg, var(--border-glow-color) 1px, transparent 1px);
261
- background-size: 100% 100%, 40px 40px, 40px 40px; /* Adjust grid size */
262
- background-position: 0 0, -1px -1px, -1px -1px; /* Align grid */
263
- }
264
-
265
- #configurator-container {
266
- background-color: rgba(10, 5, 20, 0.85);
267
- border: 2px solid var(--border-glow-color);
268
- box-shadow: var(--glow-border);
269
- padding: 25px;
270
- border-radius: 10px;
271
- width: 95%;
272
- max-width: 1200px; /* Wider layout */
273
- text-align: center;
274
- }
275
-
276
- header h1 {
277
- color: var(--primary-color);
278
- text-shadow: var(--text-glow);
279
- margin-bottom: 5px;
280
- }
281
-
282
- header p {
283
- color: var(--secondary-color);
284
- margin-bottom: 25px;
285
- }
286
-
287
- #main-layout {
288
- display: flex;
289
- justify-content: space-between;
290
- gap: 25px; /* Space between columns */
291
- text-align: left;
292
- }
293
-
294
- /* Column Styling */
295
- #gpu-visualization-column {
296
- flex: 2; /* Takes more space */
297
- display: flex;
298
- flex-direction: column;
299
- }
300
-
301
- #spacer-column {
302
- flex: 0.1; /* Thin spacer */
303
- }
304
-
305
- #controls-column {
306
- flex: 1; /* Takes less space */
307
- display: flex;
308
- flex-direction: column;
309
- gap: 20px; /* Space between control groups */
310
- }
311
-
312
- /* Left Column: Visualization */
313
- #gpu-visualization-column h2, #controls-column h3 {
314
- color: var(--secondary-color);
315
- text-shadow: 0 0 5px var(--secondary-color);
316
- margin-top: 0;
317
- margin-bottom: 15px;
318
- border-bottom: 1px solid var(--secondary-color);
319
- padding-bottom: 5px;
320
- text-align: center;
321
- }
322
-
323
- #gpu-visualization-area {
324
- flex-grow: 1;
325
- background-color: var(--gpu-area-bg);
326
- border: 2px solid var(--border-glow-color);
327
- border-radius: 8px;
328
- position: relative; /* For absolute positioning of bars and grid */
329
- min-height: 400px; /* Ensure enough height */
330
- overflow: hidden; /* Hide overflow */
331
- box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
332
- }
333
-
334
- #gpu-grid-overlay {
335
- position: absolute;
336
- top: 0; left: 0; right: 0; bottom: 0;
337
- background-image:
338
- linear-gradient(rgba(127, 0, 255, 0.3) 1px, transparent 1px), /* Fainter grid lines */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
339
  linear-gradient(90deg, rgba(127, 0, 255, 0.3) 1px, transparent 1px);
340
- background-size: 10% 10%; /* 10x10 grid visually */
341
- pointer-events: none; /* Allow clicks through */
342
- z-index: 1;
343
- }
344
-
345
-
346
- #gpu-capacity-indicator {
347
- position: absolute;
348
- top: 5px;
349
- right: 10px;
350
- color: var(--accent-color);
351
- background: rgba(0,0,0,0.5);
352
- padding: 3px 8px;
353
- border-radius: 4px;
354
- font-size: 0.8em;
355
- z-index: 3; /* Above bars */
356
- }
357
-
358
- .usage-bar {
359
- position: absolute;
360
- bottom: 0;
361
- left: 0;
362
- width: 100%;
363
- height: 0; /* Initial height */
364
- transition: height 0.5s ease-out, bottom 0.5s ease-out; /* Animate changes */
365
- box-shadow: inset 0 2px 5px rgba(0,0,0,0.5);
366
- z-index: 2; /* Below capacity indicator */
367
- display: flex;
368
- align-items: flex-start; /* Label at the top */
369
- justify-content: center;
370
- overflow: hidden; /* Prevent label overflow */
371
- }
372
-
373
- .usage-bar::before { /* Add label inside the bar */
374
- content: attr(data-label);
375
- position: absolute;
376
- top: 5px; /* Position label near the top */
377
- left: 10px;
378
- color: #000; /* Black label for contrast */
379
- font-size: 0.9em;
380
- font-weight: bold;
381
- text-shadow: 0 0 2px rgba(255, 255, 255, 0.7);
382
- opacity: 0; /* Initially hidden */
383
- transition: opacity 0.3s ease-in 0.3s; /* Fade in after resize */
384
- }
385
-
386
- .usage-bar[style*="height: 0"]::before {
387
- opacity: 0 !important; /* Keep hidden if height is 0 */
388
- }
389
- .usage-bar:not([style*="height: 0"])::before {
390
- opacity: 1; /* Show if height is not 0 */
391
- }
392
-
393
-
394
- /*#prompt-bar { background-color: var(--prompt-color); }*/
395
- #model-bar { background-color: var(--model-color); }
396
- #kvcache-bar { background-color: var(--kvcache-color); }
397
-
398
-
399
- #usage-details {
400
- margin-top: 15px;
401
- padding: 10px;
402
- background-color: var(--control-bg);
403
- border: 1px dashed var(--secondary-color);
404
- border-radius: 5px;
405
- font-size: 0.9em;
406
- }
407
- #usage-details p { margin: 4px 0; }
408
- #usage-details span { font-weight: bold; color: var(--primary-color); }
409
- #usage-details .total-usage span { color: var(--accent-color); }
410
-
411
-
412
- /* Right Column: Controls */
413
- .control-group {
414
- background-color: var(--control-bg);
415
- border: 1px solid var(--primary-color);
416
- border-radius: 6px;
417
- padding: 15px;
418
- }
419
-
420
- .control-group h3 {
421
- border-bottom: none; /* Remove border from h3 inside group */
422
- margin-bottom: 10px;
423
- color: var(--primary-color);
424
- }
425
-
426
- .control-group button {
427
- display: block; /* Stack buttons vertically */
428
- width: 100%;
429
- background-color: var(--background-color);
430
- color: var(--accent-color);
431
- border: 1px solid var(--secondary-color);
432
- padding: 10px 15px;
433
- border-radius: 4px;
434
- font-family: 'Orbitron', sans-serif;
435
- cursor: pointer;
436
- margin-bottom: 8px;
437
- transition: background-color 0.3s, box-shadow 0.3s, border-color 0.3s;
438
- text-align: center;
439
- }
440
- .control-group button:last-child { margin-bottom: 0; }
441
-
442
- .control-group button:hover {
443
- background-color: var(--secondary-color);
444
- color: var(--background-color);
445
- border-color: var(--secondary-color);
446
- box-shadow: var(--glow-secondary);
447
- }
448
-
449
- .control-group button.active {
450
- background-color: var(--primary-color);
451
- color: var(--background-color);
452
- border-color: var(--primary-color);
453
- box-shadow: var(--glow-primary);
454
- font-weight: bold;
455
- }
456
-
457
- /* Status Message */
458
- #status-message {
459
- margin-top: auto; /* Push to bottom */
460
- padding: 15px;
461
- border: 1px dashed var(--border-glow-color);
462
- border-radius: 5px;
463
- background-color: rgba(0,0,0, 0.3);
464
- text-align: center;
465
- }
466
- #status-message p { margin: 0; }
467
- #status-message span { font-weight: bold; }
468
-
469
- #status-message .status-fits { color: var(--success-color); }
470
- #status-message .status-no-fit { color: var(--error-color); }
471
- #status-message .status-error { color: var(--error-color); font-weight: bold;}
472
-
473
- footer {
474
- margin-top: 30px;
475
- color: var(--secondary-color);
476
- font-size: 0.8em;
477
- opacity: 0.7;
478
- text-align: center;
479
- width: 100%;
480
- }
481
-
482
- /* --- Remove or Comment Out --- */
483
- /* #prompt-bar { background-color: var(--prompt-color); } */
484
-
485
- /* --- Add or Modify --- */
486
- #usage-details #kvcache-prompt-info {
487
- font-size: 0.85em;
488
- color: var(--secondary-color);
489
- opacity: 0.8;
490
- margin-left: 5px;
491
- }
492
-
493
- /* Adjust grid lines maybe? */
494
- #gpu-grid-overlay {
495
- background-image:
496
- linear-gradient(rgba(127, 0, 255, 0.2) 1px, transparent 1px), /* Make grid lines fainter */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
497
  linear-gradient(90deg, rgba(127, 0, 255, 0.2) 1px, transparent 1px);
498
- background-size: 10% 10%;
499
- /* ... keep rest */
500
- }
501
-
502
- /* Ensure label contrast is okay on yellow */
503
- #kvcache-bar::before {
504
- color: #333; /* Darker gray/black for yellow background */
505
- text-shadow: 0 0 2px rgba(255, 255, 255, 0.5);
506
- }
507
-
508
-
509
- /* Make sure control group spacing is okay */
510
- #controls-column {
511
- gap: 15px; /* Slightly reduce gap if needed */
512
- }
513
- </style>
 
 
514
  </head>
 
515
  <body>
516
- <div id="configurator-container">
517
-
518
- <header>
519
- <h1>GPU Memory Configurator</h1>
520
- <p>Select GPU, Model, Quantization & Utilization to estimate VRAM usage</p>
521
- </header>
522
-
523
- <div id="main-layout">
524
-
525
- <!-- Left Column: Visualization -->
526
- <div id="gpu-visualization-column">
527
- <h2>GPU Usage</h2>
528
- <div id="gpu-visualization-area">
529
- <div id="gpu-capacity-indicator">
530
- <span id="gpu-capacity-label">80 Blocks</span>
531
- </div>
532
- <!-- Removed Prompt Bar -->
533
- <div id="model-bar" class="usage-bar" data-label="Model"></div>
534
- <div id="kvcache-bar" class="usage-bar" data-label="KV Cache"></div>
535
- <div id="gpu-grid-overlay"></div>
536
- </div>
537
- <div id="usage-details">
538
- <!-- Removed Prompt Usage Line -->
539
- <p>Model: <span id="model-usage">0</span> Blocks</p>
540
- <p>KV Cache: <span id="kvcache-usage">0</span> Blocks <span id="kvcache-prompt-info"></span></p>
541
- <p class="total-usage">Total: <span id="total-usage">0</span> Blocks</p>
542
- </div>
543
- </div>
544
-
545
- <!-- Middle Column: Spacer -->
546
- <div id="spacer-column"></div>
547
-
548
- <!-- Right Column: Controls -->
549
- <div id="controls-column">
550
- <div class="control-group" id="gpu-selector">
551
- <h3>GPU</h3>
552
- <!-- Buttons will be generated by JS -->
553
- </div>
554
- <div class="control-group" id="model-selector">
555
- <h3>Model</h3>
556
- <!-- Buttons will be generated by JS -->
557
- </div>
558
- <div class="control-group" id="quantization-selector">
559
- <h3>Quantization</h3>
560
- <!-- Buttons will be generated by JS -->
561
- </div>
562
- <!-- New Utilization Selector -->
563
- <div class="control-group" id="utilization-selector">
564
- <h3>KV Cache Util</h3>
565
- <!-- Buttons will be generated by JS -->
566
- </div>
567
-
568
- <div id="status-message">
569
- <p>Status: <span id="fit-status">-</span></p>
570
- </div>
571
- </div>
572
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
573
  </div>
574
 
575
- <footer>
576
- Synthwave VRAM Estimator
577
- </footer>
 
578
 
579
  </div>
580
 
581
- <script src="script.js"></script>
 
 
 
 
 
 
582
  </body>
 
583
  </html>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
+ <meta charset="UTF-8">
5
+ <meta author="fredmo">
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
+ <title>GPU Memory Configurator</title>
8
+ <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&display=swap" rel="stylesheet">
9
+ <script>
10
+ document.addEventListener('DOMContentLoaded', () => {
11
 
12
  // --- Data Definitions ---
13
+ const gpuData = {
14
+ 'L4': { name: 'L4', capacity: 24 },
15
+ 'A100-40': { name: 'A100 40GB', capacity: 40 },
16
+ 'A100-80': { name: 'A100 80GB', capacity: 80 },
17
+ 'H100': { name: 'H100 80GB', capacity: 80 }
18
  };
19
 
20
+ const modelData = {
21
+ 'gemma-1b': { name: 'Gemma 3 1B', baseModelSize: 3 },
22
+ 'gemma-4b': { name: 'Gemma 3 4B', baseModelSize: 8 },
23
+ 'gemma-12b': { name: 'Gemma 3 12B', baseModelSize: 20 },
24
+ 'gemma-27b': { name: 'Gemma 3 27B', baseModelSize: 45 }
25
  };
26
 
27
+ const quantizationData = {
28
+ 'fp32': { name: 'FP32', modifier: 1.0 },
29
+ 'bf16': { name: 'BF16', modifier: 0.5 },
30
+ 'int4': { name: 'INT4', modifier: 0.25 }
31
  };
32
 
 
33
  const gpuMemoryUtilizationData = {
34
+ 'util-0.5': { name: '50% KV Util', factor: 0.5 },
35
+ 'util-0.7': { name: '70% KV Util', factor: 0.7 },
36
+ 'util-0.9': { name: '90% KV Util', factor: 0.9 }
37
  };
38
 
39
  // --- State Variables ---
40
+ let selectedGpuId = 'A100-80';
41
  let selectedModelId = 'gemma-1b';
42
  let selectedQuantizationId = 'fp32';
43
+ let selectedUtilizationId = 'util-0.7'; // Default to 70%
44
 
45
  // --- DOM References ---
46
  const gpuSelectorDiv = document.getElementById('gpu-selector');
47
  const modelSelectorDiv = document.getElementById('model-selector');
48
  const quantizationSelectorDiv = document.getElementById('quantization-selector');
49
+ const utilizationSelectorDiv = document.getElementById('utilization-selector');
50
 
51
  const gpuCapacityLabel = document.getElementById('gpu-capacity-label');
52
  const visualizationArea = document.getElementById('gpu-visualization-area');
 
53
  const modelBar = document.getElementById('model-bar');
54
  const kvcacheBar = document.getElementById('kvcache-bar');
55
 
 
56
  const modelUsageSpan = document.getElementById('model-usage');
57
  const kvcacheUsageSpan = document.getElementById('kvcache-usage');
58
+ const kvcachePromptInfoSpan = document.getElementById('kvcache-prompt-info');
59
  const totalUsageSpan = document.getElementById('total-usage');
60
  const fitStatusSpan = document.getElementById('fit-status');
61
 
62
  // --- Initialization ---
63
  function initializeControls() {
64
+ createButtons(gpuData, gpuSelectorDiv, handleGpuSelect, selectedGpuId);
65
+ createButtons(modelData, modelSelectorDiv, handleModelSelect, selectedModelId);
66
+ createButtons(quantizationData, quantizationSelectorDiv, handleQuantizationSelect, selectedQuantizationId);
67
+ createButtons(gpuMemoryUtilizationData, utilizationSelectorDiv, handleUtilizationSelect, selectedUtilizationId);
68
 
69
+ updateCalculationAndDisplay(); // Initial calculation
70
  }
71
 
72
  function createButtons(data, container, clickHandler, activeId) {
73
+ const existingButtons = container.querySelectorAll('button');
74
+ existingButtons.forEach(btn => btn.remove());
75
+
76
+ Object.keys(data).forEach(id => {
77
+ const item = data[id];
78
+ const button = document.createElement('button');
79
+ button.textContent = item.name;
80
+ button.dataset.id = id;
81
+ button.addEventListener('click', () => clickHandler(id, container));
82
+ if (id === activeId) {
83
+ button.classList.add('active');
84
+ }
85
+ container.appendChild(button);
86
+ });
 
87
  }
88
 
89
  // --- Event Handlers ---
90
  function handleGpuSelect(id, container) {
91
+ selectedGpuId = id;
92
+ updateActiveButton(container, id);
93
+ updateCalculationAndDisplay();
94
  }
95
 
96
  function handleModelSelect(id, container) {
97
+ selectedModelId = id;
98
+ updateActiveButton(container, id);
99
+ updateCalculationAndDisplay();
100
  }
101
 
102
  function handleQuantizationSelect(id, container) {
103
+ selectedQuantizationId = id;
104
+ updateActiveButton(container, id);
105
+ updateCalculationAndDisplay();
106
  }
107
 
108
+ function handleUtilizationSelect(id, container) {
109
+ selectedUtilizationId = id;
110
+ updateActiveButton(container, id);
111
+ updateCalculationAndDisplay();
112
  }
113
 
114
  function updateActiveButton(container, activeId) {
115
+ const buttons = container.querySelectorAll('button');
116
+ buttons.forEach(button => {
117
+ button.classList.toggle('active', button.dataset.id === activeId);
118
+ });
119
  }
120
 
121
  // --- Calculation Logic ---
122
  function calculateSizes() {
123
+ const gpu = gpuData[selectedGpuId];
124
+ const model = modelData[selectedModelId];
125
+ const quantization = quantizationData[selectedQuantizationId];
126
+ const utilization = gpuMemoryUtilizationData[selectedUtilizationId];
127
+
128
+ const quantModifier = quantization.modifier;
129
+
130
+ const modelSize = Math.max(1, Math.ceil(model.baseModelSize * quantModifier));
131
+ const remainingSpace = gpu.capacity - modelSize;
132
+
133
+ let kvCacheSize = 0;
134
+ if (remainingSpace > 0) {
135
+ kvCacheSize = Math.floor(remainingSpace * utilization.factor);
136
+ kvCacheSize = Math.max(0, kvCacheSize);
137
+ }
138
+
139
+ const totalSize = modelSize + kvCacheSize;
140
+
141
+ return {
142
+ gpuCapacity: gpu.capacity,
143
+ modelSize,
144
+ kvCacheSize,
145
+ totalSize,
146
+ fits: totalSize <= gpu.capacity && modelSize <= gpu.capacity
147
+ };
 
 
 
 
 
 
 
 
148
  }
149
 
150
  // --- Display Logic ---
151
  function updateCalculationAndDisplay() {
152
+ const sizes = calculateSizes();
153
+
154
+ modelUsageSpan.textContent = sizes.modelSize;
155
+ kvcacheUsageSpan.textContent = sizes.kvCacheSize;
156
+ kvcachePromptInfoSpan.textContent = sizes.kvCacheSize > 0 ? "(incl. prompts)" : "";
157
+ totalUsageSpan.textContent = sizes.totalSize;
158
+ gpuCapacityLabel.textContent = `${sizes.gpuCapacity} Blocks`;
159
+
160
+ if (sizes.modelSize > sizes.gpuCapacity) {
161
+ fitStatusSpan.textContent = "Model alone exceeds GPU capacity!";
162
+ fitStatusSpan.className = 'status-no-fit status-error';
163
+ } else if (sizes.fits) {
164
+ if(sizes.kvCacheSize === 0 ) {
165
+ fitStatusSpan.textContent = "You can't send prompts you don't have KV cache available when KV cache size is 0";
166
+ fitStatusSpan.className = 'status-no-fit status-error';
167
+ }
168
+ else{
169
+ fitStatusSpan.textContent = "Fits!";
170
+ fitStatusSpan.className = 'status-fits';
171
+ }
172
+ } else {
173
+ fitStatusSpan.textContent = "Does Not Fit (Model + KV Cache)!";
174
+ fitStatusSpan.className = 'status-no-fit';
175
+ }
176
+ visualizationArea.style.borderColor = sizes.fits ? 'var(--border-glow-color)' : 'var(--error-color)';
177
+
178
+ updateVisualBars(sizes);
179
  }
180
 
181
  function updateVisualBars(sizes) {
182
+ const { modelSize, kvCacheSize, gpuCapacity } = sizes;
183
 
184
+ const modelPercent = (modelSize / gpuCapacity) * 100;
185
+ const kvCachePercent = (kvCacheSize / gpuCapacity) * 100;
 
186
 
187
+ const kvCacheBottomPercent = modelPercent;
 
188
 
189
+ modelBar.style.height = `${Math.min(100, modelPercent)}%`;
190
+ modelBar.style.bottom = `0%`;
 
191
 
192
+ kvcacheBar.style.height = `${Math.min(100 - modelPercent, kvCachePercent)}%`;
193
+ kvcacheBar.style.bottom = `${Math.min(100, modelPercent)}%`;
194
 
195
+ modelBar.dataset.label = `Model ($)`;
196
+ kvcacheBar.dataset.label = `KV Cache ($) ${sizes.kvCacheSize > 0 ? '[+Prompts]' : ''}`;
 
 
197
  }
198
 
199
  // --- Start the app ---
200
  initializeControls();
201
 
202
+ }); // End DOMContentLoaded
203
+ </script>
204
+ <style>
205
+ /* --- Keep all existing styles from the previous version --- */
206
+ :root {
207
+ --background-color: #1a0a2d;
208
+ /* Deep purple/blue */
209
+ --primary-color: #ff00ff;
210
+ /* Magenta/Pink */
211
+ --secondary-color: #00ffff;
212
+ /* Cyan */
213
+ --accent-color: #f8f8f8;
214
+ /* Bright White/Off-white */
215
+ --border-glow-color: #7f00ff;
216
+ /* Purple Glow */
217
+
218
+ --prompt-color: #00ff00;
219
+ /* Bright Green */
220
+ --model-color: var(--secondary-color);
221
+ /* Cyan */
222
+ --kvcache-color: #ffff00;
223
+ /* Yellow */
224
+
225
+ --gpu-area-bg: #0a0514;
226
+ /* Very dark for GPU viz */
227
+ --control-bg: rgba(58, 26, 90, 0.5);
228
+ /* Darker purple for controls */
229
+
230
+ --error-color: #ff4136;
231
+ /* Red for errors/no fit */
232
+ --success-color: #39cccc;
233
+ /* Teal/Cyan for success/fit */
234
+
235
+ --glow-primary: 0 0 5px var(--primary-color), 0 0 10px var(--primary-color), 0 0 15px var(--primary-color);
236
+ --glow-secondary: 0 0 5px var(--secondary-color), 0 0 10px var(--secondary-color), 0 0 15px var(--secondary-color);
237
+ --glow-border: 0 0 8px var(--border-glow-color), 0 0 15px var(--border-glow-color);
238
+ --text-glow: 0 0 3px var(--accent-color), 0 0 5px var(--primary-color);
239
+ }
240
+
241
+ body {
242
+ background-color: var(--background-color);
243
+ color: var(--accent-color);
244
+ font-family: 'Orbitron', sans-serif;
245
+ margin: 0;
246
+ padding: 20px;
247
+ display: flex;
248
+ justify-content: center;
249
+ align-items: flex-start;
250
+ min-height: 100vh;
251
+ background-image:
252
  linear-gradient(rgba(26, 10, 45, 0.9), rgba(26, 10, 45, 0.9)),
253
  /* Grid overlay */
254
  linear-gradient(var(--border-glow-color) 1px, transparent 1px),
255
  linear-gradient(90deg, var(--border-glow-color) 1px, transparent 1px);
256
+ background-size: 100% 100%, 40px 40px, 40px 40px;
257
+ /* Adjust grid size */
258
+ background-position: 0 0, -1px -1px, -1px -1px;
259
+ /* Align grid */
260
+ }
261
+
262
+ #configurator-container {
263
+ background-color: rgba(10, 5, 20, 0.85);
264
+ border: 2px solid var(--border-glow-color);
265
+ box-shadow: var(--glow-border);
266
+ padding: 25px;
267
+ border-radius: 10px;
268
+ width: 95%;
269
+ max-width: 1200px;
270
+ /* Wider layout */
271
+ text-align: center;
272
+ }
273
+
274
+ header h1 {
275
+ color: var(--primary-color);
276
+ text-shadow: var(--text-glow);
277
+ margin-bottom: 5px;
278
+ }
279
+
280
+ header p {
281
+ color: var(--secondary-color);
282
+ margin-bottom: 25px;
283
+ }
284
+
285
+ #main-layout {
286
+ display: flex;
287
+ justify-content: space-between;
288
+ gap: 25px;
289
+ /* Space between columns */
290
+ text-align: left;
291
+ }
292
+
293
+ /* Column Styling */
294
+ #gpu-visualization-column {
295
+ flex: 2;
296
+ /* Takes more space */
297
+ display: flex;
298
+ flex-direction: column;
299
+ }
300
+
301
+ #spacer-column {
302
+ flex: 0.1;
303
+ /* Thin spacer */
304
+ }
305
+
306
+ #controls-column {
307
+ flex: 1;
308
+ /* Takes less space */
309
+ display: flex;
310
+ flex-direction: column;
311
+ gap: 20px;
312
+ /* Space between control groups */
313
+ }
314
+
315
+ /* Left Column: Visualization */
316
+ #gpu-visualization-column h2,
317
+ #controls-column h3 {
318
+ color: var(--secondary-color);
319
+ text-shadow: 0 0 5px var(--secondary-color);
320
+ margin-top: 0;
321
+ margin-bottom: 15px;
322
+ border-bottom: 1px solid var(--secondary-color);
323
+ padding-bottom: 5px;
324
+ text-align: center;
325
+ }
326
+
327
+ #gpu-visualization-area {
328
+ flex-grow: 1;
329
+ background-color: var(--gpu-area-bg);
330
+ border: 2px solid var(--border-glow-color);
331
+ border-radius: 8px;
332
+ position: relative;
333
+ /* For absolute positioning of bars and grid */
334
+ min-height: 400px;
335
+ /* Ensure enough height */
336
+ overflow: hidden;
337
+ /* Hide overflow */
338
+ box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.5);
339
+ }
340
+
341
+ #gpu-grid-overlay {
342
+ position: absolute;
343
+ top: 0;
344
+ left: 0;
345
+ right: 0;
346
+ bottom: 0;
347
+ background-image:
348
+ linear-gradient(rgba(127, 0, 255, 0.3) 1px, transparent 1px),
349
+ /* Fainter grid lines */
350
  linear-gradient(90deg, rgba(127, 0, 255, 0.3) 1px, transparent 1px);
351
+ background-size: 10% 10%;
352
+ /* 10x10 grid visually */
353
+ pointer-events: none;
354
+ /* Allow clicks through */
355
+ z-index: 1;
356
+ }
357
+
358
+
359
+ #gpu-capacity-indicator {
360
+ position: absolute;
361
+ top: 5px;
362
+ right: 10px;
363
+ color: var(--accent-color);
364
+ background: rgba(0, 0, 0, 0.5);
365
+ padding: 3px 8px;
366
+ border-radius: 4px;
367
+ font-size: 0.8em;
368
+ z-index: 3;
369
+ /* Above bars */
370
+ }
371
+
372
+ .usage-bar {
373
+ position: absolute;
374
+ bottom: 0;
375
+ left: 0;
376
+ width: 100%;
377
+ height: 0;
378
+ /* Initial height */
379
+ transition: height 0.5s ease-out, bottom 0.5s ease-out;
380
+ /* Animate changes */
381
+ box-shadow: inset 0 2px 5px rgba(0, 0, 0, 0.5);
382
+ z-index: 2;
383
+ /* Below capacity indicator */
384
+ display: flex;
385
+ align-items: flex-start;
386
+ /* Label at the top */
387
+ justify-content: center;
388
+ overflow: hidden;
389
+ /* Prevent label overflow */
390
+ }
391
+
392
+ .usage-bar::before {
393
+ /* Add label inside the bar */
394
+ content: attr(data-label);
395
+ position: absolute;
396
+ top: 5px;
397
+ /* Position label near the top */
398
+ left: 10px;
399
+ color: #000;
400
+ /* Black label for contrast */
401
+ font-size: 0.9em;
402
+ font-weight: bold;
403
+ text-shadow: 0 0 2px rgba(255, 255, 255, 0.7);
404
+ opacity: 0;
405
+ /* Initially hidden */
406
+ transition: opacity 0.3s ease-in 0.3s;
407
+ /* Fade in after resize */
408
+ }
409
+
410
+ .usage-bar[style*="height: 0"]::before {
411
+ opacity: 0 !important;
412
+ /* Keep hidden if height is 0 */
413
+ }
414
+
415
+ .usage-bar:not([style*="height: 0"])::before {
416
+ opacity: 1;
417
+ /* Show if height is not 0 */
418
+ }
419
+
420
+
421
+ /*#prompt-bar { background-color: var(--prompt-color); }*/
422
+ #model-bar {
423
+ background-color: var(--model-color);
424
+ }
425
+
426
+ #kvcache-bar {
427
+ background-color: var(--kvcache-color);
428
+ }
429
+
430
+
431
+ #usage-details {
432
+ margin-top: 15px;
433
+ padding: 10px;
434
+ background-color: var(--control-bg);
435
+ border: 1px dashed var(--secondary-color);
436
+ border-radius: 5px;
437
+ font-size: 0.9em;
438
+ }
439
+
440
+ #usage-details p {
441
+ margin: 4px 0;
442
+ }
443
+
444
+ #usage-details span {
445
+ font-weight: bold;
446
+ color: var(--primary-color);
447
+ }
448
+
449
+ #usage-details .total-usage span {
450
+ color: var(--accent-color);
451
+ }
452
+
453
+
454
+ /* Right Column: Controls */
455
+ .control-group {
456
+ background-color: var(--control-bg);
457
+ border: 1px solid var(--primary-color);
458
+ border-radius: 6px;
459
+ padding: 15px;
460
+ }
461
+
462
+ .control-group h3 {
463
+ border-bottom: none;
464
+ /* Remove border from h3 inside group */
465
+ margin-bottom: 10px;
466
+ color: var(--primary-color);
467
+ }
468
+
469
+ .control-group button {
470
+ display: block;
471
+ /* Stack buttons vertically */
472
+ width: 100%;
473
+ background-color: var(--background-color);
474
+ color: var(--accent-color);
475
+ border: 1px solid var(--secondary-color);
476
+ padding: 10px 15px;
477
+ border-radius: 4px;
478
+ font-family: 'Orbitron', sans-serif;
479
+ cursor: pointer;
480
+ margin-bottom: 8px;
481
+ transition: background-color 0.3s, box-shadow 0.3s, border-color 0.3s;
482
+ text-align: center;
483
+ }
484
+
485
+ .control-group button:last-child {
486
+ margin-bottom: 0;
487
+ }
488
+
489
+ .control-group button:hover {
490
+ background-color: var(--secondary-color);
491
+ color: var(--background-color);
492
+ border-color: var(--secondary-color);
493
+ box-shadow: var(--glow-secondary);
494
+ }
495
+
496
+ .control-group button.active {
497
+ background-color: var(--primary-color);
498
+ color: var(--background-color);
499
+ border-color: var(--primary-color);
500
+ box-shadow: var(--glow-primary);
501
+ font-weight: bold;
502
+ }
503
+
504
+ /* Status Message */
505
+ #status-message {
506
+ margin-top: auto;
507
+ /* Push to bottom */
508
+ padding: 15px;
509
+ border: 1px dashed var(--border-glow-color);
510
+ border-radius: 5px;
511
+ background-color: rgba(0, 0, 0, 0.3);
512
+ text-align: center;
513
+ }
514
+
515
+ #status-message p {
516
+ margin: 0;
517
+ }
518
+
519
+ #status-message span {
520
+ font-weight: bold;
521
+ }
522
+
523
+ #status-message .status-fits {
524
+ color: var(--success-color);
525
+ }
526
+
527
+ #status-message .status-no-fit {
528
+ color: var(--error-color);
529
+ }
530
+ /* Add this for error styling */
531
+ #status-message .status-error {
532
+ color: var(--error-color);
533
+ font-weight: bold;
534
+ }
535
+
536
+ footer {
537
+ margin-top: 30px;
538
+ color: var(--secondary-color);
539
+ font-size: 0.8em;
540
+ opacity: 0.7;
541
+ text-align: center;
542
+ width: 100%;
543
+ }
544
+
545
+ /* --- Remove or Comment Out --- */
546
+ /* #prompt-bar { background-color: var(--prompt-color); } */
547
+
548
+ /* --- Add or Modify --- */
549
+ #usage-details #kvcache-prompt-info {
550
+ font-size: 0.85em;
551
+ color: var(--secondary-color);
552
+ opacity: 0.8;
553
+ margin-left: 5px;
554
+ }
555
+
556
+ /* Adjust grid lines maybe? */
557
+ #gpu-grid-overlay {
558
+ background-image:
559
+ linear-gradient(rgba(127, 0, 255, 0.2) 1px, transparent 1px),
560
+ /* Make grid lines fainter */
561
  linear-gradient(90deg, rgba(127, 0, 255, 0.2) 1px, transparent 1px);
562
+ background-size: 10% 10%;
563
+ /* ... keep rest */
564
+ }
565
+
566
+ /* Ensure label contrast is okay on yellow */
567
+ #kvcache-bar::before {
568
+ color: #333;
569
+ /* Darker gray/black for yellow background */
570
+ text-shadow: 0 0 2px rgba(255, 255, 255, 0.5);
571
+ }
572
+
573
+
574
+ /* Make sure control group spacing is okay */
575
+ #controls-column {
576
+ gap: 15px;
577
+ /* Slightly reduce gap if needed */
578
+ }
579
+ </style>
580
  </head>
581
+
582
  <body>
583
+ <div id="configurator-container">
584
+
585
+ <header>
586
+ <h1>GPU Memory Configurator</h1>
587
+ <p>Select GPU, Model, Quantization & Utilization to estimate VRAM usage</p>
588
+ </header>
589
+
590
+ <div id="main-layout">
591
+
592
+ <!-- Left Column: Visualization -->
593
+ <div id="gpu-visualization-column">
594
+ <h2>GPU Usage</h2>
595
+ <div id="gpu-visualization-area">
596
+ <div id="gpu-capacity-indicator">
597
+ <span id="gpu-capacity-label">80 Blocks</span>
598
+ </div>
599
+ <div id="model-bar" class="usage-bar" data-label="Model"></div>
600
+ <div id="kvcache-bar" class="usage-bar" data-label="KV Cache"></div>
601
+ <div id="gpu-grid-overlay"></div>
602
+ </div>
603
+ <div id="usage-details">
604
+ <p>Model: <span id="model-usage">0</span> Blocks</p>
605
+ <p>KV Cache: <span id="kvcache-usage">0</span> Blocks <span id="kvcache-prompt-info"></span></p>
606
+ <p class="total-usage">Total: <span id="total-usage">0</span> Blocks</p>
607
+ </div>
608
+ </div>
609
+
610
+ <!-- Middle Column: Spacer -->
611
+ <div id="spacer-column"></div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612
 
613
+ <!-- Right Column: Controls -->
614
+ <div id="controls-column">
615
+ <div class="control-group" id="gpu-selector">
616
+ <h3>GPU</h3>
617
+ <!-- Buttons will be generated by JS -->
618
+ </div>
619
+ <div class="control-group" id="model-selector">
620
+ <h3>Model</h3>
621
+ <!-- Buttons will be generated by JS -->
622
+ </div>
623
+ <div class="control-group" id="quantization-selector">
624
+ <h3>Quantization</h3>
625
+ <!-- Buttons will be generated by JS -->
626
+ </div>
627
+ <!-- New Utilization Selector -->
628
+ <div class="control-group" id="utilization-selector">
629
+ <h3>KV Cache Util</h3>
630
+ <!-- Buttons will be generated by JS -->
631
  </div>
632
 
633
+ <div id="status-message">
634
+ <p>Status: <span id="fit-status">-</span></p>
635
+ </div>
636
+ </div>
637
 
638
  </div>
639
 
640
+ <footer>
641
+ Synthwave VRAM Estimator
642
+ </footer>
643
+
644
+ </div>
645
+
646
+ <script src="script.js"></script>
647
  </body>
648
+
649
  </html>