Spaces:
Running
Running
Update index.html
Browse files- index.html +499 -19
index.html
CHANGED
@@ -1,19 +1,499 @@
|
|
1 |
-
<!
|
2 |
-
<html>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<!DOCTYPE html>
|
2 |
+
<html lang="en">
|
3 |
+
<head>
|
4 |
+
<meta charset="UTF-8">
|
5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6 |
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
7 |
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
8 |
+
<link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Audiowide&display=swap" rel="stylesheet">
|
9 |
+
<title>GPU Memory Visualizer (Synthwave Edition)</title>
|
10 |
+
<link rel="stylesheet" href="style.css">
|
11 |
+
|
12 |
+
<script>
|
13 |
+
document.addEventListener('DOMContentLoaded', () => {
|
14 |
+
// --- Canvas & Control Elements ---
|
15 |
+
const canvas = document.getElementById('gpuCanvas'); const ctx = canvas.getContext('2d');
|
16 |
+
const gpuContainer = document.querySelector('.gpu-container');
|
17 |
+
const gpuSelect = document.getElementById('gpu-select');
|
18 |
+
const modelSelect = document.getElementById('model-select');
|
19 |
+
const promptButtons = document.querySelectorAll('.prompt-button');
|
20 |
+
const resetButton = document.getElementById('reset-button');
|
21 |
+
const statusMessage = document.getElementById('status-message');
|
22 |
+
const workdayButton = document.getElementById('workday-pattern');
|
23 |
+
const batchButton = document.getElementById('batch-pattern');
|
24 |
+
const burstHellButton = document.getElementById('burst-hell-pattern'); // ++ Get New Button ++
|
25 |
+
// ++ Add new button to control list ++
|
26 |
+
const controlsToDisableOnError = [resetButton, workdayButton, batchButton, burstHellButton, ...promptButtons];
|
27 |
+
const simulationButtons = [workdayButton, batchButton, burstHellButton]; // ++ Group sim buttons ++
|
28 |
+
|
29 |
+
// --- Configuration --- (Keep existing)
|
30 |
+
const BLOCK_SIZE = 15; const PADDING = 1; const COLS = Math.floor(parseInt(canvas.width) / (BLOCK_SIZE + PADDING));
|
31 |
+
const BASE_CANVAS_HEIGHT = 200; const GPU_HEIGHT_MULTIPLIERS = { 'L4': 1, 'A100': 2, 'H100': 4 };
|
32 |
+
const GPU_GB = { 'L4': 24, 'A100': 40, 'H100': 80 }; const MODEL_GB = { 'gemma-1b': 2, 'gemma-4b': 8, 'gemma-12b': 24, 'gemma-27b': 54 };
|
33 |
+
let ROWS; let TOTAL_BLOCKS; let modelBlockSizes = {}; let MAIN_GPU_AREA_START_INDEX; const TOKENS_TO_GENERATE = 3;
|
34 |
+
|
35 |
+
// --- SYNTHWAVE Colors --- (Keep existing)
|
36 |
+
const COLOR_FREE = '#1a1a2e'; const COLOR_MODEL = '#4d4dff'; const COLOR_PROMPT_SMALL = '#ff00ff'; const COLOR_PROMPT_MID = '#00ffff'; const COLOR_PROMPT_LONG = '#fff000'; const COLOR_PROCESSING = '#9d00ff'; const COLOR_GENERATED = '#ff1f1f';
|
37 |
+
|
38 |
+
// --- State Variables --- (Keep existing)
|
39 |
+
let gpuType = gpuSelect.value; let baseBlockProcessingTimeMs;
|
40 |
+
let memory = []; let processingQueue = []; let visuallyPlacedPrompts = []; let currentTask = null; let nextPromptId = 0; let isSimulationRunning = false; let simulationTimeoutIds = [];
|
41 |
+
|
42 |
+
// --- Helper Functions to Disable/Enable Controls --- (Modify enableControls)
|
43 |
+
function disableControlsOnError() { controlsToDisableOnError.forEach(control => control.disabled = true); gpuSelect.disabled = false; modelSelect.disabled = false; gpuContainer.style.opacity = '0.7'; console.log("Controls Disabled (Except GPU & Model Select)"); }
|
44 |
+
function enableControls() {
|
45 |
+
// Enable non-simulation controls first
|
46 |
+
[gpuSelect, modelSelect, resetButton, ...promptButtons].forEach(control => control.disabled = false);
|
47 |
+
// Enable simulation buttons ONLY if a simulation isn't running
|
48 |
+
simulationButtons.forEach(button => button.disabled = isSimulationRunning);
|
49 |
+
gpuContainer.style.opacity = '1';
|
50 |
+
console.log("Controls Enabled/Status Updated");
|
51 |
+
}
|
52 |
+
function disableSimButtons() {
|
53 |
+
simulationButtons.forEach(button => button.disabled = true);
|
54 |
+
}
|
55 |
+
|
56 |
+
// --- Update Canvas Size & Calculate Model Block Sizes --- (Keep existing)
|
57 |
+
function updateCanvasSizeAndRecalculateGrid() { const selectedGpu = gpuSelect.value || 'L4'; const multiplier = GPU_HEIGHT_MULTIPLIERS[selectedGpu] || 1; const newHeight = Math.floor(BASE_CANVAS_HEIGHT * multiplier); canvas.height = newHeight; gpuContainer.style.height = `${newHeight + 4}px`; ROWS = Math.floor(newHeight / (BLOCK_SIZE + PADDING)); TOTAL_BLOCKS = COLS * ROWS; const l4Height = BASE_CANVAS_HEIGHT; const l4Rows = Math.floor(l4Height / (BLOCK_SIZE + PADDING)); const totalBlocksForL4 = COLS * l4Rows; const blocksPerGbScale = totalBlocksForL4 / GPU_GB['L4']; modelBlockSizes = {}; for (const modelKey in MODEL_GB) { const modelGb = MODEL_GB[modelKey]; modelBlockSizes[modelKey] = Math.round(blocksPerGbScale * modelGb); } console.log(`Set canvas height: ${newHeight}px | Grid: ${COLS}x${ROWS}=${TOTAL_BLOCKS} | Model Sizes:`, modelBlockSizes); }
|
58 |
+
|
59 |
+
// --- Initialization --- (Keep existing)
|
60 |
+
function initializeMemory() { console.log("--- Initializing Grid ---"); isSimulationRunning = false; simulationTimeoutIds.forEach(clearTimeout); simulationTimeoutIds = []; if(currentTask && currentTask.timeoutId) clearTimeout(currentTask.timeoutId); const selectedModel = modelSelect.value; const currentModelWeightsBlocks = modelBlockSizes[selectedModel] || modelBlockSizes['gemma-1b']; MAIN_GPU_AREA_START_INDEX = currentModelWeightsBlocks; console.log(`Model: ${selectedModel}, W: ${currentModelWeightsBlocks}, Total: ${TOTAL_BLOCKS}, Start: ${MAIN_GPU_AREA_START_INDEX}`); memory = new Array(TOTAL_BLOCKS).fill(COLOR_FREE); processingQueue = []; visuallyPlacedPrompts = []; currentTask = null; nextPromptId = 0; updateProcessingSpeed(); if (currentModelWeightsBlocks >= TOTAL_BLOCKS) { console.error(`Model (${currentModelWeightsBlocks}) >= total (${TOTAL_BLOCKS}) for ${gpuSelect.value}.`); statusMessage.textContent = "ERROR: MODEL TOO LARGE FOR GRID!"; statusMessage.classList.add('error'); disableControlsOnError(); } else { statusMessage.classList.remove('error'); statusMessage.textContent = 'SYSTEM IDLE.'; enableControls(); for (let i = 0; i < currentModelWeightsBlocks; i++) { memory[i] = COLOR_MODEL; } const freeSpace = TOTAL_BLOCKS - currentModelWeightsBlocks; if (freeSpace < 100) { console.warn(`Low free space (${freeSpace}).`); } } drawAll(); }
|
61 |
+
|
62 |
+
// --- Drawing Functions --- (Keep existing)
|
63 |
+
function getCoords(index) { const row = Math.floor(index / COLS); const col = index % COLS; const x = col * (BLOCK_SIZE + PADDING); const y = row * (BLOCK_SIZE + PADDING); return { x, y }; }
|
64 |
+
function drawBlock(index, color) { if (index >= memory.length || index < 0) return; const { x, y } = getCoords(index); ctx.fillStyle = color; ctx.fillRect(x, y, BLOCK_SIZE, BLOCK_SIZE); }
|
65 |
+
function drawAll() { ctx.fillStyle = COLOR_FREE; ctx.fillRect(0, 0, canvas.width, canvas.height); for (let i = 0; i < memory.length; i++) { if (memory[i] !== COLOR_FREE) { drawBlock(i, memory[i]); } } }
|
66 |
+
|
67 |
+
// --- Logic Functions --- (Keep existing)
|
68 |
+
function getPromptColor(size) { if (size <= 3) return COLOR_PROMPT_SMALL; if (size <= 5) return COLOR_PROMPT_MID; return COLOR_PROMPT_LONG; }
|
69 |
+
function updateProcessingSpeed() { gpuType = gpuSelect.value || 'L4'; if (gpuType === 'A100') { baseBlockProcessingTimeMs = 150; } else if (gpuType === 'H100') { baseBlockProcessingTimeMs = 50; } else { baseBlockProcessingTimeMs = 250; } console.log(`GPU set to ${gpuType}, base speed ${baseBlockProcessingTimeMs}ms/block`); }
|
70 |
+
function findFreeSpaceInMainGPU(size) { let c=0; for(let i=MAIN_GPU_AREA_START_INDEX;i<TOTAL_BLOCKS;i++){ if(memory[i]===COLOR_FREE){c++;if(c===size){return i-size+1;}}else{c=0;}} return -1;}
|
71 |
+
function findOldestGeneratedBlock() { for (let i=MAIN_GPU_AREA_START_INDEX; i<TOTAL_BLOCKS; i++) { if (memory[i] === COLOR_GENERATED) return i; } return -1; }
|
72 |
+
function tryPlaceWaitingPrompts() { let p=false; for(let i=processingQueue.length-1;i>=0;i--){const q=processingQueue[i];const idx=findFreeSpaceInMainGPU(q.size); if(idx!==-1){processingQueue.splice(i,1);const pl={...q,startIndex:idx};visuallyPlacedPrompts.push(pl); for(let j=0;j<q.size;j++){if(idx+j<memory.length)memory[idx+j]=q.color;} p=true;}} if(p){drawAll();} updateStatusMessage(); }
|
73 |
+
function processNextBlock() { if (!currentTask) return; const { prompt:p, startIndex:s, processedCount:pc, generatedCount:gc, generatedIndices:gi}=currentTask; let bC=false; const ept=baseBlockProcessingTimeMs; if (pc<p.size){const bI=s+pc; if (bI<TOTAL_BLOCKS&&memory[bI]===p.color){memory[bI]=COLOR_PROCESSING; currentTask.processedCount++; drawBlock(bI,COLOR_PROCESSING); bC=true; const rBI=findOldestGeneratedBlock(); if(rBI!==-1){memory[rBI]=COLOR_FREE; drawBlock(rBI,COLOR_FREE);}}else if(bI<TOTAL_BLOCKS&&memory[bI]===COLOR_PROCESSING){currentTask.processedCount++;}else{console.error(`Block ${bI} unexpected: ${memory[bI]}. Skip.`); currentTask.processedCount++;}}else if(gc<TOKENS_TO_GENERATE){const fGB=findFreeSpaceInMainGPU(1); if(fGB!==-1){memory[fGB]=COLOR_GENERATED; currentTask.generatedCount++; gi.push(fGB); drawBlock(fGB,COLOR_GENERATED); bC=true;}else{currentTask.generatedCount=TOKENS_TO_GENERATE; bC=true;}} if(pc>=p.size&&gc>=TOKENS_TO_GENERATE){statusMessage.textContent='TASK COMPLETE.';let cC=0;for(let i=0;i<p.size;i++){const idx=s+i;if(idx<TOTAL_BLOCKS&&memory[idx]===COLOR_PROCESSING){memory[idx]=COLOR_FREE; cC++;}} const tId=currentTask.timeoutId; currentTask=null; drawAll(); setTimeout(()=>{tryPlaceWaitingPrompts();checkAndStartProcessing();},50); return;} if(currentTask){currentTask.timeoutId=setTimeout(processNextBlock,ept);}}
|
74 |
+
function checkAndStartProcessing() { if (currentTask) return; if (visuallyPlacedPrompts.length > 0) { visuallyPlacedPrompts.sort((a,b)=>a.startIndex-b.startIndex); const nPI=visuallyPlacedPrompts[0]; statusMessage.textContent='PROCESSING...'; statusMessage.classList.remove('error'); visuallyPlacedPrompts.shift(); currentTask={prompt:{id:nPI.id,size:nPI.size,color:nPI.color},startIndex:nPI.startIndex,processedCount:0,generatedCount:0,generatedIndices:[],timeoutId:null}; currentTask.timeoutId=setTimeout(processNextBlock,baseBlockProcessingTimeMs); return;} updateStatusMessage();}
|
75 |
+
function updateStatusMessage() { if(statusMessage.classList.contains('error')) return; if(currentTask){const prg=currentTask.prompt.size>0?Math.round((currentTask.processedCount/currentTask.prompt.size)*100):100; statusMessage.textContent=`PROCESSING TX ${currentTask.prompt.id} [${prg}%]|KV GEN ${currentTask.generatedCount}/${TOKENS_TO_GENERATE}`;}else if(isSimulationRunning){statusMessage.textContent='SIMULATION ACTIVE...';}else if(processingQueue.length>0){statusMessage.textContent=`${processingQueue.length} TX QUEUED//WAITING GRID...`;}else if(visuallyPlacedPrompts.length>0){statusMessage.textContent=`${visuallyPlacedPrompts.length} TX ON GRID//AWAITING...`;}else{let fB=0,gB=0; for(let i=MAIN_GPU_AREA_START_INDEX;i<TOTAL_BLOCKS;i++){if(memory[i]===COLOR_FREE)fB++;else if(memory[i]===COLOR_GENERATED)gB++;} const tFB=TOTAL_BLOCKS-(MAIN_GPU_AREA_START_INDEX||0)-gB-visuallyPlacedPrompts.reduce((a,p)=>a+p.size,0); statusMessage.textContent=`SYSTEM IDLE.//${Math.max(0,tFB)} FREE//${gB} KV`;}}
|
76 |
+
|
77 |
+
// --- Simulation Functions ---
|
78 |
+
function clickPromptButton(size) { if (gpuSelect.disabled) return; /* Prevent clicks if controls disabled */ const color = getPromptColor(size); const newPrompt = { id: nextPromptId++, size: size, color: color }; processingQueue.push(newPrompt); tryPlaceWaitingPrompts(); checkAndStartProcessing(); updateStatusMessage(); }
|
79 |
+
|
80 |
+
function simulateWorkday() { if (isSimulationRunning || workdayButton.disabled) return; console.log("Starting Workday Simulation..."); isSimulationRunning = true; disableSimButtons(); simulationTimeoutIds = []; let currentTime = 0; const schedule = (delay, func) => { simulationTimeoutIds.push(setTimeout(func, currentTime + delay)); }; for (let i = 0; i < 15; i++) { schedule(i * 400, () => clickPromptButton(Math.random() < 0.7 ? 3 : 5)); } currentTime += 15 * 400 + 2000; for (let i = 0; i < 10; i++) { let size = 3; const rand = Math.random(); if (rand > 0.8) size = 15; else if (rand > 0.4) size = 5; schedule(i * 800, () => clickPromptButton(size)); } currentTime += 10 * 800 + 3000; for (let i = 0; i < 12; i++) { schedule(i * 500, () => clickPromptButton(Math.random() < 0.6 ? 3 : 5)); } currentTime += 12 * 500 + 1000; schedule(500, () => { console.log("Workday Simulation Finished."); isSimulationRunning = false; enableControls(); updateStatusMessage(); }); updateStatusMessage(); }
|
81 |
+
|
82 |
+
function simulateBatch() { if (isSimulationRunning || batchButton.disabled) return; console.log("Starting Batch Simulation..."); isSimulationRunning = true; disableSimButtons(); simulationTimeoutIds = []; let currentTime = 0; const schedule = (delay, func) => { simulationTimeoutIds.push(setTimeout(func, currentTime + delay)); }; for (let i = 0; i < 8; i++) { let size = Math.random() < 0.6 ? 15 : 5; schedule(i * 3000, () => clickPromptButton(size)); } currentTime += 8 * 3000; schedule(1000, () => clickPromptButton(3)); schedule(1500, () => clickPromptButton(5)); currentTime += 2000; schedule(500, () => { console.log("Batch Simulation Finished."); isSimulationRunning = false; enableControls(); updateStatusMessage(); }); updateStatusMessage(); }
|
83 |
+
|
84 |
+
// ++ Burst Hell Simulation ++
|
85 |
+
function simulateBurstHell() {
|
86 |
+
if (isSimulationRunning || burstHellButton.disabled) return;
|
87 |
+
console.log("Starting BURST HELL Simulation...");
|
88 |
+
isSimulationRunning = true;
|
89 |
+
disableSimButtons(); // Disable all sim buttons
|
90 |
+
simulationTimeoutIds = [];
|
91 |
+
let currentTime = 0;
|
92 |
+
const schedule = (delay, func) => {
|
93 |
+
simulationTimeoutIds.push(setTimeout(func, currentTime + delay));
|
94 |
+
};
|
95 |
+
|
96 |
+
// Morning burst x5 (more density)
|
97 |
+
const morningCount = 15 * 5;
|
98 |
+
for (let i = 0; i < morningCount; i++) {
|
99 |
+
schedule(i * 80, () => clickPromptButton(Math.random() < 0.7 ? 3 : 5)); // Faster interval
|
100 |
+
}
|
101 |
+
currentTime += morningCount * 80 + 1500; // Shorter pause
|
102 |
+
|
103 |
+
// Mid-day x5 (more density)
|
104 |
+
const middayCount = 10 * 5;
|
105 |
+
for (let i = 0; i < middayCount; i++) {
|
106 |
+
let size = 3;
|
107 |
+
const rand = Math.random();
|
108 |
+
if (rand > 0.9) size = 15; // Fewer long prompts maybe?
|
109 |
+
else if (rand > 0.3) size = 5;
|
110 |
+
schedule(i * 120, () => clickPromptButton(size)); // Faster interval
|
111 |
+
}
|
112 |
+
currentTime += middayCount * 120 + 2000; // Shorter pause
|
113 |
+
|
114 |
+
// Afternoon burst x5 (more density)
|
115 |
+
const afternoonCount = 12 * 5;
|
116 |
+
for (let i = 0; i < afternoonCount; i++) {
|
117 |
+
schedule(i * 100, () => clickPromptButton(Math.random() < 0.6 ? 3 : 5)); // Faster interval
|
118 |
+
}
|
119 |
+
currentTime += afternoonCount * 100 + 1000;
|
120 |
+
|
121 |
+
// End simulation
|
122 |
+
schedule(500, () => {
|
123 |
+
console.log("BURST HELL Simulation Finished.");
|
124 |
+
isSimulationRunning = false;
|
125 |
+
enableControls(); // Re-enable controls (including sim buttons)
|
126 |
+
updateStatusMessage();
|
127 |
+
});
|
128 |
+
updateStatusMessage();
|
129 |
+
}
|
130 |
+
// ++ End Burst Hell ++
|
131 |
+
|
132 |
+
|
133 |
+
// --- Event Listeners ---
|
134 |
+
gpuSelect.addEventListener('change', () => { console.log("GPU Changed"); updateCanvasSizeAndRecalculateGrid(); initializeMemory(); });
|
135 |
+
modelSelect.addEventListener('change', () => { console.log("Model Changed"); initializeMemory(); });
|
136 |
+
promptButtons.forEach(button => { button.addEventListener('click', () => { if (button.disabled || isSimulationRunning) return; const size = parseInt(button.getAttribute('data-size')); const color = getPromptColor(size); const newPrompt = { id: nextPromptId++, size: size, color: color }; processingQueue.push(newPrompt); tryPlaceWaitingPrompts(); checkAndStartProcessing(); updateStatusMessage(); }); });
|
137 |
+
resetButton.addEventListener('click', () => { if (resetButton.disabled) return; console.log("Reset Clicked"); updateCanvasSizeAndRecalculateGrid(); initializeMemory(); } );
|
138 |
+
workdayButton.addEventListener('click', simulateWorkday);
|
139 |
+
batchButton.addEventListener('click', simulateBatch);
|
140 |
+
burstHellButton.addEventListener('click', simulateBurstHell); // ++ Add Listener ++
|
141 |
+
|
142 |
+
// --- Initial Setup ---
|
143 |
+
updateCanvasSizeAndRecalculateGrid(); initializeMemory();
|
144 |
+
setInterval(() => { updateStatusMessage(); if (!gpuSelect.disabled) { if (!currentTask && !isSimulationRunning && visuallyPlacedPrompts.length > 0) { checkAndStartProcessing(); } if (!currentTask && !isSimulationRunning && processingQueue.length > 0) { tryPlaceWaitingPrompts(); } } }, 1000);
|
145 |
+
|
146 |
+
});
|
147 |
+
</script>
|
148 |
+
|
149 |
+
<style>
|
150 |
+
/* Import Google Fonts (already in HTML, but good practice) */
|
151 |
+
@import url('https://fonts.googleapis.com/css2?family=Orbitron:wght@400;700&family=Audiowide&display=swap');
|
152 |
+
|
153 |
+
:root {
|
154 |
+
/* Synthwave Color Palette */
|
155 |
+
--color-background: #1a1a2e; /* Deep dark blue/purple */
|
156 |
+
--color-background-light: #2a2a4e;
|
157 |
+
--color-grid-lines: #3a3a5e; /* For potential future grid lines */
|
158 |
+
--color-text: #ffffff;
|
159 |
+
--color-neon-pink: #ff00ff;
|
160 |
+
--color-neon-cyan: #00ffff;
|
161 |
+
--color-neon-purple: #9d00ff; /* Electric purple */
|
162 |
+
--color-neon-orange: #ff8c00; /* Neon orange */
|
163 |
+
--color-neon-red: #ff1f1f; /* Brighter neon red */
|
164 |
+
--color-neon-yellow: #fff000; /* Bright neon yellow */
|
165 |
+
--color-model-blue: #4d4dff; /* A slightly brighter dark blue */
|
166 |
+
|
167 |
+
/* Fonts */
|
168 |
+
--font-heading: 'Orbitron', sans-serif;
|
169 |
+
--font-body: 'Audiowide', sans-serif; /* Using Audiowide for more elements */
|
170 |
+
}
|
171 |
+
|
172 |
+
body {
|
173 |
+
font-family: var(--font-body);
|
174 |
+
background: linear-gradient(to bottom, #0f0c29, #302b63, #24243e); /* Dark Gradient */
|
175 |
+
color: var(--color-text);
|
176 |
+
display: flex;
|
177 |
+
flex-direction: column;
|
178 |
+
align-items: center;
|
179 |
+
min-height: 100vh;
|
180 |
+
margin: 0;
|
181 |
+
padding-top: 20px; /* Add some space at the top */
|
182 |
+
}
|
183 |
+
|
184 |
+
h1 {
|
185 |
+
font-family: var(--font-heading);
|
186 |
+
font-weight: 700;
|
187 |
+
color: var(--color-neon-cyan);
|
188 |
+
text-shadow: 0 0 5px var(--color-neon-cyan),
|
189 |
+
0 0 10px var(--color-neon-cyan),
|
190 |
+
0 0 15px var(--color-neon-pink); /* Pink/Cyan glow */
|
191 |
+
margin-bottom: 25px;
|
192 |
+
letter-spacing: 2px;
|
193 |
+
}
|
194 |
+
|
195 |
+
h3 {
|
196 |
+
font-family: var(--font-heading);
|
197 |
+
color: var(--color-neon-orange);
|
198 |
+
text-shadow: 0 0 5px var(--color-neon-orange);
|
199 |
+
margin-bottom: 10px;
|
200 |
+
font-weight: 400;
|
201 |
+
letter-spacing: 1px;
|
202 |
+
}
|
203 |
+
|
204 |
+
.controls, .prompt-controls, .simulation-controls {
|
205 |
+
margin: 10px;
|
206 |
+
padding: 15px 20px; /* More padding */
|
207 |
+
background-color: rgba(0, 0, 0, 0.3); /* Translucent dark background */
|
208 |
+
border: 1px solid var(--color-neon-purple);
|
209 |
+
box-shadow: 0 0 10px var(--color-neon-purple); /* Purple glow */
|
210 |
+
border-radius: 5px;
|
211 |
+
display: flex;
|
212 |
+
flex-wrap: wrap;
|
213 |
+
gap: 15px; /* More gap */
|
214 |
+
align-items: center;
|
215 |
+
justify-content: center;
|
216 |
+
width: 80%; /* Limit width */
|
217 |
+
max-width: 850px;
|
218 |
+
}
|
219 |
+
|
220 |
+
label {
|
221 |
+
color: var(--color-neon-cyan);
|
222 |
+
text-shadow: 0 0 3px var(--color-neon-cyan);
|
223 |
+
font-weight: bold;
|
224 |
+
}
|
225 |
+
|
226 |
+
select, input /* Style select boxes similarly */ {
|
227 |
+
background-color: var(--color-background-light);
|
228 |
+
color: var(--color-text);
|
229 |
+
border: 1px solid var(--color-neon-pink);
|
230 |
+
padding: 5px 8px;
|
231 |
+
border-radius: 3px;
|
232 |
+
font-family: var(--font-body);
|
233 |
+
box-shadow: inset 0 0 5px rgba(255, 0, 255, 0.5); /* Inner pink glow */
|
234 |
+
}
|
235 |
+
|
236 |
+
select option {
|
237 |
+
background-color: var(--color-background);
|
238 |
+
color: var(--color-text);
|
239 |
+
}
|
240 |
+
|
241 |
+
|
242 |
+
.gpu-container {
|
243 |
+
border: 2px solid var(--color-neon-cyan); /* Cyan border */
|
244 |
+
margin-top: 20px;
|
245 |
+
margin-bottom: 20px;
|
246 |
+
background-color: var(--color-background); /* Use dark bg for canvas container */
|
247 |
+
width: 800px;
|
248 |
+
height: 400px;
|
249 |
+
overflow: hidden;
|
250 |
+
box-shadow: 0 0 15px var(--color-neon-cyan), /* Outer glow */
|
251 |
+
inset 0 0 10px rgba(0, 0, 0, 0.5); /* Inner shadow */
|
252 |
+
padding: 2px; /* Small padding so border doesn't overlap blocks */
|
253 |
+
}
|
254 |
+
|
255 |
+
#gpuCanvas {
|
256 |
+
display: block;
|
257 |
+
background-color: var(--color-background); /* Match container */
|
258 |
+
}
|
259 |
+
|
260 |
+
/* --- Button Styling --- */
|
261 |
+
.prompt-button, .simulation-button, #reset-button {
|
262 |
+
padding: 10px 18px; /* Larger buttons */
|
263 |
+
border: none; /* Remove default border */
|
264 |
+
border-bottom: 2px solid; /* Add bottom border for 3D effect */
|
265 |
+
cursor: pointer;
|
266 |
+
border-radius: 4px;
|
267 |
+
font-weight: bold;
|
268 |
+
font-family: var(--font-body);
|
269 |
+
background-color: var(--color-background-light);
|
270 |
+
color: var(--color-text);
|
271 |
+
transition: all 0.2s ease;
|
272 |
+
text-shadow: 0 0 4px; /* Apply base text shadow */
|
273 |
+
box-shadow: 0 0 5px, inset 0 0 3px rgba(0,0,0,0.4); /* Apply base box shadow */
|
274 |
+
}
|
275 |
+
|
276 |
+
.prompt-button:hover, .simulation-button:hover, #reset-button:hover {
|
277 |
+
opacity: 0.9;
|
278 |
+
transform: translateY(-1px); /* Slight lift on hover */
|
279 |
+
}
|
280 |
+
|
281 |
+
.prompt-button:active, .simulation-button:active, #reset-button:active {
|
282 |
+
transform: translateY(1px); /* Push down on click */
|
283 |
+
box-shadow: 0 0 2px, inset 0 0 5px rgba(0,0,0,0.6);
|
284 |
+
}
|
285 |
+
|
286 |
+
/* Specific Button Colors & Glows */
|
287 |
+
#reset-button {
|
288 |
+
border-color: var(--color-neon-red);
|
289 |
+
color: var(--color-neon-red);
|
290 |
+
text-shadow: 0 0 4px var(--color-neon-red);
|
291 |
+
box-shadow: 0 0 5px var(--color-neon-red), inset 0 0 3px rgba(0,0,0,0.4);
|
292 |
+
}
|
293 |
+
#reset-button:hover { box-shadow: 0 0 8px var(--color-neon-red), inset 0 0 3px rgba(0,0,0,0.4); }
|
294 |
+
|
295 |
+
.prompt-button[data-size="3"] { /* Short -> Pink */
|
296 |
+
border-color: var(--color-neon-pink);
|
297 |
+
color: var(--color-neon-pink);
|
298 |
+
text-shadow: 0 0 4px var(--color-neon-pink);
|
299 |
+
box-shadow: 0 0 5px var(--color-neon-pink), inset 0 0 3px rgba(0,0,0,0.4);
|
300 |
+
}
|
301 |
+
.prompt-button[data-size="3"]:hover { box-shadow: 0 0 8px var(--color-neon-pink), inset 0 0 3px rgba(0,0,0,0.4); }
|
302 |
+
|
303 |
+
|
304 |
+
.prompt-button[data-size="5"] { /* Mid -> Cyan */
|
305 |
+
border-color: var(--color-neon-cyan);
|
306 |
+
color: var(--color-neon-cyan);
|
307 |
+
text-shadow: 0 0 4px var(--color-neon-cyan);
|
308 |
+
box-shadow: 0 0 5px var(--color-neon-cyan), inset 0 0 3px rgba(0,0,0,0.4);
|
309 |
+
}
|
310 |
+
.prompt-button[data-size="5"]:hover { box-shadow: 0 0 8px var(--color-neon-cyan), inset 0 0 3px rgba(0,0,0,0.4); }
|
311 |
+
|
312 |
+
|
313 |
+
.prompt-button[data-size="15"] { /* Long -> Yellow */
|
314 |
+
border-color: var(--color-neon-yellow);
|
315 |
+
color: var(--color-neon-yellow);
|
316 |
+
text-shadow: 0 0 4px var(--color-neon-yellow);
|
317 |
+
box-shadow: 0 0 5px var(--color-neon-yellow), inset 0 0 3px rgba(0,0,0,0.4);
|
318 |
+
}
|
319 |
+
.prompt-button[data-size="15"]:hover { box-shadow: 0 0 8px var(--color-neon-yellow), inset 0 0 3px rgba(0,0,0,0.4); }
|
320 |
+
|
321 |
+
|
322 |
+
.simulation-button { /* Simulation -> Orange */
|
323 |
+
border-color: var(--color-neon-orange);
|
324 |
+
color: var(--color-neon-orange);
|
325 |
+
text-shadow: 0 0 4px var(--color-neon-orange);
|
326 |
+
box-shadow: 0 0 5px var(--color-neon-orange), inset 0 0 3px rgba(0,0,0,0.4);
|
327 |
+
}
|
328 |
+
.simulation-button:hover { box-shadow: 0 0 8px var(--color-neon-orange), inset 0 0 3px rgba(0,0,0,0.4); }
|
329 |
+
|
330 |
+
.simulation-button:disabled {
|
331 |
+
border-color: #555;
|
332 |
+
color: #777;
|
333 |
+
text-shadow: none;
|
334 |
+
box-shadow: inset 0 0 5px rgba(0,0,0,0.6);
|
335 |
+
cursor: not-allowed;
|
336 |
+
opacity: 0.6;
|
337 |
+
transform: translateY(0);
|
338 |
+
}
|
339 |
+
|
340 |
+
#status-message {
|
341 |
+
font-weight: bold;
|
342 |
+
color: var(--color-text); /* White status text */
|
343 |
+
text-shadow: 0 0 3px var(--color-neon-purple); /* Purple glow */
|
344 |
+
min-width: 150px;
|
345 |
+
text-align: center;
|
346 |
+
flex-basis: 100%;
|
347 |
+
margin-top: 10px; /* More space for status */
|
348 |
+
letter-spacing: 1px;
|
349 |
+
}
|
350 |
+
#status-message {
|
351 |
+
font-weight: bold;
|
352 |
+
color: var(--color-text); /* Default white */
|
353 |
+
text-shadow: 0 0 3px var(--color-neon-purple); /* Default purple glow */
|
354 |
+
min-width: 150px;
|
355 |
+
text-align: center;
|
356 |
+
flex-basis: 100%;
|
357 |
+
margin-top: 10px;
|
358 |
+
letter-spacing: 1px;
|
359 |
+
transition: color 0.3s ease, text-shadow 0.3s ease; /* Smooth transition for color change */
|
360 |
+
}
|
361 |
+
|
362 |
+
/* ++ New Style for Error Status ++ */
|
363 |
+
#status-message.error {
|
364 |
+
color: var(--color-neon-red); /* Use neon red for errors */
|
365 |
+
text-shadow: 0 0 5px var(--color-neon-red), /* Stronger red glow */
|
366 |
+
0 0 8px #ff0000;
|
367 |
+
}
|
368 |
+
|
369 |
+
#status-message {
|
370 |
+
font-weight: bold;
|
371 |
+
color: var(--color-text);
|
372 |
+
text-shadow: 0 0 3px var(--color-neon-purple);
|
373 |
+
min-width: 150px;
|
374 |
+
text-align: center;
|
375 |
+
flex-basis: 100%;
|
376 |
+
margin-top: 10px;
|
377 |
+
letter-spacing: 1px;
|
378 |
+
transition: color 0.3s ease, text-shadow 0.3s ease; /* Smooth transition */
|
379 |
+
}
|
380 |
+
|
381 |
+
/* ++ Style for Error Message ++ */
|
382 |
+
#status-message.error {
|
383 |
+
color: var(--color-neon-red);
|
384 |
+
text-shadow: 0 0 5px var(--color-neon-red);
|
385 |
+
}
|
386 |
+
/* --- Legend Styling --- */
|
387 |
+
.legend {
|
388 |
+
margin-top: 25px; /* Space above the legend */
|
389 |
+
padding: 15px 20px;
|
390 |
+
background-color: rgba(0, 0, 0, 0.3);
|
391 |
+
border: 1px solid var(--color-neon-purple);
|
392 |
+
box-shadow: 0 0 10px var(--color-neon-purple);
|
393 |
+
border-radius: 5px;
|
394 |
+
width: 80%;
|
395 |
+
max-width: 500px; /* Make legend less wide */
|
396 |
+
text-align: center;
|
397 |
+
}
|
398 |
+
|
399 |
+
.legend h3 {
|
400 |
+
margin-top: 0; /* Remove extra top margin from heading */
|
401 |
+
margin-bottom: 15px;
|
402 |
+
color: var(--color-neon-orange); /* Match other headings */
|
403 |
+
text-shadow: 0 0 5px var(--color-neon-orange);
|
404 |
+
}
|
405 |
+
|
406 |
+
.legend-item {
|
407 |
+
display: flex; /* Align color swatch and text */
|
408 |
+
align-items: center;
|
409 |
+
justify-content: center; /* Center items within the flex line */
|
410 |
+
margin-bottom: 8px; /* Space between legend items */
|
411 |
+
font-size: 0.9em; /* Slightly smaller text */
|
412 |
+
color: var(--color-text);
|
413 |
+
}
|
414 |
+
|
415 |
+
.legend-color {
|
416 |
+
display: inline-block;
|
417 |
+
width: 15px; /* Size of the color swatch */
|
418 |
+
height: 15px;
|
419 |
+
margin-right: 10px; /* Space between swatch and text */
|
420 |
+
border-radius: 3px; /* Slightly rounded corners */
|
421 |
+
vertical-align: middle; /* Align with text */
|
422 |
+
box-shadow: inset 0 0 3px rgba(0,0,0,0.5); /* Subtle inner shadow */
|
423 |
+
}
|
424 |
+
</style>
|
425 |
+
|
426 |
+
</head>
|
427 |
+
<body>
|
428 |
+
<h1>GPU Inference Pulse (SLM Edition)</h1>
|
429 |
+
|
430 |
+
<div class="controls">
|
431 |
+
<label for="gpu-select">SYSTEM:</label>
|
432 |
+
<select id="gpu-select">
|
433 |
+
<option value="L4" selected>L4 24GB (Standard)</option>
|
434 |
+
<option value="A100">A100 40GB (High)</option>
|
435 |
+
<option value="H100">H100 80GB (Mega)</option>
|
436 |
+
</select>
|
437 |
+
|
438 |
+
<label for="model-select">MODEL:</label>
|
439 |
+
<select id="model-select">
|
440 |
+
<option value="gemma-1b" selected>Gemma 3 1B</option>
|
441 |
+
<option value="gemma-4b">Gemma 3 4B</option>
|
442 |
+
<option value="gemma-12b">Gemma 3 12B</option>
|
443 |
+
<option value="gemma-27b">Gemma 3 27B</option>
|
444 |
+
</select>
|
445 |
+
|
446 |
+
<button id="reset-button">RESET GRID</button>
|
447 |
+
<span id="status-message">SYSTEM IDLE.</span>
|
448 |
+
</div>
|
449 |
+
|
450 |
+
<div class="gpu-container">
|
451 |
+
<canvas id="gpuCanvas" width="800"></canvas>
|
452 |
+
</div>
|
453 |
+
|
454 |
+
<div class="prompt-controls">
|
455 |
+
<h3>INITIATE PROMPT:</h3>
|
456 |
+
<button class="prompt-button" data-size="3">SHORT (3)</button>
|
457 |
+
<button class="prompt-button" data-size="5">MEDIUM (5)</button>
|
458 |
+
<button class="prompt-button" data-size="15">LONG (15)</button>
|
459 |
+
</div>
|
460 |
+
|
461 |
+
<div class="simulation-controls">
|
462 |
+
<h3>SIMULATE LOAD:</h3>
|
463 |
+
<button class="simulation-button" id="workday-pattern">WORKDAY</button>
|
464 |
+
<button class="simulation-button" id="batch-pattern">BATCH RUN</button>
|
465 |
+
<!-- ++ Add Burst Hell Button ++ -->
|
466 |
+
<button class="simulation-button" id="burst-hell-pattern">BURST HELL</button>
|
467 |
+
<!-- ++ End Add ++ -->
|
468 |
+
</div>
|
469 |
+
<script src="script.js"></script> <!-- Keep your script tag -->
|
470 |
+
|
471 |
+
<!-- Color Legend Section -->
|
472 |
+
<div class="legend">
|
473 |
+
<h3>LEGEND:</h3>
|
474 |
+
<div class="legend-item">
|
475 |
+
<span class="legend-color" style="background-color: #4d4dff;"></span> Model Weights Memory Footprint
|
476 |
+
</div>
|
477 |
+
<div class="legend-item">
|
478 |
+
<span class="legend-color" style="background-color: #ff00ff;"></span> Short Prompt Input Tokens
|
479 |
+
</div>
|
480 |
+
<div class="legend-item">
|
481 |
+
<span class="legend-color" style="background-color: #00ffff;"></span> Medium Prompt Input Tokens
|
482 |
+
</div>
|
483 |
+
<div class="legend-item">
|
484 |
+
<span class="legend-color" style="background-color: #fff000;"></span> Long Prompt Input Tokens
|
485 |
+
</div>
|
486 |
+
<div class="legend-item">
|
487 |
+
<span class="legend-color" style="background-color: #9d00ff;"></span> Processing Input Tokens
|
488 |
+
</div>
|
489 |
+
<div class="legend-item">
|
490 |
+
<span class="legend-color" style="background-color: #ff1f1f;"></span> Generated Tokens (Output)
|
491 |
+
</div>
|
492 |
+
<div class="legend-item">
|
493 |
+
<span class="legend-color" style="background-color: #1a1a2e; border: 1px solid #555;"></span> Free Memory
|
494 |
+
</div>
|
495 |
+
</div>
|
496 |
+
<!-- End Color Legend Section -->
|
497 |
+
<h2>fredmo</h2>
|
498 |
+
</body>
|
499 |
+
</html>
|