Spaces:
No application file
No application file
Factor Studios
commited on
Delete virtual_hardware
Browse files- virtual_hardware/ai.py +0 -446
- virtual_hardware/driver.py +0 -312
- virtual_hardware/enhanced_cpu.py +0 -407
- virtual_hardware/vgpu.py +0 -283
- virtual_hardware/virtual_gpu_driver.py +0 -348
- virtual_hardware/virtual_ram.py +0 -385
- virtual_hardware/virtual_ssd.py +0 -318
- virtual_hardware/vram.py +0 -361
virtual_hardware/ai.py
DELETED
|
@@ -1,446 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
AI Accelerator Module
|
| 3 |
-
|
| 4 |
-
This module implements AI-specific operations, treating the vGPU as a tensor engine
|
| 5 |
-
and leveraging the simulated parallelism of 50,000 cores and 800 SMs.
|
| 6 |
-
"""
|
| 7 |
-
|
| 8 |
-
import numpy as np
|
| 9 |
-
import time
|
| 10 |
-
from typing import Dict, Any, Optional, Tuple, Union, List
|
| 11 |
-
from enum import Enum
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
class VectorOperation(Enum):
|
| 15 |
-
"""Enumeration of supported vector operations."""
|
| 16 |
-
ADD = "add"
|
| 17 |
-
SUBTRACT = "subtract"
|
| 18 |
-
MULTIPLY = "multiply"
|
| 19 |
-
DIVIDE = "divide"
|
| 20 |
-
DOT_PRODUCT = "dot_product"
|
| 21 |
-
CROSS_PRODUCT = "cross_product"
|
| 22 |
-
NORMALIZE = "normalize"
|
| 23 |
-
MAGNITUDE = "magnitude"
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
class AIAccelerator:
|
| 27 |
-
"""
|
| 28 |
-
AI Accelerator that simulates GPU-based AI computations.
|
| 29 |
-
|
| 30 |
-
This class leverages NumPy's optimized operations to simulate the parallel
|
| 31 |
-
processing capabilities of the vGPU for AI workloads.
|
| 32 |
-
"""
|
| 33 |
-
|
| 34 |
-
def __init__(self, vram=None, num_sms: int = 800, cores_per_sm: int = 62):
|
| 35 |
-
self.vram = vram
|
| 36 |
-
self.num_sms = num_sms
|
| 37 |
-
self.cores_per_sm = cores_per_sm
|
| 38 |
-
self.total_cores = num_sms * cores_per_sm
|
| 39 |
-
|
| 40 |
-
# AI operation statistics
|
| 41 |
-
self.operations_performed = 0
|
| 42 |
-
self.total_compute_time = 0.0
|
| 43 |
-
self.flops_performed = 0 # Floating point operations
|
| 44 |
-
|
| 45 |
-
# Matrix registry for storing matrices in VRAM
|
| 46 |
-
self.matrix_registry: Dict[str, str] = {} # matrix_id -> vram_address
|
| 47 |
-
self.matrix_counter = 0
|
| 48 |
-
|
| 49 |
-
def set_vram(self, vram):
|
| 50 |
-
"""Set the VRAM reference."""
|
| 51 |
-
self.vram = vram
|
| 52 |
-
|
| 53 |
-
def allocate_matrix(self, shape: Tuple[int, ...], dtype=np.float32,
|
| 54 |
-
name: Optional[str] = None) -> str:
|
| 55 |
-
"""Allocate a matrix in VRAM and return its ID."""
|
| 56 |
-
if not self.vram:
|
| 57 |
-
raise RuntimeError("VRAM not available")
|
| 58 |
-
|
| 59 |
-
if name is None:
|
| 60 |
-
name = f"matrix_{self.matrix_counter}"
|
| 61 |
-
self.matrix_counter += 1
|
| 62 |
-
|
| 63 |
-
# Create matrix data
|
| 64 |
-
matrix_data = np.zeros(shape, dtype=dtype)
|
| 65 |
-
|
| 66 |
-
# Store in VRAM as a texture (reusing texture storage mechanism)
|
| 67 |
-
matrix_id = self.vram.load_texture(matrix_data, name)
|
| 68 |
-
self.matrix_registry[name] = matrix_id
|
| 69 |
-
|
| 70 |
-
return name
|
| 71 |
-
|
| 72 |
-
def load_matrix(self, matrix_data: np.ndarray, name: Optional[str] = None) -> str:
|
| 73 |
-
"""Load matrix data into VRAM and return its ID."""
|
| 74 |
-
if not self.vram:
|
| 75 |
-
raise RuntimeError("VRAM not available")
|
| 76 |
-
|
| 77 |
-
if name is None:
|
| 78 |
-
name = f"matrix_{self.matrix_counter}"
|
| 79 |
-
self.matrix_counter += 1
|
| 80 |
-
|
| 81 |
-
# Store in VRAM
|
| 82 |
-
matrix_id = self.vram.load_texture(matrix_data, name)
|
| 83 |
-
self.matrix_registry[name] = matrix_id
|
| 84 |
-
|
| 85 |
-
return name
|
| 86 |
-
|
| 87 |
-
def get_matrix(self, matrix_id: str) -> Optional[np.ndarray]:
|
| 88 |
-
"""Retrieve matrix data from VRAM."""
|
| 89 |
-
if not self.vram or matrix_id not in self.matrix_registry:
|
| 90 |
-
return None
|
| 91 |
-
|
| 92 |
-
vram_id = self.matrix_registry[matrix_id]
|
| 93 |
-
return self.vram.get_texture(vram_id)
|
| 94 |
-
|
| 95 |
-
def matrix_multiply(self, matrix_a_id: str, matrix_b_id: str,
|
| 96 |
-
result_id: Optional[str] = None) -> Optional[str]:
|
| 97 |
-
"""Perform matrix multiplication using simulated GPU parallelism."""
|
| 98 |
-
start_time = time.time()
|
| 99 |
-
|
| 100 |
-
# Retrieve matrices from VRAM
|
| 101 |
-
matrix_a = self.get_matrix(matrix_a_id)
|
| 102 |
-
matrix_b = self.get_matrix(matrix_b_id)
|
| 103 |
-
|
| 104 |
-
if matrix_a is None or matrix_b is None:
|
| 105 |
-
print(f"Error: Could not retrieve matrices {matrix_a_id} or {matrix_b_id}")
|
| 106 |
-
return None
|
| 107 |
-
|
| 108 |
-
try:
|
| 109 |
-
# Check if matrices can be multiplied
|
| 110 |
-
if matrix_a.shape[-1] != matrix_b.shape[0]:
|
| 111 |
-
print(f"Error: Matrix dimensions incompatible for multiplication: "
|
| 112 |
-
f"{matrix_a.shape} x {matrix_b.shape}")
|
| 113 |
-
return None
|
| 114 |
-
|
| 115 |
-
# Simulate parallel processing by breaking down the operation
|
| 116 |
-
# In a real GPU, this would be distributed across SMs and cores
|
| 117 |
-
result = self._simulate_parallel_matmul(matrix_a, matrix_b)
|
| 118 |
-
|
| 119 |
-
# Store result in VRAM
|
| 120 |
-
if result_id is None:
|
| 121 |
-
result_id = f"result_{self.matrix_counter}"
|
| 122 |
-
self.matrix_counter += 1
|
| 123 |
-
|
| 124 |
-
result_matrix_id = self.load_matrix(result, result_id)
|
| 125 |
-
|
| 126 |
-
# Update statistics
|
| 127 |
-
compute_time = time.time() - start_time
|
| 128 |
-
self.total_compute_time += compute_time
|
| 129 |
-
self.operations_performed += 1
|
| 130 |
-
|
| 131 |
-
# Calculate FLOPs (2 * M * N * K for matrix multiplication)
|
| 132 |
-
m, k = matrix_a.shape
|
| 133 |
-
k2, n = matrix_b.shape
|
| 134 |
-
flops = 2 * m * n * k
|
| 135 |
-
self.flops_performed += flops
|
| 136 |
-
|
| 137 |
-
print(f"Matrix multiplication completed: {matrix_a.shape} x {matrix_b.shape} "
|
| 138 |
-
f"= {result.shape} in {compute_time:.4f}s")
|
| 139 |
-
print(f"Simulated {flops:,} FLOPs across {self.total_cores} cores")
|
| 140 |
-
|
| 141 |
-
return result_matrix_id
|
| 142 |
-
|
| 143 |
-
except Exception as e:
|
| 144 |
-
print(f"Error in matrix multiplication: {e}")
|
| 145 |
-
return None
|
| 146 |
-
|
| 147 |
-
def _simulate_parallel_matmul(self, matrix_a: np.ndarray, matrix_b: np.ndarray) -> np.ndarray:
|
| 148 |
-
"""Simulate parallel matrix multiplication across SMs."""
|
| 149 |
-
# Use NumPy's optimized matrix multiplication
|
| 150 |
-
# In a real implementation, this would be broken down into blocks
|
| 151 |
-
# and distributed across the simulated SMs
|
| 152 |
-
|
| 153 |
-
# For demonstration, we can show how the work would be distributed
|
| 154 |
-
m, k = matrix_a.shape
|
| 155 |
-
k2, n = matrix_b.shape
|
| 156 |
-
|
| 157 |
-
# Calculate work distribution
|
| 158 |
-
total_output_elements = m * n
|
| 159 |
-
elements_per_sm = max(1, total_output_elements // self.num_sms)
|
| 160 |
-
|
| 161 |
-
print(f"Distributing {total_output_elements:,} output elements across "
|
| 162 |
-
f"{self.num_sms} SMs ({elements_per_sm} elements per SM)")
|
| 163 |
-
|
| 164 |
-
# Perform the actual computation using NumPy
|
| 165 |
-
result = np.dot(matrix_a, matrix_b)
|
| 166 |
-
|
| 167 |
-
return result
|
| 168 |
-
|
| 169 |
-
def vector_operation(self, operation: VectorOperation, vector_a_id: str,
|
| 170 |
-
vector_b_id: Optional[str] = None,
|
| 171 |
-
result_id: Optional[str] = None) -> Optional[str]:
|
| 172 |
-
"""Perform vector operations using simulated GPU parallelism."""
|
| 173 |
-
start_time = time.time()
|
| 174 |
-
|
| 175 |
-
# Retrieve vectors from VRAM
|
| 176 |
-
vector_a = self.get_matrix(vector_a_id)
|
| 177 |
-
if vector_a is None:
|
| 178 |
-
print(f"Error: Could not retrieve vector {vector_a_id}")
|
| 179 |
-
return None
|
| 180 |
-
|
| 181 |
-
vector_b = None
|
| 182 |
-
if vector_b_id:
|
| 183 |
-
vector_b = self.get_matrix(vector_b_id)
|
| 184 |
-
if vector_b is None:
|
| 185 |
-
print(f"Error: Could not retrieve vector {vector_b_id}")
|
| 186 |
-
return None
|
| 187 |
-
|
| 188 |
-
try:
|
| 189 |
-
result = None
|
| 190 |
-
flops = 0
|
| 191 |
-
|
| 192 |
-
if operation == VectorOperation.ADD:
|
| 193 |
-
if vector_b is None:
|
| 194 |
-
raise ValueError("Vector B required for addition")
|
| 195 |
-
result = vector_a + vector_b
|
| 196 |
-
flops = vector_a.size
|
| 197 |
-
|
| 198 |
-
elif operation == VectorOperation.SUBTRACT:
|
| 199 |
-
if vector_b is None:
|
| 200 |
-
raise ValueError("Vector B required for subtraction")
|
| 201 |
-
result = vector_a - vector_b
|
| 202 |
-
flops = vector_a.size
|
| 203 |
-
|
| 204 |
-
elif operation == VectorOperation.MULTIPLY:
|
| 205 |
-
if vector_b is None:
|
| 206 |
-
raise ValueError("Vector B required for multiplication")
|
| 207 |
-
result = vector_a * vector_b
|
| 208 |
-
flops = vector_a.size
|
| 209 |
-
|
| 210 |
-
elif operation == VectorOperation.DIVIDE:
|
| 211 |
-
if vector_b is None:
|
| 212 |
-
raise ValueError("Vector B required for division")
|
| 213 |
-
result = vector_a / vector_b
|
| 214 |
-
flops = vector_a.size
|
| 215 |
-
|
| 216 |
-
elif operation == VectorOperation.DOT_PRODUCT:
|
| 217 |
-
if vector_b is None:
|
| 218 |
-
raise ValueError("Vector B required for dot product")
|
| 219 |
-
result = np.dot(vector_a.flatten(), vector_b.flatten())
|
| 220 |
-
flops = 2 * vector_a.size
|
| 221 |
-
|
| 222 |
-
elif operation == VectorOperation.CROSS_PRODUCT:
|
| 223 |
-
if vector_b is None:
|
| 224 |
-
raise ValueError("Vector B required for cross product")
|
| 225 |
-
result = np.cross(vector_a, vector_b)
|
| 226 |
-
flops = 6 # Approximate for 3D cross product
|
| 227 |
-
|
| 228 |
-
elif operation == VectorOperation.NORMALIZE:
|
| 229 |
-
magnitude = np.linalg.norm(vector_a)
|
| 230 |
-
result = vector_a / magnitude if magnitude > 0 else vector_a
|
| 231 |
-
flops = vector_a.size * 2 # Division + magnitude calculation
|
| 232 |
-
|
| 233 |
-
elif operation == VectorOperation.MAGNITUDE:
|
| 234 |
-
result = np.array([np.linalg.norm(vector_a)])
|
| 235 |
-
flops = vector_a.size * 2 # Squares and sum
|
| 236 |
-
|
| 237 |
-
else:
|
| 238 |
-
raise ValueError(f"Unsupported vector operation: {operation}")
|
| 239 |
-
|
| 240 |
-
# Store result in VRAM
|
| 241 |
-
if result_id is None:
|
| 242 |
-
result_id = f"vector_result_{self.matrix_counter}"
|
| 243 |
-
self.matrix_counter += 1
|
| 244 |
-
|
| 245 |
-
result_vector_id = self.load_matrix(result, result_id)
|
| 246 |
-
|
| 247 |
-
# Update statistics
|
| 248 |
-
compute_time = time.time() - start_time
|
| 249 |
-
self.total_compute_time += compute_time
|
| 250 |
-
self.operations_performed += 1
|
| 251 |
-
self.flops_performed += flops
|
| 252 |
-
|
| 253 |
-
print(f"Vector operation {operation.value} completed in {compute_time:.4f}s")
|
| 254 |
-
|
| 255 |
-
return result_vector_id
|
| 256 |
-
|
| 257 |
-
except Exception as e:
|
| 258 |
-
print(f"Error in vector operation {operation.value}: {e}")
|
| 259 |
-
return None
|
| 260 |
-
|
| 261 |
-
def convolution_2d(self, input_id: str, kernel_id: str,
|
| 262 |
-
stride: int = 1, padding: int = 0,
|
| 263 |
-
result_id: Optional[str] = None) -> Optional[str]:
|
| 264 |
-
"""Perform 2D convolution operation."""
|
| 265 |
-
start_time = time.time()
|
| 266 |
-
|
| 267 |
-
# Retrieve input and kernel from VRAM
|
| 268 |
-
input_data = self.get_matrix(input_id)
|
| 269 |
-
kernel = self.get_matrix(kernel_id)
|
| 270 |
-
|
| 271 |
-
if input_data is None or kernel is None:
|
| 272 |
-
print(f"Error: Could not retrieve input or kernel")
|
| 273 |
-
return None
|
| 274 |
-
|
| 275 |
-
try:
|
| 276 |
-
# Simple 2D convolution implementation
|
| 277 |
-
# In a real GPU implementation, this would be highly optimized
|
| 278 |
-
# and distributed across many cores
|
| 279 |
-
|
| 280 |
-
if len(input_data.shape) == 2:
|
| 281 |
-
input_h, input_w = input_data.shape
|
| 282 |
-
channels = 1
|
| 283 |
-
else:
|
| 284 |
-
input_h, input_w, channels = input_data.shape
|
| 285 |
-
|
| 286 |
-
kernel_h, kernel_w = kernel.shape[:2]
|
| 287 |
-
|
| 288 |
-
# Calculate output dimensions
|
| 289 |
-
output_h = (input_h + 2 * padding - kernel_h) // stride + 1
|
| 290 |
-
output_w = (input_w + 2 * padding - kernel_w) // stride + 1
|
| 291 |
-
|
| 292 |
-
# Initialize output
|
| 293 |
-
if channels == 1:
|
| 294 |
-
output = np.zeros((output_h, output_w))
|
| 295 |
-
else:
|
| 296 |
-
output = np.zeros((output_h, output_w, channels))
|
| 297 |
-
|
| 298 |
-
# Pad input if necessary
|
| 299 |
-
if padding > 0:
|
| 300 |
-
if channels == 1:
|
| 301 |
-
padded_input = np.pad(input_data, padding, mode='constant')
|
| 302 |
-
else:
|
| 303 |
-
padded_input = np.pad(input_data,
|
| 304 |
-
((padding, padding), (padding, padding), (0, 0)),
|
| 305 |
-
mode='constant')
|
| 306 |
-
else:
|
| 307 |
-
padded_input = input_data
|
| 308 |
-
|
| 309 |
-
# Perform convolution
|
| 310 |
-
flops = 0
|
| 311 |
-
for y in range(0, output_h):
|
| 312 |
-
for x in range(0, output_w):
|
| 313 |
-
y_start = y * stride
|
| 314 |
-
x_start = x * stride
|
| 315 |
-
|
| 316 |
-
if channels == 1:
|
| 317 |
-
patch = padded_input[y_start:y_start+kernel_h, x_start:x_start+kernel_w]
|
| 318 |
-
output[y, x] = np.sum(patch * kernel)
|
| 319 |
-
flops += kernel_h * kernel_w * 2 # Multiply and add
|
| 320 |
-
else:
|
| 321 |
-
for c in range(channels):
|
| 322 |
-
patch = padded_input[y_start:y_start+kernel_h,
|
| 323 |
-
x_start:x_start+kernel_w, c]
|
| 324 |
-
output[y, x, c] = np.sum(patch * kernel)
|
| 325 |
-
flops += kernel_h * kernel_w * 2
|
| 326 |
-
|
| 327 |
-
# Store result in VRAM
|
| 328 |
-
if result_id is None:
|
| 329 |
-
result_id = f"conv_result_{self.matrix_counter}"
|
| 330 |
-
self.matrix_counter += 1
|
| 331 |
-
|
| 332 |
-
result_conv_id = self.load_matrix(output, result_id)
|
| 333 |
-
|
| 334 |
-
# Update statistics
|
| 335 |
-
compute_time = time.time() - start_time
|
| 336 |
-
self.total_compute_time += compute_time
|
| 337 |
-
self.operations_performed += 1
|
| 338 |
-
self.flops_performed += flops
|
| 339 |
-
|
| 340 |
-
print(f"2D Convolution completed: {input_data.shape} * {kernel.shape} "
|
| 341 |
-
f"= {output.shape} in {compute_time:.4f}s")
|
| 342 |
-
print(f"Simulated {flops:,} FLOPs")
|
| 343 |
-
|
| 344 |
-
return result_conv_id
|
| 345 |
-
|
| 346 |
-
except Exception as e:
|
| 347 |
-
print(f"Error in 2D convolution: {e}")
|
| 348 |
-
return None
|
| 349 |
-
|
| 350 |
-
def get_stats(self) -> Dict[str, Any]:
|
| 351 |
-
"""Get AI accelerator statistics."""
|
| 352 |
-
avg_compute_time = self.total_compute_time / max(1, self.operations_performed)
|
| 353 |
-
flops_per_second = self.flops_performed / max(0.001, self.total_compute_time)
|
| 354 |
-
|
| 355 |
-
return {
|
| 356 |
-
"operations_performed": self.operations_performed,
|
| 357 |
-
"total_compute_time": self.total_compute_time,
|
| 358 |
-
"avg_compute_time": avg_compute_time,
|
| 359 |
-
"flops_performed": self.flops_performed,
|
| 360 |
-
"flops_per_second": flops_per_second,
|
| 361 |
-
"matrices_in_memory": len(self.matrix_registry),
|
| 362 |
-
"simulated_cores": self.total_cores,
|
| 363 |
-
"simulated_sms": self.num_sms
|
| 364 |
-
}
|
| 365 |
-
|
| 366 |
-
def reset_stats(self) -> None:
|
| 367 |
-
"""Reset AI accelerator statistics."""
|
| 368 |
-
self.operations_performed = 0
|
| 369 |
-
self.total_compute_time = 0.0
|
| 370 |
-
self.flops_performed = 0
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
if __name__ == "__main__":
|
| 374 |
-
# Test the AI accelerator
|
| 375 |
-
from vram import VRAM
|
| 376 |
-
|
| 377 |
-
# Create VRAM and AI accelerator
|
| 378 |
-
vram = VRAM(memory_size_gb=1)
|
| 379 |
-
ai = AIAccelerator(vram)
|
| 380 |
-
|
| 381 |
-
print("Testing AI Accelerator...")
|
| 382 |
-
|
| 383 |
-
# Test matrix operations
|
| 384 |
-
# Create test matrices
|
| 385 |
-
matrix_a = np.random.rand(100, 50).astype(np.float32)
|
| 386 |
-
matrix_b = np.random.rand(50, 75).astype(np.float32)
|
| 387 |
-
|
| 388 |
-
# Load matrices into VRAM
|
| 389 |
-
a_id = ai.load_matrix(matrix_a, "test_matrix_a")
|
| 390 |
-
b_id = ai.load_matrix(matrix_b, "test_matrix_b")
|
| 391 |
-
|
| 392 |
-
# Perform matrix multiplication
|
| 393 |
-
result_id = ai.matrix_multiply(a_id, b_id, "multiplication_result")
|
| 394 |
-
|
| 395 |
-
if result_id:
|
| 396 |
-
result = ai.get_matrix(result_id)
|
| 397 |
-
print(f"Matrix multiplication result shape: {result.shape}")
|
| 398 |
-
|
| 399 |
-
# Verify result
|
| 400 |
-
expected = np.dot(matrix_a, matrix_b)
|
| 401 |
-
if np.allclose(result, expected):
|
| 402 |
-
print("Matrix multiplication result is correct!")
|
| 403 |
-
else:
|
| 404 |
-
print("Matrix multiplication result is incorrect!")
|
| 405 |
-
|
| 406 |
-
# Test vector operations
|
| 407 |
-
vector_a = np.random.rand(1000).astype(np.float32)
|
| 408 |
-
vector_b = np.random.rand(1000).astype(np.float32)
|
| 409 |
-
|
| 410 |
-
va_id = ai.load_matrix(vector_a, "vector_a")
|
| 411 |
-
vb_id = ai.load_matrix(vector_b, "vector_b")
|
| 412 |
-
|
| 413 |
-
# Test vector addition
|
| 414 |
-
add_result_id = ai.vector_operation(VectorOperation.ADD, va_id, vb_id)
|
| 415 |
-
if add_result_id:
|
| 416 |
-
add_result = ai.get_matrix(add_result_id)
|
| 417 |
-
expected_add = vector_a + vector_b
|
| 418 |
-
if np.allclose(add_result, expected_add):
|
| 419 |
-
print("Vector addition result is correct!")
|
| 420 |
-
|
| 421 |
-
# Test dot product
|
| 422 |
-
dot_result_id = ai.vector_operation(VectorOperation.DOT_PRODUCT, va_id, vb_id)
|
| 423 |
-
if dot_result_id:
|
| 424 |
-
dot_result = ai.get_matrix(dot_result_id)
|
| 425 |
-
expected_dot = np.dot(vector_a, vector_b)
|
| 426 |
-
if np.allclose(dot_result[0], expected_dot):
|
| 427 |
-
print("Dot product result is correct!")
|
| 428 |
-
|
| 429 |
-
# Test 2D convolution
|
| 430 |
-
input_image = np.random.rand(32, 32).astype(np.float32)
|
| 431 |
-
kernel = np.array([[1, 0, -1], [2, 0, -2], [1, 0, -1]], dtype=np.float32) # Sobel edge detector
|
| 432 |
-
|
| 433 |
-
img_id = ai.load_matrix(input_image, "test_image")
|
| 434 |
-
kernel_id = ai.load_matrix(kernel, "sobel_kernel")
|
| 435 |
-
|
| 436 |
-
conv_result_id = ai.convolution_2d(img_id, kernel_id)
|
| 437 |
-
if conv_result_id:
|
| 438 |
-
conv_result = ai.get_matrix(conv_result_id)
|
| 439 |
-
print(f"Convolution result shape: {conv_result.shape}")
|
| 440 |
-
|
| 441 |
-
# Print final statistics
|
| 442 |
-
stats = ai.get_stats()
|
| 443 |
-
print(f"AI Accelerator stats: {stats}")
|
| 444 |
-
|
| 445 |
-
print("AI Accelerator test completed!")
|
| 446 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
virtual_hardware/driver.py
DELETED
|
@@ -1,312 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
GPU Driver Module
|
| 3 |
-
|
| 4 |
-
This module acts as the interface between a virtual CPU (or external command source)
|
| 5 |
-
and the vGPU, handling command queuing and interpretation.
|
| 6 |
-
"""
|
| 7 |
-
|
| 8 |
-
import asyncio
|
| 9 |
-
from collections import deque
|
| 10 |
-
from enum import Enum
|
| 11 |
-
from typing import Dict, Any, Optional, List
|
| 12 |
-
from dataclasses import dataclass
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
class CommandType(Enum):
|
| 16 |
-
"""Enumeration of supported GPU commands."""
|
| 17 |
-
CLEAR = "clear"
|
| 18 |
-
DRAW_RECT = "draw_rect"
|
| 19 |
-
DRAW_PIXEL = "draw_pixel"
|
| 20 |
-
DRAW_IMAGE = "draw_image"
|
| 21 |
-
SET_SHADER = "set_shader"
|
| 22 |
-
MATRIX_MULTIPLY = "matrix_multiply"
|
| 23 |
-
VECTOR_OP = "vector_op"
|
| 24 |
-
CREATE_FRAMEBUFFER = "create_framebuffer"
|
| 25 |
-
SET_FRAMEBUFFER = "set_framebuffer"
|
| 26 |
-
LOAD_TEXTURE = "load_texture"
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
@dataclass
|
| 30 |
-
class Command:
|
| 31 |
-
"""Represents a single command to be executed by the vGPU."""
|
| 32 |
-
command_id: str
|
| 33 |
-
command_type: CommandType
|
| 34 |
-
parameters: Dict[str, Any]
|
| 35 |
-
priority: int = 0
|
| 36 |
-
timestamp: float = 0.0
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
class GPUDriver:
|
| 40 |
-
"""
|
| 41 |
-
GPU Driver that manages command queues and interfaces with the vGPU.
|
| 42 |
-
|
| 43 |
-
This class receives commands from external sources (virtual CPU, applications)
|
| 44 |
-
and translates them into tasks that can be processed by the vGPU.
|
| 45 |
-
"""
|
| 46 |
-
|
| 47 |
-
def __init__(self, vgpu=None):
|
| 48 |
-
self.vgpu = vgpu
|
| 49 |
-
|
| 50 |
-
# Command queue management
|
| 51 |
-
self.command_queue = deque()
|
| 52 |
-
self.command_counter = 0
|
| 53 |
-
|
| 54 |
-
# Current state
|
| 55 |
-
self.current_framebuffer = None
|
| 56 |
-
self.current_shader = None
|
| 57 |
-
|
| 58 |
-
# Command processing statistics
|
| 59 |
-
self.commands_processed = 0
|
| 60 |
-
self.commands_failed = 0
|
| 61 |
-
|
| 62 |
-
def set_vgpu(self, vgpu):
|
| 63 |
-
"""Set the vGPU reference."""
|
| 64 |
-
self.vgpu = vgpu
|
| 65 |
-
|
| 66 |
-
def submit_command(self, command_type: CommandType, parameters: Dict[str, Any],
|
| 67 |
-
priority: int = 0) -> str:
|
| 68 |
-
"""Submit a command to the GPU driver."""
|
| 69 |
-
command_id = f"cmd_{self.command_counter}"
|
| 70 |
-
self.command_counter += 1
|
| 71 |
-
|
| 72 |
-
command = Command(
|
| 73 |
-
command_id=command_id,
|
| 74 |
-
command_type=command_type,
|
| 75 |
-
parameters=parameters,
|
| 76 |
-
priority=priority,
|
| 77 |
-
timestamp=asyncio.get_event_loop().time()
|
| 78 |
-
)
|
| 79 |
-
|
| 80 |
-
# Insert command based on priority (higher priority first)
|
| 81 |
-
if priority > 0:
|
| 82 |
-
# Find insertion point for priority queue
|
| 83 |
-
inserted = False
|
| 84 |
-
for i, existing_cmd in enumerate(self.command_queue):
|
| 85 |
-
if existing_cmd.priority < priority:
|
| 86 |
-
self.command_queue.insert(i, command)
|
| 87 |
-
inserted = True
|
| 88 |
-
break
|
| 89 |
-
if not inserted:
|
| 90 |
-
self.command_queue.append(command)
|
| 91 |
-
else:
|
| 92 |
-
self.command_queue.append(command)
|
| 93 |
-
|
| 94 |
-
return command_id
|
| 95 |
-
|
| 96 |
-
async def process_commands(self) -> None:
|
| 97 |
-
"""Process all pending commands in the queue."""
|
| 98 |
-
while self.command_queue:
|
| 99 |
-
command = self.command_queue.popleft()
|
| 100 |
-
await self._execute_command(command)
|
| 101 |
-
|
| 102 |
-
async def _execute_command(self, command: Command) -> None:
|
| 103 |
-
"""Execute a single command."""
|
| 104 |
-
try:
|
| 105 |
-
if command.command_type == CommandType.CLEAR:
|
| 106 |
-
await self._handle_clear(command)
|
| 107 |
-
elif command.command_type == CommandType.DRAW_RECT:
|
| 108 |
-
await self._handle_draw_rect(command)
|
| 109 |
-
elif command.command_type == CommandType.DRAW_PIXEL:
|
| 110 |
-
await self._handle_draw_pixel(command)
|
| 111 |
-
elif command.command_type == CommandType.DRAW_IMAGE:
|
| 112 |
-
await self._handle_draw_image(command)
|
| 113 |
-
elif command.command_type == CommandType.SET_SHADER:
|
| 114 |
-
await self._handle_set_shader(command)
|
| 115 |
-
elif command.command_type == CommandType.MATRIX_MULTIPLY:
|
| 116 |
-
await self._handle_matrix_multiply(command)
|
| 117 |
-
elif command.command_type == CommandType.VECTOR_OP:
|
| 118 |
-
await self._handle_vector_op(command)
|
| 119 |
-
elif command.command_type == CommandType.CREATE_FRAMEBUFFER:
|
| 120 |
-
await self._handle_create_framebuffer(command)
|
| 121 |
-
elif command.command_type == CommandType.SET_FRAMEBUFFER:
|
| 122 |
-
await self._handle_set_framebuffer(command)
|
| 123 |
-
elif command.command_type == CommandType.LOAD_TEXTURE:
|
| 124 |
-
await self._handle_load_texture(command)
|
| 125 |
-
else:
|
| 126 |
-
print(f"Unknown command type: {command.command_type}")
|
| 127 |
-
self.commands_failed += 1
|
| 128 |
-
return
|
| 129 |
-
|
| 130 |
-
self.commands_processed += 1
|
| 131 |
-
|
| 132 |
-
except Exception as e:
|
| 133 |
-
print(f"Error executing command {command.command_id}: {e}")
|
| 134 |
-
self.commands_failed += 1
|
| 135 |
-
|
| 136 |
-
async def _handle_clear(self, command: Command) -> None:
|
| 137 |
-
"""Handle CLEAR command."""
|
| 138 |
-
if self.vgpu and self.current_framebuffer:
|
| 139 |
-
from vgpu import TaskType
|
| 140 |
-
task_id = self.vgpu.submit_task(
|
| 141 |
-
TaskType.RENDER_CLEAR,
|
| 142 |
-
{
|
| 143 |
-
"framebuffer_id": self.current_framebuffer,
|
| 144 |
-
**command.parameters
|
| 145 |
-
}
|
| 146 |
-
)
|
| 147 |
-
|
| 148 |
-
async def _handle_draw_rect(self, command: Command) -> None:
|
| 149 |
-
"""Handle DRAW_RECT command."""
|
| 150 |
-
if self.vgpu and self.current_framebuffer:
|
| 151 |
-
from vgpu import TaskType
|
| 152 |
-
task_id = self.vgpu.submit_task(
|
| 153 |
-
TaskType.RENDER_RECT,
|
| 154 |
-
{
|
| 155 |
-
"framebuffer_id": self.current_framebuffer,
|
| 156 |
-
**command.parameters
|
| 157 |
-
}
|
| 158 |
-
)
|
| 159 |
-
|
| 160 |
-
async def _handle_draw_pixel(self, command: Command) -> None:
|
| 161 |
-
"""Handle DRAW_PIXEL command."""
|
| 162 |
-
if self.vgpu and self.current_framebuffer:
|
| 163 |
-
from vgpu import TaskType
|
| 164 |
-
# Convert single pixel to a 1x1 rectangle
|
| 165 |
-
params = command.parameters.copy()
|
| 166 |
-
params.update({
|
| 167 |
-
"framebuffer_id": self.current_framebuffer,
|
| 168 |
-
"width": 1,
|
| 169 |
-
"height": 1
|
| 170 |
-
})
|
| 171 |
-
task_id = self.vgpu.submit_task(TaskType.RENDER_RECT, params)
|
| 172 |
-
|
| 173 |
-
async def _handle_draw_image(self, command: Command) -> None:
|
| 174 |
-
"""Handle DRAW_IMAGE command."""
|
| 175 |
-
if self.vgpu and self.current_framebuffer:
|
| 176 |
-
from vgpu import TaskType
|
| 177 |
-
task_id = self.vgpu.submit_task(
|
| 178 |
-
TaskType.RENDER_IMAGE,
|
| 179 |
-
{
|
| 180 |
-
"framebuffer_id": self.current_framebuffer,
|
| 181 |
-
**command.parameters
|
| 182 |
-
}
|
| 183 |
-
)
|
| 184 |
-
|
| 185 |
-
async def _handle_set_shader(self, command: Command) -> None:
|
| 186 |
-
"""Handle SET_SHADER command."""
|
| 187 |
-
shader_id = command.parameters.get("shader_id")
|
| 188 |
-
if shader_id:
|
| 189 |
-
self.current_shader = shader_id
|
| 190 |
-
|
| 191 |
-
async def _handle_matrix_multiply(self, command: Command) -> None:
|
| 192 |
-
"""Handle MATRIX_MULTIPLY command."""
|
| 193 |
-
if self.vgpu:
|
| 194 |
-
from vgpu import TaskType
|
| 195 |
-
task_id = self.vgpu.submit_task(
|
| 196 |
-
TaskType.AI_MATRIX_MULTIPLY,
|
| 197 |
-
command.parameters
|
| 198 |
-
)
|
| 199 |
-
|
| 200 |
-
async def _handle_vector_op(self, command: Command) -> None:
|
| 201 |
-
"""Handle VECTOR_OP command."""
|
| 202 |
-
if self.vgpu:
|
| 203 |
-
from vgpu import TaskType
|
| 204 |
-
task_id = self.vgpu.submit_task(
|
| 205 |
-
TaskType.AI_VECTOR_OP,
|
| 206 |
-
command.parameters
|
| 207 |
-
)
|
| 208 |
-
|
| 209 |
-
async def _handle_create_framebuffer(self, command: Command) -> None:
|
| 210 |
-
"""Handle CREATE_FRAMEBUFFER command."""
|
| 211 |
-
if self.vgpu and self.vgpu.vram:
|
| 212 |
-
width = command.parameters.get("width", 800)
|
| 213 |
-
height = command.parameters.get("height", 600)
|
| 214 |
-
channels = command.parameters.get("channels", 3)
|
| 215 |
-
name = command.parameters.get("name")
|
| 216 |
-
|
| 217 |
-
framebuffer_id = self.vgpu.vram.create_framebuffer(width, height, channels, name)
|
| 218 |
-
|
| 219 |
-
# Set as current framebuffer if none is set
|
| 220 |
-
if self.current_framebuffer is None:
|
| 221 |
-
self.current_framebuffer = framebuffer_id
|
| 222 |
-
|
| 223 |
-
async def _handle_set_framebuffer(self, command: Command) -> None:
|
| 224 |
-
"""Handle SET_FRAMEBUFFER command."""
|
| 225 |
-
framebuffer_id = command.parameters.get("framebuffer_id")
|
| 226 |
-
if framebuffer_id and self.vgpu and self.vgpu.vram:
|
| 227 |
-
if self.vgpu.vram.get_framebuffer(framebuffer_id):
|
| 228 |
-
self.current_framebuffer = framebuffer_id
|
| 229 |
-
|
| 230 |
-
async def _handle_load_texture(self, command: Command) -> None:
|
| 231 |
-
"""Handle LOAD_TEXTURE command."""
|
| 232 |
-
if self.vgpu and self.vgpu.vram:
|
| 233 |
-
texture_data = command.parameters.get("texture_data")
|
| 234 |
-
name = command.parameters.get("name")
|
| 235 |
-
|
| 236 |
-
if texture_data is not None:
|
| 237 |
-
texture_id = self.vgpu.vram.load_texture(texture_data, name)
|
| 238 |
-
|
| 239 |
-
def get_current_framebuffer(self) -> Optional[str]:
|
| 240 |
-
"""Get the current active framebuffer ID."""
|
| 241 |
-
return self.current_framebuffer
|
| 242 |
-
|
| 243 |
-
def get_current_shader(self) -> Optional[str]:
|
| 244 |
-
"""Get the current active shader ID."""
|
| 245 |
-
return self.current_shader
|
| 246 |
-
|
| 247 |
-
def get_stats(self) -> Dict[str, Any]:
|
| 248 |
-
"""Get driver statistics."""
|
| 249 |
-
return {
|
| 250 |
-
"commands_in_queue": len(self.command_queue),
|
| 251 |
-
"commands_processed": self.commands_processed,
|
| 252 |
-
"commands_failed": self.commands_failed,
|
| 253 |
-
"current_framebuffer": self.current_framebuffer,
|
| 254 |
-
"current_shader": self.current_shader
|
| 255 |
-
}
|
| 256 |
-
|
| 257 |
-
# Convenience methods for common operations
|
| 258 |
-
def clear_screen(self, color: tuple = (0, 0, 0)) -> str:
|
| 259 |
-
"""Clear the current framebuffer with the specified color."""
|
| 260 |
-
return self.submit_command(CommandType.CLEAR, {"color": color})
|
| 261 |
-
|
| 262 |
-
def draw_rectangle(self, x: int, y: int, width: int, height: int,
|
| 263 |
-
color: tuple = (255, 255, 255)) -> str:
|
| 264 |
-
"""Draw a rectangle on the current framebuffer."""
|
| 265 |
-
return self.submit_command(
|
| 266 |
-
CommandType.DRAW_RECT,
|
| 267 |
-
{"x": x, "y": y, "width": width, "height": height, "color": color}
|
| 268 |
-
)
|
| 269 |
-
|
| 270 |
-
def draw_pixel(self, x: int, y: int, color: tuple = (255, 255, 255)) -> str:
|
| 271 |
-
"""Draw a single pixel on the current framebuffer."""
|
| 272 |
-
return self.submit_command(
|
| 273 |
-
CommandType.DRAW_PIXEL,
|
| 274 |
-
{"x": x, "y": y, "color": color}
|
| 275 |
-
)
|
| 276 |
-
|
| 277 |
-
def create_framebuffer(self, width: int, height: int, channels: int = 3,
|
| 278 |
-
name: Optional[str] = None) -> str:
|
| 279 |
-
"""Create a new framebuffer."""
|
| 280 |
-
return self.submit_command(
|
| 281 |
-
CommandType.CREATE_FRAMEBUFFER,
|
| 282 |
-
{"width": width, "height": height, "channels": channels, "name": name}
|
| 283 |
-
)
|
| 284 |
-
|
| 285 |
-
def set_framebuffer(self, framebuffer_id: str) -> str:
|
| 286 |
-
"""Set the active framebuffer."""
|
| 287 |
-
return self.submit_command(
|
| 288 |
-
CommandType.SET_FRAMEBUFFER,
|
| 289 |
-
{"framebuffer_id": framebuffer_id}
|
| 290 |
-
)
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
if __name__ == "__main__":
|
| 294 |
-
# Test the driver
|
| 295 |
-
async def test_driver():
|
| 296 |
-
driver = GPUDriver()
|
| 297 |
-
|
| 298 |
-
# Submit some test commands
|
| 299 |
-
driver.create_framebuffer(800, 600)
|
| 300 |
-
driver.clear_screen((255, 0, 0))
|
| 301 |
-
driver.draw_rectangle(100, 100, 200, 150, (0, 255, 0))
|
| 302 |
-
driver.draw_pixel(400, 300, (0, 0, 255))
|
| 303 |
-
|
| 304 |
-
print(f"Driver stats: {driver.get_stats()}")
|
| 305 |
-
|
| 306 |
-
# Process commands (without vGPU, they won't actually execute)
|
| 307 |
-
await driver.process_commands()
|
| 308 |
-
|
| 309 |
-
print(f"Driver stats after processing: {driver.get_stats()}")
|
| 310 |
-
|
| 311 |
-
asyncio.run(test_driver())
|
| 312 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
virtual_hardware/enhanced_cpu.py
DELETED
|
@@ -1,407 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Enhanced CPU Module with GPU Integration
|
| 3 |
-
|
| 4 |
-
This module extends the original CPU implementation to include GPU communication
|
| 5 |
-
capabilities and enhanced threading support for the 50 cores / 100 threads configuration.
|
| 6 |
-
"""
|
| 7 |
-
|
| 8 |
-
import multiprocessing
|
| 9 |
-
import threading
|
| 10 |
-
import time
|
| 11 |
-
import queue
|
| 12 |
-
from typing import Dict, Any, Optional, List
|
| 13 |
-
from dataclasses import dataclass
|
| 14 |
-
|
| 15 |
-
# Import original CPU components
|
| 16 |
-
from virtual_hardware_display_system.src.cpu import Core, MultiCoreCPU, CPULogger
|
| 17 |
-
|
| 18 |
-
# Import GPU driver interface
|
| 19 |
-
from virtual_gpu_driver import VirtualGPUDriver, CPUGPUInterface, GPUCommandType
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
@dataclass
|
| 23 |
-
class VirtualThread:
|
| 24 |
-
"""Represents a virtual thread running on a CPU core."""
|
| 25 |
-
thread_id: int
|
| 26 |
-
core_id: int
|
| 27 |
-
program_counter: int = 0
|
| 28 |
-
stack_pointer: int = 255
|
| 29 |
-
registers: Dict[str, int] = None
|
| 30 |
-
status: str = "ready" # ready, running, waiting, terminated
|
| 31 |
-
priority: int = 1
|
| 32 |
-
|
| 33 |
-
def __post_init__(self):
|
| 34 |
-
if self.registers is None:
|
| 35 |
-
self.registers = {"AX": 0, "BX": 0, "CX": 0, "DX": 0}
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
class ThreadScheduler:
|
| 39 |
-
"""Simple round-robin thread scheduler for virtual threads."""
|
| 40 |
-
|
| 41 |
-
def __init__(self, max_threads_per_core: int = 2):
|
| 42 |
-
self.max_threads_per_core = max_threads_per_core
|
| 43 |
-
self.threads: Dict[int, List[VirtualThread]] = {} # core_id -> list of threads
|
| 44 |
-
self.current_thread_index: Dict[int, int] = {} # core_id -> current thread index
|
| 45 |
-
self.thread_counter = 0
|
| 46 |
-
|
| 47 |
-
def create_thread(self, core_id: int, program_counter: int = 0) -> int:
|
| 48 |
-
"""Create a new virtual thread on the specified core."""
|
| 49 |
-
if core_id not in self.threads:
|
| 50 |
-
self.threads[core_id] = []
|
| 51 |
-
self.current_thread_index[core_id] = 0
|
| 52 |
-
|
| 53 |
-
if len(self.threads[core_id]) >= self.max_threads_per_core:
|
| 54 |
-
return -1 # Core is at thread capacity
|
| 55 |
-
|
| 56 |
-
thread_id = self.thread_counter
|
| 57 |
-
self.thread_counter += 1
|
| 58 |
-
|
| 59 |
-
thread = VirtualThread(
|
| 60 |
-
thread_id=thread_id,
|
| 61 |
-
core_id=core_id,
|
| 62 |
-
program_counter=program_counter
|
| 63 |
-
)
|
| 64 |
-
|
| 65 |
-
self.threads[core_id].append(thread)
|
| 66 |
-
return thread_id
|
| 67 |
-
|
| 68 |
-
def get_current_thread(self, core_id: int) -> Optional[VirtualThread]:
|
| 69 |
-
"""Get the currently scheduled thread for a core."""
|
| 70 |
-
if core_id not in self.threads or not self.threads[core_id]:
|
| 71 |
-
return None
|
| 72 |
-
|
| 73 |
-
threads = self.threads[core_id]
|
| 74 |
-
current_index = self.current_thread_index[core_id]
|
| 75 |
-
|
| 76 |
-
if current_index < len(threads):
|
| 77 |
-
return threads[current_index]
|
| 78 |
-
return None
|
| 79 |
-
|
| 80 |
-
def schedule_next_thread(self, core_id: int) -> Optional[VirtualThread]:
|
| 81 |
-
"""Schedule the next thread for execution on a core."""
|
| 82 |
-
if core_id not in self.threads or not self.threads[core_id]:
|
| 83 |
-
return None
|
| 84 |
-
|
| 85 |
-
threads = self.threads[core_id]
|
| 86 |
-
if not threads:
|
| 87 |
-
return None
|
| 88 |
-
|
| 89 |
-
# Round-robin scheduling
|
| 90 |
-
self.current_thread_index[core_id] = (self.current_thread_index[core_id] + 1) % len(threads)
|
| 91 |
-
return self.get_current_thread(core_id)
|
| 92 |
-
|
| 93 |
-
def terminate_thread(self, thread_id: int) -> bool:
|
| 94 |
-
"""Terminate a virtual thread."""
|
| 95 |
-
for core_id, threads in self.threads.items():
|
| 96 |
-
for i, thread in enumerate(threads):
|
| 97 |
-
if thread.thread_id == thread_id:
|
| 98 |
-
thread.status = "terminated"
|
| 99 |
-
threads.pop(i)
|
| 100 |
-
# Adjust current thread index if necessary
|
| 101 |
-
if self.current_thread_index[core_id] >= len(threads):
|
| 102 |
-
self.current_thread_index[core_id] = 0
|
| 103 |
-
return True
|
| 104 |
-
return False
|
| 105 |
-
|
| 106 |
-
def get_thread_count(self, core_id: int) -> int:
|
| 107 |
-
"""Get the number of active threads on a core."""
|
| 108 |
-
return len(self.threads.get(core_id, []))
|
| 109 |
-
|
| 110 |
-
def get_total_thread_count(self) -> int:
|
| 111 |
-
"""Get the total number of active threads across all cores."""
|
| 112 |
-
return sum(len(threads) for threads in self.threads.values())
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
class EnhancedCore(Core):
|
| 116 |
-
"""Enhanced CPU core with GPU integration and threading support."""
|
| 117 |
-
|
| 118 |
-
def __init__(self, core_id: int, gpu_interface: Optional[CPUGPUInterface] = None):
|
| 119 |
-
super().__init__(core_id)
|
| 120 |
-
self.gpu_interface = gpu_interface
|
| 121 |
-
self.thread_scheduler = ThreadScheduler(max_threads_per_core=2) # 2 threads per core for 100 total
|
| 122 |
-
self.gpu_command_queue = queue.Queue()
|
| 123 |
-
self.gpu_results = {}
|
| 124 |
-
|
| 125 |
-
# Enhanced instruction set for GPU operations
|
| 126 |
-
self.gpu_instructions = {
|
| 127 |
-
'GPU_CLEAR', 'GPU_RECT', 'GPU_TRANSFER', 'GPU_ALLOC', 'GPU_AI_INFER',
|
| 128 |
-
'GPU_MATRIX_MUL', 'GPU_WAIT', 'GPU_STATUS'
|
| 129 |
-
}
|
| 130 |
-
|
| 131 |
-
def connect_gpu_interface(self, gpu_interface: CPUGPUInterface):
|
| 132 |
-
"""Connect the GPU interface to this core."""
|
| 133 |
-
self.gpu_interface = gpu_interface
|
| 134 |
-
|
| 135 |
-
def create_virtual_thread(self, program_counter: int = 0) -> int:
|
| 136 |
-
"""Create a new virtual thread on this core."""
|
| 137 |
-
return self.thread_scheduler.create_thread(self.core_id, program_counter)
|
| 138 |
-
|
| 139 |
-
def execute_with_threading(self, instruction):
|
| 140 |
-
"""Execute instruction with threading support."""
|
| 141 |
-
current_thread = self.thread_scheduler.get_current_thread(self.core_id)
|
| 142 |
-
|
| 143 |
-
if current_thread is None:
|
| 144 |
-
# No threads, execute normally
|
| 145 |
-
return self.execute(instruction)
|
| 146 |
-
|
| 147 |
-
# Save current core state to thread
|
| 148 |
-
current_thread.registers["AX"] = self.AX
|
| 149 |
-
current_thread.registers["BX"] = self.BX
|
| 150 |
-
current_thread.registers["CX"] = self.CX
|
| 151 |
-
current_thread.registers["DX"] = self.DX
|
| 152 |
-
current_thread.program_counter = self.PC
|
| 153 |
-
current_thread.stack_pointer = self.SP
|
| 154 |
-
|
| 155 |
-
# Execute instruction
|
| 156 |
-
result = self.execute(instruction)
|
| 157 |
-
|
| 158 |
-
# Restore thread state to core
|
| 159 |
-
self.AX = current_thread.registers["AX"]
|
| 160 |
-
self.BX = current_thread.registers["BX"]
|
| 161 |
-
self.CX = current_thread.registers["CX"]
|
| 162 |
-
self.DX = current_thread.registers["DX"]
|
| 163 |
-
self.PC = current_thread.program_counter
|
| 164 |
-
self.SP = current_thread.stack_pointer
|
| 165 |
-
|
| 166 |
-
return result
|
| 167 |
-
|
| 168 |
-
def execute(self, instruction):
|
| 169 |
-
"""Enhanced execute method with GPU instruction support."""
|
| 170 |
-
op = instruction.get("op")
|
| 171 |
-
|
| 172 |
-
# Handle GPU instructions
|
| 173 |
-
if op in self.gpu_instructions:
|
| 174 |
-
return self._execute_gpu_instruction(instruction)
|
| 175 |
-
|
| 176 |
-
# Handle regular CPU instructions
|
| 177 |
-
return super().execute(instruction)
|
| 178 |
-
|
| 179 |
-
def _execute_gpu_instruction(self, instruction):
|
| 180 |
-
"""Execute GPU-specific instructions."""
|
| 181 |
-
if not self.gpu_interface:
|
| 182 |
-
print(f"Core {self.core_id} Error: GPU interface not connected")
|
| 183 |
-
return
|
| 184 |
-
|
| 185 |
-
op = instruction.get("op")
|
| 186 |
-
|
| 187 |
-
try:
|
| 188 |
-
if op == 'GPU_CLEAR':
|
| 189 |
-
color = instruction.get('color', (0, 0, 0))
|
| 190 |
-
cmd_id = self.gpu_interface.gpu_clear_screen(color, self.core_id)
|
| 191 |
-
self.gpu_results[cmd_id] = "pending"
|
| 192 |
-
self.AX = hash(cmd_id) & 0xFFFF # Store command ID hash in AX
|
| 193 |
-
|
| 194 |
-
elif op == 'GPU_RECT':
|
| 195 |
-
x = instruction.get('x', 0)
|
| 196 |
-
y = instruction.get('y', 0)
|
| 197 |
-
width = instruction.get('width', 100)
|
| 198 |
-
height = instruction.get('height', 100)
|
| 199 |
-
color = instruction.get('color', (255, 255, 255))
|
| 200 |
-
cmd_id = self.gpu_interface.gpu_draw_rect(x, y, width, height, color, self.core_id)
|
| 201 |
-
self.gpu_results[cmd_id] = "pending"
|
| 202 |
-
self.AX = hash(cmd_id) & 0xFFFF
|
| 203 |
-
|
| 204 |
-
elif op == 'GPU_TRANSFER':
|
| 205 |
-
data = instruction.get('data', b'')
|
| 206 |
-
name = instruction.get('name', f'transfer_{self.core_id}')
|
| 207 |
-
cmd_id = self.gpu_interface.gpu_transfer_data(data, name, self.core_id)
|
| 208 |
-
self.gpu_results[cmd_id] = "pending"
|
| 209 |
-
self.AX = hash(cmd_id) & 0xFFFF
|
| 210 |
-
|
| 211 |
-
elif op == 'GPU_ALLOC':
|
| 212 |
-
width = instruction.get('width', 1920)
|
| 213 |
-
height = instruction.get('height', 1080)
|
| 214 |
-
channels = instruction.get('channels', 3)
|
| 215 |
-
name = instruction.get('name')
|
| 216 |
-
cmd_id = self.gpu_interface.gpu_alloc_framebuffer(width, height, channels, name, self.core_id)
|
| 217 |
-
self.gpu_results[cmd_id] = "pending"
|
| 218 |
-
self.AX = hash(cmd_id) & 0xFFFF
|
| 219 |
-
|
| 220 |
-
elif op == 'GPU_AI_INFER':
|
| 221 |
-
model_data = instruction.get('model_data')
|
| 222 |
-
input_data = instruction.get('input_data')
|
| 223 |
-
cmd_id = self.gpu_interface.gpu_ai_inference(model_data, input_data, self.core_id)
|
| 224 |
-
self.gpu_results[cmd_id] = "pending"
|
| 225 |
-
self.AX = hash(cmd_id) & 0xFFFF
|
| 226 |
-
|
| 227 |
-
elif op == 'GPU_MATRIX_MUL':
|
| 228 |
-
matrix_a = instruction.get('matrix_a')
|
| 229 |
-
matrix_b = instruction.get('matrix_b')
|
| 230 |
-
cmd_id = self.gpu_interface.gpu_matrix_multiply(matrix_a, matrix_b, self.core_id)
|
| 231 |
-
self.gpu_results[cmd_id] = "pending"
|
| 232 |
-
self.AX = hash(cmd_id) & 0xFFFF
|
| 233 |
-
|
| 234 |
-
elif op == 'GPU_WAIT':
|
| 235 |
-
cmd_id_hash = instruction.get('cmd_id_hash', self.AX)
|
| 236 |
-
timeout = instruction.get('timeout', 10.0)
|
| 237 |
-
|
| 238 |
-
# Find command ID by hash (simplified)
|
| 239 |
-
cmd_id = None
|
| 240 |
-
for cid in self.gpu_results:
|
| 241 |
-
if (hash(cid) & 0xFFFF) == cmd_id_hash:
|
| 242 |
-
cmd_id = cid
|
| 243 |
-
break
|
| 244 |
-
|
| 245 |
-
if cmd_id:
|
| 246 |
-
success = self.gpu_interface.wait_for_gpu_task(cmd_id, timeout)
|
| 247 |
-
self.ZF = 1 if success else 0
|
| 248 |
-
if success:
|
| 249 |
-
self.gpu_results[cmd_id] = "completed"
|
| 250 |
-
else:
|
| 251 |
-
self.ZF = 0
|
| 252 |
-
|
| 253 |
-
elif op == 'GPU_STATUS':
|
| 254 |
-
cmd_id_hash = instruction.get('cmd_id_hash', self.AX)
|
| 255 |
-
|
| 256 |
-
# Find command ID by hash and check status
|
| 257 |
-
for cid in self.gpu_results:
|
| 258 |
-
if (hash(cid) & 0xFFFF) == cmd_id_hash:
|
| 259 |
-
status = self.gpu_interface.gpu_driver.get_command_status(cid)
|
| 260 |
-
if status == "completed":
|
| 261 |
-
self.ZF = 1
|
| 262 |
-
elif status == "error":
|
| 263 |
-
self.ZF = 0
|
| 264 |
-
self.CF = 1
|
| 265 |
-
else:
|
| 266 |
-
self.ZF = 0
|
| 267 |
-
self.CF = 0
|
| 268 |
-
break
|
| 269 |
-
|
| 270 |
-
except Exception as e:
|
| 271 |
-
print(f"Core {self.core_id} GPU instruction error: {e}")
|
| 272 |
-
self.CF = 1 # Set carry flag to indicate error
|
| 273 |
-
|
| 274 |
-
def run_with_threading(self):
|
| 275 |
-
"""Enhanced run method with threading support."""
|
| 276 |
-
# Create initial threads if none exist
|
| 277 |
-
if self.thread_scheduler.get_total_thread_count() == 0:
|
| 278 |
-
self.create_virtual_thread(0) # Create at least one thread
|
| 279 |
-
|
| 280 |
-
time_slice = 0.01 # 10ms time slice per thread
|
| 281 |
-
|
| 282 |
-
while True:
|
| 283 |
-
current_thread = self.thread_scheduler.get_current_thread(self.core_id)
|
| 284 |
-
|
| 285 |
-
if current_thread is None:
|
| 286 |
-
break # No threads to execute
|
| 287 |
-
|
| 288 |
-
if current_thread.status == "terminated":
|
| 289 |
-
self.thread_scheduler.schedule_next_thread(self.core_id)
|
| 290 |
-
continue
|
| 291 |
-
|
| 292 |
-
# Execute instructions for current thread
|
| 293 |
-
start_time = time.time()
|
| 294 |
-
instruction_count = 0
|
| 295 |
-
|
| 296 |
-
while (time.time() - start_time) < time_slice and instruction_count < 100:
|
| 297 |
-
try:
|
| 298 |
-
instruction = self.fetch()
|
| 299 |
-
decoded_instruction = self.decode(instruction)
|
| 300 |
-
self.execute_with_threading(decoded_instruction)
|
| 301 |
-
|
| 302 |
-
if decoded_instruction and decoded_instruction.get('op') == 'HLT':
|
| 303 |
-
current_thread.status = "terminated"
|
| 304 |
-
break
|
| 305 |
-
|
| 306 |
-
instruction_count += 1
|
| 307 |
-
|
| 308 |
-
except Exception as e:
|
| 309 |
-
print(f"Core {self.core_id} Thread {current_thread.thread_id} error: {e}")
|
| 310 |
-
current_thread.status = "terminated"
|
| 311 |
-
break
|
| 312 |
-
|
| 313 |
-
# Schedule next thread
|
| 314 |
-
self.thread_scheduler.schedule_next_thread(self.core_id)
|
| 315 |
-
|
| 316 |
-
# Small delay to prevent busy waiting
|
| 317 |
-
time.sleep(0.001)
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
class EnhancedMultiCoreCPU(MultiCoreCPU):
|
| 321 |
-
"""Enhanced multi-core CPU with GPU integration and threading support."""
|
| 322 |
-
|
| 323 |
-
def __init__(self, num_cores: int = 50, gpu_driver: Optional[VirtualGPUDriver] = None):
|
| 324 |
-
# Initialize with enhanced cores
|
| 325 |
-
self.num_cores = num_cores
|
| 326 |
-
self.total_cores = 50000 # Virtual GPU cores, not CPU cores
|
| 327 |
-
self.cores_per_sm = self.total_cores // num_cores
|
| 328 |
-
|
| 329 |
-
# Create enhanced cores
|
| 330 |
-
self.cores = []
|
| 331 |
-
for i in range(num_cores):
|
| 332 |
-
core = EnhancedCore(i)
|
| 333 |
-
if gpu_driver:
|
| 334 |
-
gpu_interface = CPUGPUInterface(gpu_driver)
|
| 335 |
-
core.connect_gpu_interface(gpu_interface)
|
| 336 |
-
self.cores.append(core)
|
| 337 |
-
|
| 338 |
-
self.shared_ram = None
|
| 339 |
-
self.shared_interrupt_handler = None
|
| 340 |
-
self.gpu_driver = gpu_driver
|
| 341 |
-
|
| 342 |
-
# Threading statistics
|
| 343 |
-
self.total_threads_created = 0
|
| 344 |
-
|
| 345 |
-
def create_threads_on_all_cores(self, threads_per_core: int = 2):
|
| 346 |
-
"""Create virtual threads on all cores to achieve 100 total threads."""
|
| 347 |
-
total_threads = 0
|
| 348 |
-
for core in self.cores:
|
| 349 |
-
for _ in range(threads_per_core):
|
| 350 |
-
thread_id = core.create_virtual_thread()
|
| 351 |
-
if thread_id != -1:
|
| 352 |
-
total_threads += 1
|
| 353 |
-
self.total_threads_created += 1
|
| 354 |
-
|
| 355 |
-
print(f"Created {total_threads} virtual threads across {self.num_cores} cores")
|
| 356 |
-
return total_threads
|
| 357 |
-
|
| 358 |
-
def get_threading_stats(self) -> Dict[str, Any]:
|
| 359 |
-
"""Get threading statistics across all cores."""
|
| 360 |
-
stats = {
|
| 361 |
-
"total_cores": self.num_cores,
|
| 362 |
-
"total_threads_created": self.total_threads_created,
|
| 363 |
-
"active_threads_per_core": {},
|
| 364 |
-
"total_active_threads": 0
|
| 365 |
-
}
|
| 366 |
-
|
| 367 |
-
for core in self.cores:
|
| 368 |
-
thread_count = core.thread_scheduler.get_total_thread_count()
|
| 369 |
-
stats["active_threads_per_core"][core.core_id] = thread_count
|
| 370 |
-
stats["total_active_threads"] += thread_count
|
| 371 |
-
|
| 372 |
-
return stats
|
| 373 |
-
|
| 374 |
-
def get_gpu_stats(self) -> Dict[str, Any]:
|
| 375 |
-
"""Get GPU-related statistics."""
|
| 376 |
-
if self.gpu_driver:
|
| 377 |
-
return self.gpu_driver.get_driver_stats()
|
| 378 |
-
return {"error": "No GPU driver connected"}
|
| 379 |
-
|
| 380 |
-
def __str__(self):
|
| 381 |
-
threading_stats = self.get_threading_stats()
|
| 382 |
-
return (f"EnhancedMultiCoreCPU with {self.num_cores} cores, "
|
| 383 |
-
f"{threading_stats['total_active_threads']} active threads, "
|
| 384 |
-
f"GPU {'connected' if self.gpu_driver else 'not connected'}")
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
if __name__ == "__main__":
|
| 388 |
-
# Test the enhanced CPU with GPU integration
|
| 389 |
-
print("Testing Enhanced CPU with GPU Integration...")
|
| 390 |
-
|
| 391 |
-
# This would normally be connected to actual GPU components
|
| 392 |
-
# For testing, we'll create a mock setup
|
| 393 |
-
|
| 394 |
-
# Create enhanced CPU
|
| 395 |
-
enhanced_cpu = EnhancedMultiCoreCPU(num_cores=4) # Use 4 cores for testing
|
| 396 |
-
|
| 397 |
-
# Create threads
|
| 398 |
-
threads_created = enhanced_cpu.create_threads_on_all_cores(threads_per_core=2)
|
| 399 |
-
print(f"Created {threads_created} threads")
|
| 400 |
-
|
| 401 |
-
# Get stats
|
| 402 |
-
threading_stats = enhanced_cpu.get_threading_stats()
|
| 403 |
-
print(f"Threading stats: {threading_stats}")
|
| 404 |
-
|
| 405 |
-
print(f"Enhanced CPU: {enhanced_cpu}")
|
| 406 |
-
print("Enhanced CPU test completed")
|
| 407 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
virtual_hardware/vgpu.py
DELETED
|
@@ -1,283 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
vGPU Core Processor Module
|
| 3 |
-
|
| 4 |
-
This module implements the central orchestrator of the virtual GPU, managing
|
| 5 |
-
workload distribution across 800 SMs and 50,000 cores, and coordinating
|
| 6 |
-
operations between all other modules.
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
import asyncio
|
| 10 |
-
import time
|
| 11 |
-
from collections import deque
|
| 12 |
-
from enum import Enum
|
| 13 |
-
from typing import Dict, List, Optional, Any
|
| 14 |
-
from dataclasses import dataclass
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
class TaskType(Enum):
|
| 18 |
-
"""Enumeration of task types that can be processed by the vGPU."""
|
| 19 |
-
RENDER_PIXEL_BLOCK = "render_pixel_block"
|
| 20 |
-
RENDER_CLEAR = "render_clear"
|
| 21 |
-
RENDER_RECT = "render_rect"
|
| 22 |
-
RENDER_IMAGE = "render_image"
|
| 23 |
-
AI_MATRIX_MULTIPLY = "ai_matrix_multiply"
|
| 24 |
-
AI_VECTOR_OP = "ai_vector_op"
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
class TaskStatus(Enum):
|
| 28 |
-
"""Enumeration of task statuses."""
|
| 29 |
-
PENDING = "pending"
|
| 30 |
-
IN_PROGRESS = "in_progress"
|
| 31 |
-
COMPLETED = "completed"
|
| 32 |
-
FAILED = "failed"
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
@dataclass
|
| 36 |
-
class Task:
|
| 37 |
-
"""Represents a single task to be processed by the vGPU."""
|
| 38 |
-
task_id: str
|
| 39 |
-
task_type: TaskType
|
| 40 |
-
payload: Dict[str, Any]
|
| 41 |
-
sm_id: Optional[int] = None
|
| 42 |
-
status: TaskStatus = TaskStatus.PENDING
|
| 43 |
-
created_time: float = 0.0
|
| 44 |
-
start_time: float = 0.0
|
| 45 |
-
end_time: float = 0.0
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
class StreamingMultiprocessor:
|
| 49 |
-
"""Represents a single Streaming Multiprocessor (SM) in the vGPU."""
|
| 50 |
-
|
| 51 |
-
def __init__(self, sm_id: int, cores_per_sm: int = 62):
|
| 52 |
-
self.sm_id = sm_id
|
| 53 |
-
self.cores_per_sm = cores_per_sm
|
| 54 |
-
self.task_queue = deque()
|
| 55 |
-
self.current_task: Optional[Task] = None
|
| 56 |
-
self.is_busy = False
|
| 57 |
-
self.total_tasks_processed = 0
|
| 58 |
-
|
| 59 |
-
def add_task(self, task: Task) -> None:
|
| 60 |
-
"""Add a task to this SM's queue."""
|
| 61 |
-
task.sm_id = self.sm_id
|
| 62 |
-
self.task_queue.append(task)
|
| 63 |
-
|
| 64 |
-
def get_next_task(self) -> Optional[Task]:
|
| 65 |
-
"""Get the next task from the queue."""
|
| 66 |
-
if self.task_queue and not self.is_busy:
|
| 67 |
-
task = self.task_queue.popleft()
|
| 68 |
-
self.current_task = task
|
| 69 |
-
self.is_busy = True
|
| 70 |
-
task.status = TaskStatus.IN_PROGRESS
|
| 71 |
-
task.start_time = time.time()
|
| 72 |
-
return task
|
| 73 |
-
return None
|
| 74 |
-
|
| 75 |
-
def complete_task(self) -> Optional[Task]:
|
| 76 |
-
"""Mark the current task as completed."""
|
| 77 |
-
if self.current_task:
|
| 78 |
-
self.current_task.status = TaskStatus.COMPLETED
|
| 79 |
-
self.current_task.end_time = time.time()
|
| 80 |
-
completed_task = self.current_task
|
| 81 |
-
self.current_task = None
|
| 82 |
-
self.is_busy = False
|
| 83 |
-
self.total_tasks_processed += 1
|
| 84 |
-
return completed_task
|
| 85 |
-
return None
|
| 86 |
-
|
| 87 |
-
def get_queue_length(self) -> int:
|
| 88 |
-
"""Get the current queue length."""
|
| 89 |
-
return len(self.task_queue)
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
class VirtualGPU:
|
| 93 |
-
"""
|
| 94 |
-
The main Virtual GPU class that orchestrates all operations.
|
| 95 |
-
|
| 96 |
-
This class manages 800 SMs with a total of 50,000 cores, handles task
|
| 97 |
-
distribution, and coordinates with other modules like VRAM, renderer, and AI.
|
| 98 |
-
"""
|
| 99 |
-
|
| 100 |
-
def __init__(self, num_sms: int = 800, total_cores: int = 50000):
|
| 101 |
-
self.num_sms = num_sms
|
| 102 |
-
self.total_cores = total_cores
|
| 103 |
-
self.cores_per_sm = total_cores // num_sms
|
| 104 |
-
|
| 105 |
-
# Initialize Streaming Multiprocessors
|
| 106 |
-
self.sms: List[StreamingMultiprocessor] = []
|
| 107 |
-
for i in range(num_sms):
|
| 108 |
-
# Distribute cores evenly, with some SMs getting an extra core if needed
|
| 109 |
-
cores_for_this_sm = self.cores_per_sm
|
| 110 |
-
if i < (total_cores % num_sms):
|
| 111 |
-
cores_for_this_sm += 1
|
| 112 |
-
self.sms.append(StreamingMultiprocessor(i, cores_for_this_sm))
|
| 113 |
-
|
| 114 |
-
# Global task management
|
| 115 |
-
self.pending_tasks = deque()
|
| 116 |
-
self.completed_tasks = deque()
|
| 117 |
-
self.task_counter = 0
|
| 118 |
-
|
| 119 |
-
# GPU state
|
| 120 |
-
self.is_running = False
|
| 121 |
-
self.clock_cycle = 0
|
| 122 |
-
self.tick_rate = 60 # Hz
|
| 123 |
-
|
| 124 |
-
# Module references (to be set by external initialization)
|
| 125 |
-
self.vram = None
|
| 126 |
-
self.renderer = None
|
| 127 |
-
self.ai_accelerator = None
|
| 128 |
-
self.driver = None
|
| 129 |
-
|
| 130 |
-
def set_modules(self, vram, renderer, ai_accelerator, driver):
|
| 131 |
-
"""Set references to other vGPU modules."""
|
| 132 |
-
self.vram = vram
|
| 133 |
-
self.renderer = renderer
|
| 134 |
-
self.ai_accelerator = ai_accelerator
|
| 135 |
-
self.driver = driver
|
| 136 |
-
|
| 137 |
-
def submit_task(self, task_type: TaskType, payload: Dict[str, Any]) -> str:
|
| 138 |
-
"""Submit a new task to the vGPU."""
|
| 139 |
-
task_id = f"task_{self.task_counter}"
|
| 140 |
-
self.task_counter += 1
|
| 141 |
-
|
| 142 |
-
task = Task(
|
| 143 |
-
task_id=task_id,
|
| 144 |
-
task_type=task_type,
|
| 145 |
-
payload=payload,
|
| 146 |
-
created_time=time.time()
|
| 147 |
-
)
|
| 148 |
-
|
| 149 |
-
self.pending_tasks.append(task)
|
| 150 |
-
return task_id
|
| 151 |
-
|
| 152 |
-
def distribute_tasks(self) -> None:
|
| 153 |
-
"""Distribute pending tasks to available SMs using round-robin."""
|
| 154 |
-
sm_index = 0
|
| 155 |
-
max_queue_length = 10 # Prevent any SM from being overloaded
|
| 156 |
-
|
| 157 |
-
while self.pending_tasks:
|
| 158 |
-
# Find an SM that's not overloaded
|
| 159 |
-
attempts = 0
|
| 160 |
-
while attempts < self.num_sms:
|
| 161 |
-
current_sm = self.sms[sm_index]
|
| 162 |
-
if current_sm.get_queue_length() < max_queue_length:
|
| 163 |
-
task = self.pending_tasks.popleft()
|
| 164 |
-
current_sm.add_task(task)
|
| 165 |
-
break
|
| 166 |
-
sm_index = (sm_index + 1) % self.num_sms
|
| 167 |
-
attempts += 1
|
| 168 |
-
|
| 169 |
-
if attempts >= self.num_sms:
|
| 170 |
-
# All SMs are overloaded, break to avoid infinite loop
|
| 171 |
-
break
|
| 172 |
-
|
| 173 |
-
sm_index = (sm_index + 1) % self.num_sms
|
| 174 |
-
|
| 175 |
-
def process_sm_tasks(self) -> None:
|
| 176 |
-
"""Process tasks on all SMs."""
|
| 177 |
-
for sm in self.sms:
|
| 178 |
-
# Start a new task if the SM is idle
|
| 179 |
-
if not sm.is_busy:
|
| 180 |
-
task = sm.get_next_task()
|
| 181 |
-
if task:
|
| 182 |
-
# Task will be processed in the next step
|
| 183 |
-
pass
|
| 184 |
-
|
| 185 |
-
# Process the current task (simulate work completion)
|
| 186 |
-
if sm.current_task:
|
| 187 |
-
# Simulate task processing by calling appropriate module
|
| 188 |
-
self._execute_task(sm.current_task)
|
| 189 |
-
completed_task = sm.complete_task()
|
| 190 |
-
if completed_task:
|
| 191 |
-
self.completed_tasks.append(completed_task)
|
| 192 |
-
|
| 193 |
-
def _execute_task(self, task: Task) -> None:
|
| 194 |
-
"""Execute a specific task by calling the appropriate module."""
|
| 195 |
-
try:
|
| 196 |
-
if task.task_type == TaskType.RENDER_CLEAR and self.renderer:
|
| 197 |
-
self.renderer.clear(**task.payload)
|
| 198 |
-
elif task.task_type == TaskType.RENDER_RECT and self.renderer:
|
| 199 |
-
self.renderer.draw_rect(**task.payload)
|
| 200 |
-
elif task.task_type == TaskType.RENDER_IMAGE and self.renderer:
|
| 201 |
-
self.renderer.draw_image(**task.payload)
|
| 202 |
-
elif task.task_type == TaskType.AI_MATRIX_MULTIPLY and self.ai_accelerator:
|
| 203 |
-
self.ai_accelerator.matrix_multiply(**task.payload)
|
| 204 |
-
elif task.task_type == TaskType.AI_VECTOR_OP and self.ai_accelerator:
|
| 205 |
-
self.ai_accelerator.vector_operation(**task.payload)
|
| 206 |
-
else:
|
| 207 |
-
print(f"Unknown task type: {task.task_type}")
|
| 208 |
-
task.status = TaskStatus.FAILED
|
| 209 |
-
except Exception as e:
|
| 210 |
-
print(f"Error executing task {task.task_id}: {e}")
|
| 211 |
-
task.status = TaskStatus.FAILED
|
| 212 |
-
|
| 213 |
-
async def tick(self) -> None:
|
| 214 |
-
"""Main GPU tick cycle."""
|
| 215 |
-
self.clock_cycle += 1
|
| 216 |
-
|
| 217 |
-
# 1. Distribute pending tasks to SMs
|
| 218 |
-
self.distribute_tasks()
|
| 219 |
-
|
| 220 |
-
# 2. Process tasks on all SMs
|
| 221 |
-
self.process_sm_tasks()
|
| 222 |
-
|
| 223 |
-
# 3. Handle any driver commands
|
| 224 |
-
if self.driver:
|
| 225 |
-
await self.driver.process_commands()
|
| 226 |
-
|
| 227 |
-
async def run(self) -> None:
|
| 228 |
-
"""Main GPU execution loop."""
|
| 229 |
-
self.is_running = True
|
| 230 |
-
tick_interval = 1.0 / self.tick_rate
|
| 231 |
-
|
| 232 |
-
print(f"Starting vGPU with {self.num_sms} SMs and {self.total_cores} cores")
|
| 233 |
-
print(f"Tick rate: {self.tick_rate} Hz")
|
| 234 |
-
|
| 235 |
-
while self.is_running:
|
| 236 |
-
start_time = time.time()
|
| 237 |
-
|
| 238 |
-
await self.tick()
|
| 239 |
-
|
| 240 |
-
# Maintain consistent tick rate
|
| 241 |
-
elapsed = time.time() - start_time
|
| 242 |
-
if elapsed < tick_interval:
|
| 243 |
-
await asyncio.sleep(tick_interval - elapsed)
|
| 244 |
-
|
| 245 |
-
def stop(self) -> None:
|
| 246 |
-
"""Stop the GPU execution."""
|
| 247 |
-
self.is_running = False
|
| 248 |
-
|
| 249 |
-
def get_stats(self) -> Dict[str, Any]:
|
| 250 |
-
"""Get current GPU statistics."""
|
| 251 |
-
total_tasks_processed = sum(sm.total_tasks_processed for sm in self.sms)
|
| 252 |
-
total_queue_length = sum(sm.get_queue_length() for sm in self.sms)
|
| 253 |
-
busy_sms = sum(1 for sm in self.sms if sm.is_busy)
|
| 254 |
-
|
| 255 |
-
return {
|
| 256 |
-
"clock_cycle": self.clock_cycle,
|
| 257 |
-
"total_sms": self.num_sms,
|
| 258 |
-
"total_cores": self.total_cores,
|
| 259 |
-
"busy_sms": busy_sms,
|
| 260 |
-
"total_tasks_processed": total_tasks_processed,
|
| 261 |
-
"pending_tasks": len(self.pending_tasks),
|
| 262 |
-
"total_queue_length": total_queue_length,
|
| 263 |
-
"completed_tasks": len(self.completed_tasks)
|
| 264 |
-
}
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
if __name__ == "__main__":
|
| 268 |
-
# Basic test of the vGPU
|
| 269 |
-
async def test_vgpu():
|
| 270 |
-
vgpu = VirtualGPU()
|
| 271 |
-
|
| 272 |
-
# Submit some test tasks
|
| 273 |
-
vgpu.submit_task(TaskType.RENDER_CLEAR, {"color": (255, 0, 0)})
|
| 274 |
-
vgpu.submit_task(TaskType.RENDER_RECT, {"x": 10, "y": 10, "width": 100, "height": 50, "color": (0, 255, 0)})
|
| 275 |
-
|
| 276 |
-
# Run a few ticks
|
| 277 |
-
for _ in range(5):
|
| 278 |
-
await vgpu.tick()
|
| 279 |
-
print(f"Stats: {vgpu.get_stats()}")
|
| 280 |
-
await asyncio.sleep(0.1)
|
| 281 |
-
|
| 282 |
-
asyncio.run(test_vgpu())
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
virtual_hardware/virtual_gpu_driver.py
DELETED
|
@@ -1,348 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Virtual GPU Driver Module
|
| 3 |
-
|
| 4 |
-
This module provides the interface between the simulated CPU and the virtual GPU.
|
| 5 |
-
It handles command queues, data transfers, and status management for CPU-GPU communication.
|
| 6 |
-
"""
|
| 7 |
-
|
| 8 |
-
import asyncio
|
| 9 |
-
import queue
|
| 10 |
-
import threading
|
| 11 |
-
import time
|
| 12 |
-
from typing import Dict, Any, Optional, List
|
| 13 |
-
from dataclasses import dataclass
|
| 14 |
-
from enum import Enum
|
| 15 |
-
|
| 16 |
-
# Import virtual GPU components
|
| 17 |
-
from virtual_gpu.vgpu import VirtualGPU, TaskType, Task
|
| 18 |
-
from virtual_gpu.vram import VRAM
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
class GPUCommandType(Enum):
|
| 22 |
-
"""Types of commands that can be sent to the GPU."""
|
| 23 |
-
RENDER_CLEAR = "render_clear"
|
| 24 |
-
RENDER_RECT = "render_rect"
|
| 25 |
-
RENDER_IMAGE = "render_image"
|
| 26 |
-
AI_INFERENCE = "ai_inference"
|
| 27 |
-
AI_MATRIX_MULTIPLY = "ai_matrix_multiply"
|
| 28 |
-
AI_VECTOR_OP = "ai_vector_op"
|
| 29 |
-
DATA_TRANSFER = "data_transfer"
|
| 30 |
-
MEMORY_ALLOC = "memory_alloc"
|
| 31 |
-
MEMORY_FREE = "memory_free"
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
@dataclass
|
| 35 |
-
class GPUCommand:
|
| 36 |
-
"""Represents a command to be executed by the GPU."""
|
| 37 |
-
command_id: str
|
| 38 |
-
command_type: GPUCommandType
|
| 39 |
-
payload: Dict[str, Any]
|
| 40 |
-
cpu_core_id: int
|
| 41 |
-
status: str = "pending"
|
| 42 |
-
result: Optional[Any] = None
|
| 43 |
-
created_time: float = 0.0
|
| 44 |
-
completed_time: float = 0.0
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
class VirtualGPUDriver:
|
| 48 |
-
"""
|
| 49 |
-
Virtual GPU Driver that provides the interface between CPU and GPU.
|
| 50 |
-
|
| 51 |
-
This driver manages command queues, data transfers, and status tracking
|
| 52 |
-
for CPU-GPU communication in the integrated virtual hardware system.
|
| 53 |
-
"""
|
| 54 |
-
|
| 55 |
-
def __init__(self, vgpu: VirtualGPU, vram: VRAM):
|
| 56 |
-
self.vgpu = vgpu
|
| 57 |
-
self.vram = vram
|
| 58 |
-
|
| 59 |
-
# Command management
|
| 60 |
-
self.command_queue = queue.Queue()
|
| 61 |
-
self.completed_commands = {}
|
| 62 |
-
self.command_counter = 0
|
| 63 |
-
|
| 64 |
-
# Status tracking
|
| 65 |
-
self.is_running = False
|
| 66 |
-
self.driver_thread = None
|
| 67 |
-
|
| 68 |
-
# Statistics
|
| 69 |
-
self.total_commands_processed = 0
|
| 70 |
-
self.total_data_transferred = 0
|
| 71 |
-
|
| 72 |
-
def start_driver(self):
|
| 73 |
-
"""Start the GPU driver in a separate thread."""
|
| 74 |
-
if not self.is_running:
|
| 75 |
-
self.is_running = True
|
| 76 |
-
self.driver_thread = threading.Thread(target=self._driver_loop, daemon=True)
|
| 77 |
-
self.driver_thread.start()
|
| 78 |
-
print("Virtual GPU Driver started")
|
| 79 |
-
|
| 80 |
-
def stop_driver(self):
|
| 81 |
-
"""Stop the GPU driver."""
|
| 82 |
-
self.is_running = False
|
| 83 |
-
if self.driver_thread:
|
| 84 |
-
self.driver_thread.join(timeout=1.0)
|
| 85 |
-
print("Virtual GPU Driver stopped")
|
| 86 |
-
|
| 87 |
-
def submit_command(self, command_type: GPUCommandType, payload: Dict[str, Any],
|
| 88 |
-
cpu_core_id: int = 0) -> str:
|
| 89 |
-
"""Submit a command to the GPU and return command ID."""
|
| 90 |
-
command_id = f"gpu_cmd_{self.command_counter}"
|
| 91 |
-
self.command_counter += 1
|
| 92 |
-
|
| 93 |
-
command = GPUCommand(
|
| 94 |
-
command_id=command_id,
|
| 95 |
-
command_type=command_type,
|
| 96 |
-
payload=payload,
|
| 97 |
-
cpu_core_id=cpu_core_id,
|
| 98 |
-
created_time=time.time()
|
| 99 |
-
)
|
| 100 |
-
|
| 101 |
-
self.command_queue.put(command)
|
| 102 |
-
return command_id
|
| 103 |
-
|
| 104 |
-
def get_command_status(self, command_id: str) -> Optional[str]:
|
| 105 |
-
"""Get the status of a command."""
|
| 106 |
-
if command_id in self.completed_commands:
|
| 107 |
-
return self.completed_commands[command_id].status
|
| 108 |
-
return "pending"
|
| 109 |
-
|
| 110 |
-
def get_command_result(self, command_id: str) -> Optional[Any]:
|
| 111 |
-
"""Get the result of a completed command."""
|
| 112 |
-
if command_id in self.completed_commands:
|
| 113 |
-
command = self.completed_commands[command_id]
|
| 114 |
-
if command.status == "completed":
|
| 115 |
-
return command.result
|
| 116 |
-
return None
|
| 117 |
-
|
| 118 |
-
def wait_for_command(self, command_id: str, timeout: float = 10.0) -> bool:
|
| 119 |
-
"""Wait for a command to complete."""
|
| 120 |
-
start_time = time.time()
|
| 121 |
-
while time.time() - start_time < timeout:
|
| 122 |
-
if command_id in self.completed_commands:
|
| 123 |
-
return self.completed_commands[command_id].status == "completed"
|
| 124 |
-
time.sleep(0.01)
|
| 125 |
-
return False
|
| 126 |
-
|
| 127 |
-
def transfer_data_to_vram(self, data: Any, name: str, delay_ms: float = 0.0) -> Optional[str]:
|
| 128 |
-
"""Transfer data from CPU RAM to VRAM."""
|
| 129 |
-
try:
|
| 130 |
-
texture_id = self.vram.transfer_from_ram(name, data, delay_ms)
|
| 131 |
-
if texture_id:
|
| 132 |
-
self.total_data_transferred += len(data) if hasattr(data, '__len__') else 0
|
| 133 |
-
return texture_id
|
| 134 |
-
except Exception as e:
|
| 135 |
-
print(f"Error transferring data to VRAM: {e}")
|
| 136 |
-
return None
|
| 137 |
-
|
| 138 |
-
def create_framebuffer(self, width: int, height: int, channels: int = 3,
|
| 139 |
-
name: Optional[str] = None) -> Optional[str]:
|
| 140 |
-
"""Create a framebuffer in VRAM."""
|
| 141 |
-
try:
|
| 142 |
-
return self.vram.create_framebuffer(width, height, channels, name)
|
| 143 |
-
except Exception as e:
|
| 144 |
-
print(f"Error creating framebuffer: {e}")
|
| 145 |
-
return None
|
| 146 |
-
|
| 147 |
-
def _driver_loop(self):
|
| 148 |
-
"""Main driver loop that processes commands."""
|
| 149 |
-
while self.is_running:
|
| 150 |
-
try:
|
| 151 |
-
# Process commands from the queue
|
| 152 |
-
try:
|
| 153 |
-
command = self.command_queue.get_nowait()
|
| 154 |
-
self._process_command(command)
|
| 155 |
-
except queue.Empty:
|
| 156 |
-
pass
|
| 157 |
-
|
| 158 |
-
# Small delay to prevent busy waiting
|
| 159 |
-
time.sleep(0.001)
|
| 160 |
-
|
| 161 |
-
except Exception as e:
|
| 162 |
-
print(f"Error in GPU driver loop: {e}")
|
| 163 |
-
|
| 164 |
-
def _process_command(self, command: GPUCommand):
|
| 165 |
-
"""Process a single GPU command."""
|
| 166 |
-
try:
|
| 167 |
-
command.status = "processing"
|
| 168 |
-
|
| 169 |
-
if command.command_type == GPUCommandType.RENDER_CLEAR:
|
| 170 |
-
# Submit clear task to vGPU
|
| 171 |
-
task_id = self.vgpu.submit_task(TaskType.RENDER_CLEAR, command.payload)
|
| 172 |
-
command.result = {"task_id": task_id}
|
| 173 |
-
|
| 174 |
-
elif command.command_type == GPUCommandType.RENDER_RECT:
|
| 175 |
-
# Submit rectangle rendering task to vGPU
|
| 176 |
-
task_id = self.vgpu.submit_task(TaskType.RENDER_RECT, command.payload)
|
| 177 |
-
command.result = {"task_id": task_id}
|
| 178 |
-
|
| 179 |
-
elif command.command_type == GPUCommandType.RENDER_IMAGE:
|
| 180 |
-
# Submit image rendering task to vGPU
|
| 181 |
-
task_id = self.vgpu.submit_task(TaskType.RENDER_IMAGE, command.payload)
|
| 182 |
-
command.result = {"task_id": task_id}
|
| 183 |
-
|
| 184 |
-
elif command.command_type == GPUCommandType.AI_MATRIX_MULTIPLY:
|
| 185 |
-
# Submit matrix multiplication task to vGPU
|
| 186 |
-
task_id = self.vgpu.submit_task(TaskType.AI_MATRIX_MULTIPLY, command.payload)
|
| 187 |
-
command.result = {"task_id": task_id}
|
| 188 |
-
|
| 189 |
-
elif command.command_type == GPUCommandType.AI_VECTOR_OP:
|
| 190 |
-
# Submit vector operation task to vGPU
|
| 191 |
-
task_id = self.vgpu.submit_task(TaskType.AI_VECTOR_OP, command.payload)
|
| 192 |
-
command.result = {"task_id": task_id}
|
| 193 |
-
|
| 194 |
-
elif command.command_type == GPUCommandType.DATA_TRANSFER:
|
| 195 |
-
# Handle data transfer to VRAM
|
| 196 |
-
data = command.payload.get("data")
|
| 197 |
-
name = command.payload.get("name", f"transfer_{command.command_id}")
|
| 198 |
-
delay_ms = command.payload.get("delay_ms", 0.0)
|
| 199 |
-
|
| 200 |
-
texture_id = self.transfer_data_to_vram(data, name, delay_ms)
|
| 201 |
-
command.result = {"texture_id": texture_id}
|
| 202 |
-
|
| 203 |
-
elif command.command_type == GPUCommandType.MEMORY_ALLOC:
|
| 204 |
-
# Create framebuffer or allocate memory
|
| 205 |
-
width = command.payload.get("width", 1920)
|
| 206 |
-
height = command.payload.get("height", 1080)
|
| 207 |
-
channels = command.payload.get("channels", 3)
|
| 208 |
-
name = command.payload.get("name")
|
| 209 |
-
|
| 210 |
-
fb_id = self.create_framebuffer(width, height, channels, name)
|
| 211 |
-
command.result = {"framebuffer_id": fb_id}
|
| 212 |
-
|
| 213 |
-
elif command.command_type == GPUCommandType.MEMORY_FREE:
|
| 214 |
-
# Free framebuffer or memory
|
| 215 |
-
name = command.payload.get("name")
|
| 216 |
-
success = self.vram.delete_framebuffer(name)
|
| 217 |
-
command.result = {"success": success}
|
| 218 |
-
|
| 219 |
-
else:
|
| 220 |
-
command.status = "error"
|
| 221 |
-
command.result = {"error": f"Unknown command type: {command.command_type}"}
|
| 222 |
-
|
| 223 |
-
if command.status != "error":
|
| 224 |
-
command.status = "completed"
|
| 225 |
-
|
| 226 |
-
command.completed_time = time.time()
|
| 227 |
-
self.completed_commands[command.command_id] = command
|
| 228 |
-
self.total_commands_processed += 1
|
| 229 |
-
|
| 230 |
-
except Exception as e:
|
| 231 |
-
command.status = "error"
|
| 232 |
-
command.result = {"error": str(e)}
|
| 233 |
-
command.completed_time = time.time()
|
| 234 |
-
self.completed_commands[command.command_id] = command
|
| 235 |
-
print(f"Error processing GPU command {command.command_id}: {e}")
|
| 236 |
-
|
| 237 |
-
def get_driver_stats(self) -> Dict[str, Any]:
|
| 238 |
-
"""Get driver statistics."""
|
| 239 |
-
vgpu_stats = self.vgpu.get_stats()
|
| 240 |
-
vram_stats = self.vram.get_stats()
|
| 241 |
-
|
| 242 |
-
return {
|
| 243 |
-
"driver_status": "running" if self.is_running else "stopped",
|
| 244 |
-
"total_commands_processed": self.total_commands_processed,
|
| 245 |
-
"total_data_transferred": self.total_data_transferred,
|
| 246 |
-
"pending_commands": self.command_queue.qsize(),
|
| 247 |
-
"completed_commands": len(self.completed_commands),
|
| 248 |
-
"vgpu_stats": vgpu_stats,
|
| 249 |
-
"vram_stats": vram_stats
|
| 250 |
-
}
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
# CPU Extensions for GPU Communication
|
| 254 |
-
class CPUGPUInterface:
|
| 255 |
-
"""
|
| 256 |
-
Interface for CPU cores to communicate with the GPU driver.
|
| 257 |
-
|
| 258 |
-
This class provides high-level methods that CPU cores can use to
|
| 259 |
-
interact with the virtual GPU without dealing with low-level details.
|
| 260 |
-
"""
|
| 261 |
-
|
| 262 |
-
def __init__(self, gpu_driver: VirtualGPUDriver):
|
| 263 |
-
self.gpu_driver = gpu_driver
|
| 264 |
-
|
| 265 |
-
def gpu_clear_screen(self, color: tuple = (0, 0, 0), core_id: int = 0) -> str:
|
| 266 |
-
"""Clear the screen with the specified color."""
|
| 267 |
-
payload = {"color": color}
|
| 268 |
-
return self.gpu_driver.submit_command(GPUCommandType.RENDER_CLEAR, payload, core_id)
|
| 269 |
-
|
| 270 |
-
def gpu_draw_rect(self, x: int, y: int, width: int, height: int,
|
| 271 |
-
color: tuple = (255, 255, 255), core_id: int = 0) -> str:
|
| 272 |
-
"""Draw a rectangle on the screen."""
|
| 273 |
-
payload = {
|
| 274 |
-
"x": x, "y": y, "width": width, "height": height, "color": color
|
| 275 |
-
}
|
| 276 |
-
return self.gpu_driver.submit_command(GPUCommandType.RENDER_RECT, payload, core_id)
|
| 277 |
-
|
| 278 |
-
def gpu_ai_inference(self, model_data: Any, input_data: Any, core_id: int = 0) -> str:
|
| 279 |
-
"""Perform AI inference using the GPU."""
|
| 280 |
-
payload = {"model_data": model_data, "input_data": input_data}
|
| 281 |
-
return self.gpu_driver.submit_command(GPUCommandType.AI_INFERENCE, payload, core_id)
|
| 282 |
-
|
| 283 |
-
def gpu_matrix_multiply(self, matrix_a: Any, matrix_b: Any, core_id: int = 0) -> str:
|
| 284 |
-
"""Perform matrix multiplication on the GPU."""
|
| 285 |
-
payload = {"matrix_a": matrix_a, "matrix_b": matrix_b}
|
| 286 |
-
return self.gpu_driver.submit_command(GPUCommandType.AI_MATRIX_MULTIPLY, payload, core_id)
|
| 287 |
-
|
| 288 |
-
def gpu_transfer_data(self, data: Any, name: str, core_id: int = 0) -> str:
|
| 289 |
-
"""Transfer data to GPU VRAM."""
|
| 290 |
-
payload = {"data": data, "name": name}
|
| 291 |
-
return self.gpu_driver.submit_command(GPUCommandType.DATA_TRANSFER, payload, core_id)
|
| 292 |
-
|
| 293 |
-
def gpu_alloc_framebuffer(self, width: int, height: int, channels: int = 3,
|
| 294 |
-
name: Optional[str] = None, core_id: int = 0) -> str:
|
| 295 |
-
"""Allocate a framebuffer in GPU VRAM."""
|
| 296 |
-
payload = {"width": width, "height": height, "channels": channels, "name": name}
|
| 297 |
-
return self.gpu_driver.submit_command(GPUCommandType.MEMORY_ALLOC, payload, core_id)
|
| 298 |
-
|
| 299 |
-
def wait_for_gpu_task(self, command_id: str, timeout: float = 10.0) -> bool:
|
| 300 |
-
"""Wait for a GPU task to complete."""
|
| 301 |
-
return self.gpu_driver.wait_for_command(command_id, timeout)
|
| 302 |
-
|
| 303 |
-
def get_gpu_result(self, command_id: str) -> Optional[Any]:
|
| 304 |
-
"""Get the result of a completed GPU task."""
|
| 305 |
-
return self.gpu_driver.get_command_result(command_id)
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
if __name__ == "__main__":
|
| 309 |
-
# Test the GPU driver
|
| 310 |
-
from virtual_gpu.vgpu import VirtualGPU
|
| 311 |
-
from virtual_gpu.vram import VRAM
|
| 312 |
-
|
| 313 |
-
# Create virtual GPU and VRAM
|
| 314 |
-
vgpu = VirtualGPU(num_sms=800, total_cores=50000)
|
| 315 |
-
vram = VRAM(memory_size_gb=500)
|
| 316 |
-
|
| 317 |
-
# Create and start the driver
|
| 318 |
-
driver = VirtualGPUDriver(vgpu, vram)
|
| 319 |
-
driver.start_driver()
|
| 320 |
-
|
| 321 |
-
# Create CPU-GPU interface
|
| 322 |
-
cpu_gpu = CPUGPUInterface(driver)
|
| 323 |
-
|
| 324 |
-
# Test some operations
|
| 325 |
-
print("Testing GPU driver...")
|
| 326 |
-
|
| 327 |
-
# Clear screen
|
| 328 |
-
cmd_id = cpu_gpu.gpu_clear_screen((255, 0, 0))
|
| 329 |
-
print(f"Clear screen command: {cmd_id}")
|
| 330 |
-
|
| 331 |
-
# Draw rectangle
|
| 332 |
-
cmd_id = cpu_gpu.gpu_draw_rect(100, 100, 200, 150, (0, 255, 0))
|
| 333 |
-
print(f"Draw rectangle command: {cmd_id}")
|
| 334 |
-
|
| 335 |
-
# Transfer some data
|
| 336 |
-
test_data = b"Hello GPU!"
|
| 337 |
-
cmd_id = cpu_gpu.gpu_transfer_data(test_data, "test_data")
|
| 338 |
-
print(f"Data transfer command: {cmd_id}")
|
| 339 |
-
|
| 340 |
-
# Wait a bit and check stats
|
| 341 |
-
time.sleep(1)
|
| 342 |
-
stats = driver.get_driver_stats()
|
| 343 |
-
print(f"Driver stats: {stats}")
|
| 344 |
-
|
| 345 |
-
# Stop the driver
|
| 346 |
-
driver.stop_driver()
|
| 347 |
-
print("GPU driver test completed")
|
| 348 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
virtual_hardware/virtual_ram.py
DELETED
|
@@ -1,385 +0,0 @@
|
|
| 1 |
-
"""
|
| 2 |
-
Virtual RAM Module - 128GB System Memory Abstraction
|
| 3 |
-
|
| 4 |
-
This module implements a symbolic representation of 128GB system RAM using
|
| 5 |
-
efficient data structures and lazy allocation strategies. It avoids allocating
|
| 6 |
-
real memory and uses dictionaries or sparse mappings to simulate blocks.
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
import time
|
| 10 |
-
from typing import Dict, Any, Optional, Union
|
| 11 |
-
from dataclasses import dataclass
|
| 12 |
-
import numpy as np
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
@dataclass
|
| 16 |
-
class RAMBlock:
|
| 17 |
-
"""Represents a block of memory in the symbolic RAM."""
|
| 18 |
-
name: str
|
| 19 |
-
size_bytes: int
|
| 20 |
-
allocated_time: float
|
| 21 |
-
last_accessed: float
|
| 22 |
-
access_count: int = 0
|
| 23 |
-
# We use a symbolic representation instead of actual data
|
| 24 |
-
# The data field will be None for large blocks to avoid memory allocation
|
| 25 |
-
data: Optional[Union[np.ndarray, bytes]] = None
|
| 26 |
-
is_symbolic: bool = True # True if this is a symbolic block (no real data)
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
class VirtualRAM:
|
| 30 |
-
"""
|
| 31 |
-
Virtual RAM class that simulates 128GB of system memory symbolically.
|
| 32 |
-
|
| 33 |
-
This class provides block allocation, tracking, and transfer capabilities
|
| 34 |
-
without actually allocating large amounts of physical memory.
|
| 35 |
-
"""
|
| 36 |
-
|
| 37 |
-
def __init__(self, capacity_gb: int = 128):
|
| 38 |
-
self.capacity_bytes = capacity_gb * 1024 * 1024 * 1024 # Convert GB to bytes
|
| 39 |
-
self.capacity_gb = capacity_gb
|
| 40 |
-
|
| 41 |
-
# Block registry - stores metadata about allocated blocks
|
| 42 |
-
self.blocks: Dict[str, RAMBlock] = {}
|
| 43 |
-
|
| 44 |
-
# Memory usage tracking
|
| 45 |
-
self.allocated_bytes = 0
|
| 46 |
-
self.allocation_counter = 0
|
| 47 |
-
|
| 48 |
-
# Access simulation parameters
|
| 49 |
-
self.access_delay_ms = 0.1 # Simulated RAM access delay
|
| 50 |
-
self.transfer_bandwidth_gbps = 51.2 # DDR5-6400 bandwidth
|
| 51 |
-
|
| 52 |
-
# Statistics
|
| 53 |
-
self.total_allocations = 0
|
| 54 |
-
self.total_deallocations = 0
|
| 55 |
-
self.total_accesses = 0
|
| 56 |
-
self.total_transfers = 0
|
| 57 |
-
|
| 58 |
-
print(f"VirtualRAM initialized with {capacity_gb}GB capacity")
|
| 59 |
-
|
| 60 |
-
def allocate_block(self, name: str, size_bytes: int,
|
| 61 |
-
store_data: bool = False) -> bool:
|
| 62 |
-
"""
|
| 63 |
-
Allocate a block of memory symbolically.
|
| 64 |
-
|
| 65 |
-
Args:
|
| 66 |
-
name: Unique name for the block
|
| 67 |
-
size_bytes: Size of the block in bytes
|
| 68 |
-
store_data: If True, actually allocate small amounts of real data for testing
|
| 69 |
-
If False (default), only store metadata symbolically
|
| 70 |
-
|
| 71 |
-
Returns:
|
| 72 |
-
True if allocation successful, False if not enough space or name exists
|
| 73 |
-
"""
|
| 74 |
-
# Check if name already exists
|
| 75 |
-
if name in self.blocks:
|
| 76 |
-
print(f"Block '{name}' already exists")
|
| 77 |
-
return False
|
| 78 |
-
|
| 79 |
-
# Check if we have enough capacity
|
| 80 |
-
if self.allocated_bytes + size_bytes > self.capacity_bytes:
|
| 81 |
-
print(f"Not enough capacity: requested {size_bytes:,} bytes, "
|
| 82 |
-
f"available {self.capacity_bytes - self.allocated_bytes:,} bytes")
|
| 83 |
-
return False
|
| 84 |
-
|
| 85 |
-
# Create the block
|
| 86 |
-
current_time = time.time()
|
| 87 |
-
# For all blocks, we store only metadata to avoid memory issues
|
| 88 |
-
actual_data = None
|
| 89 |
-
is_symbolic = True
|
| 90 |
-
|
| 91 |
-
# If store_data is explicitly requested and size is very small, we can store actual data
|
| 92 |
-
if store_data and size_bytes <= 1024 * 1024 * 10: # Up to 10MB for actual data
|
| 93 |
-
actual_data = np.zeros(size_bytes, dtype=np.uint8)
|
| 94 |
-
is_symbolic = False
|
| 95 |
-
print(f"Allocated real data for block \'{name}\' ({size_bytes:,} bytes)")
|
| 96 |
-
else:
|
| 97 |
-
print(f"Created symbolic block \'{name}\' of {size_bytes:,} bytes")
|
| 98 |
-
block = RAMBlock(
|
| 99 |
-
name=name,
|
| 100 |
-
size_bytes=size_bytes,
|
| 101 |
-
allocated_time=current_time,
|
| 102 |
-
last_accessed=current_time,
|
| 103 |
-
data=actual_data,
|
| 104 |
-
is_symbolic=is_symbolic
|
| 105 |
-
)
|
| 106 |
-
|
| 107 |
-
self.blocks[name] = block
|
| 108 |
-
self.allocated_bytes += size_bytes
|
| 109 |
-
self.total_allocations += 1
|
| 110 |
-
self.allocation_counter += 1
|
| 111 |
-
|
| 112 |
-
print(f"Allocated block '{name}': {size_bytes:,} bytes "
|
| 113 |
-
f"({'symbolic' if is_symbolic else 'real data'})")
|
| 114 |
-
|
| 115 |
-
return True
|
| 116 |
-
|
| 117 |
-
def get_block(self, name: str) -> Optional[RAMBlock]:
|
| 118 |
-
"""
|
| 119 |
-
Retrieve a block by name and simulate access delay.
|
| 120 |
-
|
| 121 |
-
Args:
|
| 122 |
-
name: Name of the block to retrieve
|
| 123 |
-
|
| 124 |
-
Returns:
|
| 125 |
-
RAMBlock if found, None otherwise
|
| 126 |
-
"""
|
| 127 |
-
if name not in self.blocks:
|
| 128 |
-
return None
|
| 129 |
-
|
| 130 |
-
# Simulate access delay
|
| 131 |
-
time.sleep(self.access_delay_ms / 1000.0)
|
| 132 |
-
|
| 133 |
-
# Update access statistics
|
| 134 |
-
block = self.blocks[name]
|
| 135 |
-
block.last_accessed = time.time()
|
| 136 |
-
block.access_count += 1
|
| 137 |
-
self.total_accesses += 1
|
| 138 |
-
|
| 139 |
-
return block
|
| 140 |
-
|
| 141 |
-
def release_block(self, name: str) -> bool:
|
| 142 |
-
"""
|
| 143 |
-
Deallocate a block of memory.
|
| 144 |
-
|
| 145 |
-
Args:
|
| 146 |
-
name: Name of the block to deallocate
|
| 147 |
-
|
| 148 |
-
Returns:
|
| 149 |
-
True if deallocation successful, False if block not found
|
| 150 |
-
"""
|
| 151 |
-
if name not in self.blocks:
|
| 152 |
-
print(f"Block '{name}' not found")
|
| 153 |
-
return False
|
| 154 |
-
|
| 155 |
-
block = self.blocks[name]
|
| 156 |
-
self.allocated_bytes -= block.size_bytes
|
| 157 |
-
self.total_deallocations += 1
|
| 158 |
-
|
| 159 |
-
del self.blocks[name]
|
| 160 |
-
|
| 161 |
-
print(f"Released block '{name}': {block.size_bytes:,} bytes")
|
| 162 |
-
return True
|
| 163 |
-
|
| 164 |
-
def transfer_to_vram(self, block_name: str, vram_instance,
|
| 165 |
-
vram_name: Optional[str] = None) -> Optional[str]:
|
| 166 |
-
"""
|
| 167 |
-
Transfer a RAM block to VRAM with delay simulation.
|
| 168 |
-
|
| 169 |
-
Args:
|
| 170 |
-
block_name: Name of the RAM block to transfer
|
| 171 |
-
vram_instance: Instance of VRAM to transfer to
|
| 172 |
-
vram_name: Optional name for the block in VRAM
|
| 173 |
-
|
| 174 |
-
Returns:
|
| 175 |
-
VRAM block ID if successful, None otherwise
|
| 176 |
-
"""
|
| 177 |
-
# Get the block from RAM
|
| 178 |
-
block = self.get_block(block_name)
|
| 179 |
-
if block is None:
|
| 180 |
-
print(f"Block '{block_name}' not found in RAM")
|
| 181 |
-
return None
|
| 182 |
-
|
| 183 |
-
# Calculate transfer time based on bandwidth
|
| 184 |
-
transfer_time_ms = (block.size_bytes / (self.transfer_bandwidth_gbps * 1e9)) * 1000
|
| 185 |
-
|
| 186 |
-
print(f"Transferring '{block_name}' ({block.size_bytes:,} bytes) "
|
| 187 |
-
f"from RAM to VRAM (estimated {transfer_time_ms:.2f}ms)")
|
| 188 |
-
|
| 189 |
-
# Prepare data for transfer
|
| 190 |
-
if block.is_symbolic:
|
| 191 |
-
# For symbolic blocks, create a small representative data sample
|
| 192 |
-
sample_size = min(1024, block.size_bytes) # 1KB sample
|
| 193 |
-
transfer_data = np.random.randint(0, 256, sample_size, dtype=np.uint8)
|
| 194 |
-
print(f"Using {sample_size} byte sample for symbolic block transfer")
|
| 195 |
-
else:
|
| 196 |
-
# Use actual data
|
| 197 |
-
transfer_data = block.data
|
| 198 |
-
|
| 199 |
-
# Perform the transfer to VRAM
|
| 200 |
-
if vram_name is None:
|
| 201 |
-
vram_name = f"ram_transfer_{block_name}"
|
| 202 |
-
|
| 203 |
-
vram_id = vram_instance.transfer_from_ram(vram_name, transfer_data,
|
| 204 |
-
delay_ms=transfer_time_ms)
|
| 205 |
-
|
| 206 |
-
if vram_id:
|
| 207 |
-
self.total_transfers += 1
|
| 208 |
-
print(f"Successfully transferred '{block_name}' to VRAM as '{vram_id}'")
|
| 209 |
-
else:
|
| 210 |
-
print(f"Failed to transfer '{block_name}' to VRAM")
|
| 211 |
-
|
| 212 |
-
return vram_id
|
| 213 |
-
|
| 214 |
-
def create_tensor_block(self, name: str, shape: tuple, dtype=np.float32) -> bool:
|
| 215 |
-
"""
|
| 216 |
-
Create a tensor block with specified shape and data type.
|
| 217 |
-
|
| 218 |
-
Args:
|
| 219 |
-
name: Name for the tensor block
|
| 220 |
-
shape: Shape of the tensor (e.g., (1024, 1024, 3))
|
| 221 |
-
dtype: Data type of the tensor
|
| 222 |
-
|
| 223 |
-
Returns:
|
| 224 |
-
True if creation successful, False otherwise
|
| 225 |
-
"""
|
| 226 |
-
# Calculate size in bytes
|
| 227 |
-
element_size = np.dtype(dtype).itemsize
|
| 228 |
-
total_elements = np.prod(shape)
|
| 229 |
-
size_bytes = total_elements * element_size
|
| 230 |
-
|
| 231 |
-
# Allocate the block symbolically
|
| 232 |
-
success = self.allocate_block(name, size_bytes, store_data=False)
|
| 233 |
-
|
| 234 |
-
if success:
|
| 235 |
-
# Store tensor metadata
|
| 236 |
-
block = self.blocks[name]
|
| 237 |
-
block.tensor_shape = shape
|
| 238 |
-
block.tensor_dtype = dtype
|
| 239 |
-
print(f"Created tensor block '{name}' with shape {shape} and dtype {dtype}")
|
| 240 |
-
|
| 241 |
-
return success
|
| 242 |
-
|
| 243 |
-
def info(self) -> Dict[str, Any]:
|
| 244 |
-
"""
|
| 245 |
-
Get comprehensive information about the Virtual RAM state.
|
| 246 |
-
|
| 247 |
-
Returns:
|
| 248 |
-
Dictionary containing RAM usage statistics and metadata
|
| 249 |
-
"""
|
| 250 |
-
used_bytes = self.allocated_bytes
|
| 251 |
-
free_bytes = self.capacity_bytes - used_bytes
|
| 252 |
-
utilization_percent = (used_bytes / self.capacity_bytes) * 100
|
| 253 |
-
|
| 254 |
-
# Calculate average block size
|
| 255 |
-
avg_block_size = used_bytes / len(self.blocks) if self.blocks else 0
|
| 256 |
-
|
| 257 |
-
# Find largest and smallest blocks
|
| 258 |
-
largest_block = max(self.blocks.values(), key=lambda b: b.size_bytes) if self.blocks else None
|
| 259 |
-
smallest_block = min(self.blocks.values(), key=lambda b: b.size_bytes) if self.blocks else None
|
| 260 |
-
|
| 261 |
-
# Count symbolic vs real blocks
|
| 262 |
-
symbolic_blocks = sum(1 for b in self.blocks.values() if b.is_symbolic)
|
| 263 |
-
real_blocks = len(self.blocks) - symbolic_blocks
|
| 264 |
-
|
| 265 |
-
info_dict = {
|
| 266 |
-
"capacity_gb": self.capacity_gb,
|
| 267 |
-
"capacity_bytes": self.capacity_bytes,
|
| 268 |
-
"used_bytes": used_bytes,
|
| 269 |
-
"free_bytes": free_bytes,
|
| 270 |
-
"utilization_percent": utilization_percent,
|
| 271 |
-
"total_blocks": len(self.blocks),
|
| 272 |
-
"symbolic_blocks": symbolic_blocks,
|
| 273 |
-
"real_data_blocks": real_blocks,
|
| 274 |
-
"avg_block_size_bytes": avg_block_size,
|
| 275 |
-
"largest_block_name": largest_block.name if largest_block else None,
|
| 276 |
-
"largest_block_size": largest_block.size_bytes if largest_block else 0,
|
| 277 |
-
"smallest_block_name": smallest_block.name if smallest_block else None,
|
| 278 |
-
"smallest_block_size": smallest_block.size_bytes if smallest_block else 0,
|
| 279 |
-
"total_allocations": self.total_allocations,
|
| 280 |
-
"total_deallocations": self.total_deallocations,
|
| 281 |
-
"total_accesses": self.total_accesses,
|
| 282 |
-
"total_transfers": self.total_transfers,
|
| 283 |
-
"block_names": list(self.blocks.keys())
|
| 284 |
-
}
|
| 285 |
-
|
| 286 |
-
return info_dict
|
| 287 |
-
|
| 288 |
-
def print_info(self) -> None:
|
| 289 |
-
"""Print a formatted summary of Virtual RAM information."""
|
| 290 |
-
info = self.info()
|
| 291 |
-
|
| 292 |
-
print("\n" + "="*50)
|
| 293 |
-
print("VIRTUAL RAM INFORMATION")
|
| 294 |
-
print("="*50)
|
| 295 |
-
print(f"Capacity: {info['capacity_gb']} GB ({info['capacity_bytes']:,} bytes)")
|
| 296 |
-
print(f"Used: {info['used_bytes']:,} bytes ({info['utilization_percent']:.2f}%)")
|
| 297 |
-
print(f"Free: {info['free_bytes']:,} bytes")
|
| 298 |
-
print(f"Total Blocks: {info['total_blocks']}")
|
| 299 |
-
print(f" - Symbolic blocks: {info['symbolic_blocks']}")
|
| 300 |
-
print(f" - Real data blocks: {info['real_data_blocks']}")
|
| 301 |
-
|
| 302 |
-
if info['total_blocks'] > 0:
|
| 303 |
-
print(f"Average block size: {info['avg_block_size_bytes']:,.0f} bytes")
|
| 304 |
-
print(f"Largest block: '{info['largest_block_name']}' ({info['largest_block_size']:,} bytes)")
|
| 305 |
-
print(f"Smallest block: '{info['smallest_block_name']}' ({info['smallest_block_size']:,} bytes)")
|
| 306 |
-
|
| 307 |
-
print(f"\nStatistics:")
|
| 308 |
-
print(f" - Total allocations: {info['total_allocations']}")
|
| 309 |
-
print(f" - Total deallocations: {info['total_deallocations']}")
|
| 310 |
-
print(f" - Total accesses: {info['total_accesses']}")
|
| 311 |
-
print(f" - Total transfers: {info['total_transfers']}")
|
| 312 |
-
|
| 313 |
-
if info['block_names']:
|
| 314 |
-
print(f"\nBlock names: {', '.join(info['block_names'])}")
|
| 315 |
-
|
| 316 |
-
print("="*50)
|
| 317 |
-
|
| 318 |
-
def simulate_workload(self, num_operations: int = 100) -> None:
|
| 319 |
-
"""
|
| 320 |
-
Simulate a typical workload with allocations, accesses, and deallocations.
|
| 321 |
-
|
| 322 |
-
Args:
|
| 323 |
-
num_operations: Number of operations to simulate
|
| 324 |
-
"""
|
| 325 |
-
print(f"\nSimulating workload with {num_operations} operations...")
|
| 326 |
-
|
| 327 |
-
import random
|
| 328 |
-
|
| 329 |
-
for i in range(num_operations):
|
| 330 |
-
operation = random.choice(['allocate', 'access', 'deallocate'])
|
| 331 |
-
|
| 332 |
-
if operation == 'allocate' and len(self.blocks) < 50: # Limit to 50 blocks
|
| 333 |
-
size = random.randint(1024, 100 * 1024 * 1024) # 1KB to 100MB
|
| 334 |
-
name = f"workload_block_{i}"
|
| 335 |
-
self.allocate_block(name, size)
|
| 336 |
-
|
| 337 |
-
elif operation == 'access' and self.blocks:
|
| 338 |
-
block_name = random.choice(list(self.blocks.keys()))
|
| 339 |
-
self.get_block(block_name)
|
| 340 |
-
|
| 341 |
-
elif operation == 'deallocate' and self.blocks:
|
| 342 |
-
block_name = random.choice(list(self.blocks.keys()))
|
| 343 |
-
self.release_block(block_name)
|
| 344 |
-
|
| 345 |
-
print(f"Workload simulation completed.")
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
if __name__ == "__main__":
|
| 349 |
-
# Test the VirtualRAM module
|
| 350 |
-
print("Testing VirtualRAM module...")
|
| 351 |
-
|
| 352 |
-
# Create a VirtualRAM instance with 128GB capacity
|
| 353 |
-
vram = VirtualRAM(capacity_gb=128)
|
| 354 |
-
|
| 355 |
-
# Test basic allocation
|
| 356 |
-
print("\n1. Testing basic allocation...")
|
| 357 |
-
vram.allocate_block("small_buffer", 1024 * 1024, store_data=True) # 1MB with real data
|
| 358 |
-
vram.allocate_block("medium_buffer", 50 * 1024 * 1024) # 50MB symbolic
|
| 359 |
-
vram.allocate_block("large_tensor", 16 * 1024 * 1024 * 1024) # 16GB symbolic
|
| 360 |
-
|
| 361 |
-
# Test tensor creation
|
| 362 |
-
print("\n2. Testing tensor creation...")
|
| 363 |
-
vram.create_tensor_block("ai_weights", (1000, 1000, 512), np.float32)
|
| 364 |
-
vram.create_tensor_block("image_batch", (32, 224, 224, 3), np.uint8)
|
| 365 |
-
|
| 366 |
-
# Test block access
|
| 367 |
-
print("\n3. Testing block access...")
|
| 368 |
-
block = vram.get_block("small_buffer")
|
| 369 |
-
if block:
|
| 370 |
-
print(f"Accessed block: {block.name}, size: {block.size_bytes:,} bytes")
|
| 371 |
-
|
| 372 |
-
# Test info display
|
| 373 |
-
print("\n4. Testing info display...")
|
| 374 |
-
vram.print_info()
|
| 375 |
-
|
| 376 |
-
# Test workload simulation
|
| 377 |
-
print("\n5. Testing workload simulation...")
|
| 378 |
-
vram.simulate_workload(20)
|
| 379 |
-
|
| 380 |
-
# Final info
|
| 381 |
-
print("\n6. Final state...")
|
| 382 |
-
vram.print_info()
|
| 383 |
-
|
| 384 |
-
print("\nVirtualRAM test completed!")
|
| 385 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
virtual_hardware/virtual_ssd.py
DELETED
|
@@ -1,318 +0,0 @@
|
|
| 1 |
-
|
| 2 |
-
"""
|
| 3 |
-
Virtual SSD Main Module - Integrates all components
|
| 4 |
-
"""
|
| 5 |
-
import os
|
| 6 |
-
import shutil
|
| 7 |
-
import json
|
| 8 |
-
import base64
|
| 9 |
-
from typing import Optional, Dict
|
| 10 |
-
from builtins import open
|
| 11 |
-
|
| 12 |
-
from .virtual_flash import VirtualFlash
|
| 13 |
-
from .file_system_map import FileSystemMap
|
| 14 |
-
from .ssd_controller import SSDController
|
| 15 |
-
from .virtual_ram_buffer import VirtualRAMBuffer
|
| 16 |
-
from .virtual_driver import VirtualDriver
|
| 17 |
-
from .virtual_os import VirtualOS
|
| 18 |
-
from .app_interface import AppInterface
|
| 19 |
-
|
| 20 |
-
from .persistent_virtual_disk import PersistentVirtualDisk
|
| 21 |
-
from .volatile_virtual_disk import VolatileVirtualDisk
|
| 22 |
-
|
| 23 |
-
class VirtualSSD:
|
| 24 |
-
"""
|
| 25 |
-
Main class to integrate all virtual SSD components.
|
| 26 |
-
Provides mount, shutdown, and application-level file operations.
|
| 27 |
-
"""
|
| 28 |
-
|
| 29 |
-
def __init__(self, capacity_gb: int = 2048, page_size: int = 4096, pages_per_block: int = 256):
|
| 30 |
-
self.capacity_gb = capacity_gb
|
| 31 |
-
self.page_size = page_size
|
| 32 |
-
self.pages_per_block = pages_per_block
|
| 33 |
-
|
| 34 |
-
# Calculate total logical blocks based on capacity and page size
|
| 35 |
-
# Assuming 1 logical block = 1 page for simplicity in initial FTL
|
| 36 |
-
self.total_logical_blocks = (capacity_gb * 1024 * 1024 * 1024) // page_size
|
| 37 |
-
|
| 38 |
-
self.flash: Optional[VirtualFlash] = None
|
| 39 |
-
self.ram_buffer: Optional[VirtualRAMBuffer] = None
|
| 40 |
-
self.controller: Optional[SSDController] = None
|
| 41 |
-
self.file_system: Optional[FileSystemMap] = None
|
| 42 |
-
self.driver: Optional[VirtualDriver] = None
|
| 43 |
-
self.os: Optional[VirtualOS] = None
|
| 44 |
-
self.app_interface: Optional[AppInterface] = None
|
| 45 |
-
|
| 46 |
-
self.mounted = False
|
| 47 |
-
|
| 48 |
-
# Persistent Virtual Disk
|
| 49 |
-
self.pvd = PersistentVirtualDisk(capacity_gb, page_size, pages_per_block)
|
| 50 |
-
self.vvd: Optional[VolatileVirtualDisk] = None
|
| 51 |
-
|
| 52 |
-
print("VirtualSSD instance created.")
|
| 53 |
-
|
| 54 |
-
def mount(self):
|
| 55 |
-
"""
|
| 56 |
-
Mount the virtual SSD, initializing all components.
|
| 57 |
-
"""
|
| 58 |
-
if self.mounted:
|
| 59 |
-
print("Virtual SSD already mounted.")
|
| 60 |
-
return
|
| 61 |
-
|
| 62 |
-
print("Mounting Virtual SSD...")
|
| 63 |
-
|
| 64 |
-
# Ensure storage directory exists for snapshot
|
| 65 |
-
os.makedirs("virtual_ssd_data", exist_ok=True)
|
| 66 |
-
|
| 67 |
-
try:
|
| 68 |
-
# Load PVD state
|
| 69 |
-
pvd_snapshot_path = os.path.join("virtual_ssd_data", "pvd_snapshot.json")
|
| 70 |
-
pvd_snapshot_data = None
|
| 71 |
-
if os.path.exists(pvd_snapshot_path):
|
| 72 |
-
with open(pvd_snapshot_path, 'r') as f:
|
| 73 |
-
pvd_snapshot_data = json.load(f)
|
| 74 |
-
self.pvd.load_from_storage(pvd_snapshot_data['pvd_state'])
|
| 75 |
-
else:
|
| 76 |
-
self.pvd.load_from_storage() # Initialize fresh state
|
| 77 |
-
|
| 78 |
-
self.flash = self.pvd.get_flash()
|
| 79 |
-
self.controller = self.pvd.get_controller()
|
| 80 |
-
self.file_system = self.pvd.get_file_system()
|
| 81 |
-
|
| 82 |
-
self.vvd = VolatileVirtualDisk(self.flash, self.file_system, self.controller)
|
| 83 |
-
|
| 84 |
-
self.ram_buffer = VirtualRAMBuffer(capacity_bytes=128 * 1024 * 1024) # 128MB buffer
|
| 85 |
-
self.driver = VirtualDriver(self.vvd, self.page_size) # Driver interacts with VVD
|
| 86 |
-
self.os = VirtualOS(self.file_system, self.driver, self.ram_buffer)
|
| 87 |
-
self.app_interface = AppInterface(self.os)
|
| 88 |
-
|
| 89 |
-
self.mounted = True
|
| 90 |
-
print("Virtual SSD mounted successfully.")
|
| 91 |
-
except Exception as e:
|
| 92 |
-
print(f"Error mounting Virtual SSD: {e}")
|
| 93 |
-
self.shutdown() # Attempt to clean up
|
| 94 |
-
self.mounted = False
|
| 95 |
-
|
| 96 |
-
def shutdown(self):
|
| 97 |
-
"""
|
| 98 |
-
Shutdown the virtual SSD, saving all states to a snapshot.
|
| 99 |
-
"""
|
| 100 |
-
if not self.mounted:
|
| 101 |
-
print("Virtual SSD not mounted.")
|
| 102 |
-
return
|
| 103 |
-
|
| 104 |
-
print("Shutting down Virtual SSD...")
|
| 105 |
-
|
| 106 |
-
try:
|
| 107 |
-
if self.app_interface:
|
| 108 |
-
self.app_interface.sync() # Ensure all data is flushed
|
| 109 |
-
if self.vvd:
|
| 110 |
-
self.vvd.shutdown() # Flush dirty pages to PVD
|
| 111 |
-
if self.os:
|
| 112 |
-
self.os.shutdown()
|
| 113 |
-
if self.driver:
|
| 114 |
-
self.driver.shutdown()
|
| 115 |
-
|
| 116 |
-
# Save PVD state
|
| 117 |
-
os.makedirs("virtual_ssd_data", exist_ok=True) # Ensure directory exists
|
| 118 |
-
pvd_snapshot_path = os.path.join("virtual_ssd_data", "pvd_snapshot.json")
|
| 119 |
-
pvd_state = self.pvd.save_to_storage()
|
| 120 |
-
with open(pvd_snapshot_path, 'w') as f:
|
| 121 |
-
json.dump({"pvd_state": pvd_state}, f, indent=2)
|
| 122 |
-
print(f"PVD state saved to snapshot: {pvd_snapshot_path}")
|
| 123 |
-
|
| 124 |
-
self.pvd.shutdown()
|
| 125 |
-
|
| 126 |
-
self.mounted = False
|
| 127 |
-
print("Virtual SSD shutdown complete.")
|
| 128 |
-
except Exception as e:
|
| 129 |
-
print(f"Error during Virtual SSD shutdown: {e}")
|
| 130 |
-
|
| 131 |
-
def save_file(self, filename: str, data: bytes) -> bool:
|
| 132 |
-
"""
|
| 133 |
-
Save a file to the virtual SSD.
|
| 134 |
-
"""
|
| 135 |
-
if not self.mounted or not self.app_interface:
|
| 136 |
-
print("Error: Virtual SSD not mounted.")
|
| 137 |
-
return False
|
| 138 |
-
return self.app_interface.save(filename, data)
|
| 139 |
-
|
| 140 |
-
def read_file(self, filename: str) -> Optional[bytes]:
|
| 141 |
-
"""
|
| 142 |
-
Read a file from the virtual SSD.
|
| 143 |
-
"""
|
| 144 |
-
if not self.mounted or not self.app_interface:
|
| 145 |
-
print("Error: Virtual SSD not mounted.")
|
| 146 |
-
return None
|
| 147 |
-
return self.app_interface.load(filename)
|
| 148 |
-
|
| 149 |
-
def delete_file(self, filename: str) -> bool:
|
| 150 |
-
"""
|
| 151 |
-
Delete a file from the virtual SSD.
|
| 152 |
-
"""
|
| 153 |
-
if not self.mounted or not self.app_interface:
|
| 154 |
-
print("Error: Virtual SSD not mounted.")
|
| 155 |
-
return False
|
| 156 |
-
return self.app_interface.delete(filename)
|
| 157 |
-
|
| 158 |
-
def list_files(self) -> Dict:
|
| 159 |
-
"""
|
| 160 |
-
List all files and their metadata on the virtual SSD.
|
| 161 |
-
"""
|
| 162 |
-
if not self.mounted or not self.app_interface:
|
| 163 |
-
print("Error: Virtual SSD not mounted.")
|
| 164 |
-
return {}
|
| 165 |
-
return self.app_interface.list_files()
|
| 166 |
-
|
| 167 |
-
def get_capacity_info(self) -> Dict:
|
| 168 |
-
"""
|
| 169 |
-
Get capacity information of the virtual SSD.
|
| 170 |
-
"""
|
| 171 |
-
if not self.mounted or not self.app_interface:
|
| 172 |
-
print("Error: Virtual SSD not mounted.")
|
| 173 |
-
return {}
|
| 174 |
-
return self.app_interface.get_capacity_info()
|
| 175 |
-
|
| 176 |
-
def get_full_stats(self) -> Dict:
|
| 177 |
-
"""
|
| 178 |
-
Get comprehensive statistics from all layers.
|
| 179 |
-
"""
|
| 180 |
-
if not self.mounted or not self.app_interface or not self.os or not self.flash or not self.controller:
|
| 181 |
-
print("Error: Virtual SSD not mounted or components missing.")
|
| 182 |
-
return {}
|
| 183 |
-
|
| 184 |
-
return {
|
| 185 |
-
"flash_stats": self.flash.get_flash_stats(),
|
| 186 |
-
"ftl_stats": self.controller.get_ftl_stats(),
|
| 187 |
-
"file_system_stats": self.file_system.get_usage_stats(),
|
| 188 |
-
"ram_buffer_stats": self.ram_buffer.get_buffer_status()
|
| 189 |
-
}
|
| 190 |
-
|
| 191 |
-
def format_ssd(self):
|
| 192 |
-
"""
|
| 193 |
-
Formats the virtual SSD, deleting all data and resetting state.
|
| 194 |
-
"""
|
| 195 |
-
if self.mounted:
|
| 196 |
-
self.shutdown()
|
| 197 |
-
|
| 198 |
-
print("Formatting Virtual SSD...")
|
| 199 |
-
storage_dir = "virtual_ssd_data"
|
| 200 |
-
if os.path.exists(storage_dir):
|
| 201 |
-
shutil.rmtree(storage_dir)
|
| 202 |
-
print(f"Removed existing storage directory: {storage_dir}")
|
| 203 |
-
|
| 204 |
-
self.pvd = PersistentVirtualDisk(self.capacity_gb, self.page_size, self.pages_per_block)
|
| 205 |
-
self.pvd.format_disk()
|
| 206 |
-
self.flash = self.pvd.get_flash()
|
| 207 |
-
self.controller = self.pvd.get_controller()
|
| 208 |
-
self.file_system = self.pvd.get_file_system()
|
| 209 |
-
|
| 210 |
-
self.ram_buffer = VirtualRAMBuffer(capacity_bytes=128 * 1024 * 1024)
|
| 211 |
-
self.driver = VirtualDriver(self.controller, self.page_size)
|
| 212 |
-
self.os = VirtualOS(self.file_system, self.driver, self.ram_buffer)
|
| 213 |
-
self.app_interface = AppInterface(self.os)
|
| 214 |
-
|
| 215 |
-
self.mounted = True
|
| 216 |
-
print("Virtual SSD formatted and re-mounted.")
|
| 217 |
-
|
| 218 |
-
def __del__(self):
|
| 219 |
-
"""
|
| 220 |
-
Ensure shutdown is called on object deletion.
|
| 221 |
-
"""
|
| 222 |
-
try:
|
| 223 |
-
if self.mounted:
|
| 224 |
-
self.shutdown()
|
| 225 |
-
except:
|
| 226 |
-
pass
|
| 227 |
-
|
| 228 |
-
if __name__ == "__main__":
|
| 229 |
-
# Example Usage
|
| 230 |
-
ssd = VirtualSSD(capacity_gb=2) # Create a 2GB virtual SSD for testing
|
| 231 |
-
ssd.mount()
|
| 232 |
-
|
| 233 |
-
# Test file operations
|
| 234 |
-
test_data_small = b"Hello, this is a small test file." * 10 # 330 bytes
|
| 235 |
-
test_data_large = b"This is a larger test file content." * 1000 # 35KB
|
| 236 |
-
test_data_very_large = b"A very large file for testing purposes. " * 100000 # ~4MB
|
| 237 |
-
|
| 238 |
-
print("\n--- Testing file saves ---")
|
| 239 |
-
ssd.save_file("small_file.txt", test_data_small)
|
| 240 |
-
ssd.save_file("large_file.bin", test_data_large)
|
| 241 |
-
ssd.save_file("video.mp4", test_data_very_large)
|
| 242 |
-
ssd.save_file("another_file.txt", b"Some more data.")
|
| 243 |
-
|
| 244 |
-
# Upload the created test file
|
| 245 |
-
with open("/home/ubuntu/test_upload_file.txt", "rb") as f:
|
| 246 |
-
uploaded_data = f.read()
|
| 247 |
-
ssd.save_file("uploaded_test_file.txt", uploaded_data)
|
| 248 |
-
print("Uploaded test_upload_file.txt to virtual SSD.")
|
| 249 |
-
|
| 250 |
-
print("\n--- Listing files ---")
|
| 251 |
-
files = ssd.list_files()
|
| 252 |
-
for filename, info in files.items():
|
| 253 |
-
print(f"File: {filename}, Size: {info['size']} bytes, Blocks: {len(info['blocks'])}")
|
| 254 |
-
|
| 255 |
-
print("\n--- Checking capacity info ---")
|
| 256 |
-
capacity_info = ssd.get_capacity_info()
|
| 257 |
-
print(f"Total: {capacity_info['total_gb']} GB, Used: {capacity_info['used_gb']} GB, Free: {capacity_info['free_gb']} GB, Usage: {capacity_info['usage_percent']:.2f}%")
|
| 258 |
-
|
| 259 |
-
print("\n--- Reading files ---")
|
| 260 |
-
read_small_data = ssd.read_file("small_file.txt")
|
| 261 |
-
print(f"Read small_file.txt: {read_small_data[:50]}...")
|
| 262 |
-
assert read_small_data == test_data_small
|
| 263 |
-
|
| 264 |
-
read_large_data = ssd.read_file("large_file.bin")
|
| 265 |
-
print(f"Read large_file.bin: {read_large_data[:50]}...")
|
| 266 |
-
assert read_large_data == test_data_large
|
| 267 |
-
|
| 268 |
-
read_video_data = ssd.read_file("video.mp4")
|
| 269 |
-
print(f"Read video.mp4: {read_video_data[:50]}...")
|
| 270 |
-
assert read_video_data == test_data_very_large
|
| 271 |
-
|
| 272 |
-
read_uploaded_data = ssd.read_file("uploaded_test_file.txt")
|
| 273 |
-
print(f"Read uploaded_test_file.txt: {read_uploaded_data[:50]}...")
|
| 274 |
-
assert read_uploaded_data == uploaded_data
|
| 275 |
-
|
| 276 |
-
print("\n--- Deleting a file ---")
|
| 277 |
-
ssd.delete_file("large_file.bin")
|
| 278 |
-
print("\n--- Listing files after deletion ---")
|
| 279 |
-
files = ssd.list_files()
|
| 280 |
-
for filename, info in files.items():
|
| 281 |
-
print(f"File: {filename}, Size: {info['size']} bytes, Blocks: {len(info['blocks'])}")
|
| 282 |
-
|
| 283 |
-
print("\n--- Checking capacity info after deletion ---")
|
| 284 |
-
capacity_info = ssd.get_capacity_info()
|
| 285 |
-
print(f"Total: {capacity_info['total_gb']} GB, Used: {capacity_info['used_gb']} GB, Free: {capacity_info['free_gb']} GB, Usage: {capacity_info['usage_percent']:.2f}%")
|
| 286 |
-
|
| 287 |
-
print("\n--- Testing persistence ---")
|
| 288 |
-
ssd.shutdown()
|
| 289 |
-
print("SSD shut down. Re-mounting to check persistence.")
|
| 290 |
-
ssd_reloaded = VirtualSSD(capacity_gb=2) # Same capacity
|
| 291 |
-
ssd_reloaded.mount()
|
| 292 |
-
|
| 293 |
-
print("\n--- Listing files after re-mount ---")
|
| 294 |
-
files_reloaded = ssd_reloaded.list_files()
|
| 295 |
-
for filename, info in files_reloaded.items():
|
| 296 |
-
print(f"File: {filename}, Size: {info['size']} bytes, Blocks: {len(info['blocks'])}")
|
| 297 |
-
|
| 298 |
-
read_small_data_reloaded = ssd_reloaded.read_file("small_file.txt")
|
| 299 |
-
assert read_small_data_reloaded == test_data_small
|
| 300 |
-
print("small_file.txt read successfully after remount.")
|
| 301 |
-
|
| 302 |
-
read_uploaded_data_reloaded = ssd_reloaded.read_file("uploaded_test_file.txt")
|
| 303 |
-
assert read_uploaded_data_reloaded == uploaded_data
|
| 304 |
-
print("uploaded_test_file.txt read successfully after remount.")
|
| 305 |
-
|
| 306 |
-
# Test formatting
|
| 307 |
-
print("\n--- Testing format ---")
|
| 308 |
-
ssd_reloaded.format_ssd()
|
| 309 |
-
print("\n--- Listing files after format ---")
|
| 310 |
-
files_after_format = ssd_reloaded.list_files()
|
| 311 |
-
print(f"Files after format: {files_after_format}")
|
| 312 |
-
assert not files_after_format
|
| 313 |
-
|
| 314 |
-
ssd_reloaded.shutdown()
|
| 315 |
-
print("All tests complete.")
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
virtual_hardware/vram.py
DELETED
|
@@ -1,361 +0,0 @@
|
|
| 1 |
-
import numpy as np
|
| 2 |
-
from collections import OrderedDict
|
| 3 |
-
from typing import Dict, Any, Optional, Tuple, Union
|
| 4 |
-
from dataclasses import dataclass
|
| 5 |
-
import time
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
@dataclass
|
| 9 |
-
class MemoryBlock:
|
| 10 |
-
"""Represents a block of memory in the symbolic VRAM."""
|
| 11 |
-
address: int
|
| 12 |
-
size: int
|
| 13 |
-
data: Optional[Any]
|
| 14 |
-
allocated_time: float
|
| 15 |
-
last_accessed: float
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
class Framebuffer:
|
| 19 |
-
"""Represents a 2D drawing surface in VRAM."""
|
| 20 |
-
|
| 21 |
-
def __init__(self, width: int, height: int, channels: int = 3, dtype=np.uint8):
|
| 22 |
-
self.width = width
|
| 23 |
-
self.height = height
|
| 24 |
-
self.channels = channels
|
| 25 |
-
self.dtype = dtype
|
| 26 |
-
|
| 27 |
-
# Create the pixel buffer symbolically to avoid large allocations
|
| 28 |
-
# The actual pixel data will be managed by the MemoryManager
|
| 29 |
-
self.pixel_buffer_address: Optional[int] = None
|
| 30 |
-
self.pixel_buffer_size: int = width * height * channels * np.dtype(dtype).itemsize
|
| 31 |
-
self.pixel_buffer = np.zeros((height, width, channels), dtype=dtype)
|
| 32 |
-
self.vram_address: Optional[int] = None # This is the address in the MemoryManager
|
| 33 |
-
|
| 34 |
-
def resize(self, new_width: int, new_height: int) -> None:
|
| 35 |
-
# No actual data to resize, just update symbolic size
|
| 36 |
-
self.width = new_width
|
| 37 |
-
self.height = new_height
|
| 38 |
-
self.pixel_buffer_size = new_width * new_height * self.channels * np.dtype(self.dtype).itemsize
|
| 39 |
-
|
| 40 |
-
def clear(self, color: Tuple[int, int, int]) -> None:
|
| 41 |
-
self.pixel_buffer[:, :] = color
|
| 42 |
-
|
| 43 |
-
def get_pixel(self, x: int, y: int) -> np.ndarray:
|
| 44 |
-
if 0 <= x < self.width and 0 <= y < self.height:
|
| 45 |
-
return self.pixel_buffer[y, x]
|
| 46 |
-
return np.zeros(self.channels, dtype=self.dtype)
|
| 47 |
-
def set_pixel(self, x: int, y: int, color: Tuple[int, int, int]) -> None:
|
| 48 |
-
if 0 <= x < self.width and 0 <= y < self.height:
|
| 49 |
-
self.pixel_buffer[y, x] = color[:self.channels]
|
| 50 |
-
|
| 51 |
-
def get_memory_usage(self) -> int:
|
| 52 |
-
"""Get the memory usage of this framebuffer in bytes."""
|
| 53 |
-
return self.pixel_buffer_size
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
class MemoryManager:
|
| 57 |
-
"""Manages the symbolic 500GB GDDR7 memory space."""
|
| 58 |
-
|
| 59 |
-
def __init__(self, total_memory_gb: int = 500, block_size_kb: int = 4):
|
| 60 |
-
self.total_memory_bytes = total_memory_gb * 1024 * 1024 * 1024 # 500GB
|
| 61 |
-
self.block_size_bytes = block_size_kb * 1024 # 4KB blocks
|
| 62 |
-
self.total_blocks = self.total_memory_bytes // self.block_size_bytes
|
| 63 |
-
|
| 64 |
-
# Symbolic memory space - only allocated blocks are stored
|
| 65 |
-
self.memory_blocks: Dict[int, MemoryBlock] = {}
|
| 66 |
-
|
| 67 |
-
# Free block tracking - use a list of free block ranges instead of a set of all blocks
|
| 68 |
-
self.free_block_ranges = [(0, self.total_blocks - 1)] # (start_block_id, end_block_id)
|
| 69 |
-
self.allocated_blocks = set() # Still track allocated blocks for quick lookup
|
| 70 |
-
|
| 71 |
-
# Address allocation counter
|
| 72 |
-
self.next_address = 0
|
| 73 |
-
|
| 74 |
-
def allocate_block(self, size_bytes: int) -> Optional[int]:
|
| 75 |
-
"""Allocate a block of memory and return its address."""
|
| 76 |
-
blocks_needed = (size_bytes + self.block_size_bytes - 1) // self.block_size_bytes
|
| 77 |
-
|
| 78 |
-
# Find a suitable contiguous block range
|
| 79 |
-
for i, (start, end) in enumerate(self.free_block_ranges):
|
| 80 |
-
available_blocks = end - start + 1
|
| 81 |
-
if available_blocks >= blocks_needed:
|
| 82 |
-
# Found a suitable range
|
| 83 |
-
base_block_id = start
|
| 84 |
-
|
| 85 |
-
# Update free_block_ranges
|
| 86 |
-
new_start = start + blocks_needed
|
| 87 |
-
if new_start <= end:
|
| 88 |
-
self.free_block_ranges[i] = (new_start, end)
|
| 89 |
-
else:
|
| 90 |
-
self.free_block_ranges.pop(i)
|
| 91 |
-
|
| 92 |
-
# Add to allocated_blocks
|
| 93 |
-
for j in range(blocks_needed):
|
| 94 |
-
self.allocated_blocks.add(base_block_id + j)
|
| 95 |
-
|
| 96 |
-
# Create memory block
|
| 97 |
-
base_address = base_block_id * self.block_size_bytes
|
| 98 |
-
|
| 99 |
-
memory_block = MemoryBlock(
|
| 100 |
-
address=base_address,
|
| 101 |
-
size=size_bytes,
|
| 102 |
-
data=bytearray(size_bytes), # Allocate actual bytearray for data
|
| 103 |
-
allocated_time=time.time(),
|
| 104 |
-
last_accessed=time.time()
|
| 105 |
-
)
|
| 106 |
-
self.memory_blocks[base_address] = memory_block
|
| 107 |
-
return base_address
|
| 108 |
-
|
| 109 |
-
return None # Out of memory
|
| 110 |
-
|
| 111 |
-
def deallocate_block(self, address: int) -> bool:
|
| 112 |
-
"""Deallocate a block of memory."""
|
| 113 |
-
if address in self.memory_blocks:
|
| 114 |
-
memory_block = self.memory_blocks[address]
|
| 115 |
-
blocks_to_free = (memory_block.size + self.block_size_bytes - 1) // self.block_size_bytes
|
| 116 |
-
|
| 117 |
-
base_block_id = address // self.block_size_bytes
|
| 118 |
-
for i in range(blocks_to_free):
|
| 119 |
-
block_id = base_block_id + i
|
| 120 |
-
if block_id in self.allocated_blocks:
|
| 121 |
-
self.allocated_blocks.remove(block_id)
|
| 122 |
-
# Add back to free_block_ranges (simple merge for now)
|
| 123 |
-
self.free_block_ranges.append((block_id, block_id))
|
| 124 |
-
self.free_block_ranges.sort() # Keep sorted for efficient merging
|
| 125 |
-
|
| 126 |
-
del self.memory_blocks[address]
|
| 127 |
-
return True
|
| 128 |
-
return False
|
| 129 |
-
|
| 130 |
-
def read_data(self, address: int, size: int) -> Optional[np.ndarray]:
|
| 131 |
-
"""Read data from memory."""
|
| 132 |
-
if address in self.memory_blocks:
|
| 133 |
-
memory_block = self.memory_blocks[address]
|
| 134 |
-
if memory_block.data is not None and size <= memory_block.size:
|
| 135 |
-
return np.frombuffer(memory_block.data[:size], dtype=np.uint8) # Return as numpy array
|
| 136 |
-
return None
|
| 137 |
-
|
| 138 |
-
def write_data(self, address: int, data: Union[np.ndarray, bytes]) -> bool:
|
| 139 |
-
"""Write data to memory."""
|
| 140 |
-
if address in self.memory_blocks:
|
| 141 |
-
memory_block = self.memory_blocks[address]
|
| 142 |
-
if memory_block.data is not None:
|
| 143 |
-
if isinstance(data, np.ndarray):
|
| 144 |
-
data_bytes = data.tobytes()
|
| 145 |
-
elif isinstance(data, bytes):
|
| 146 |
-
data_bytes = data
|
| 147 |
-
else:
|
| 148 |
-
raise TypeError("Data must be a NumPy array or bytes.")
|
| 149 |
-
|
| 150 |
-
if len(data_bytes) <= memory_block.size:
|
| 151 |
-
memory_block.data[:len(data_bytes)] = data_bytes
|
| 152 |
-
return True
|
| 153 |
-
return False
|
| 154 |
-
|
| 155 |
-
def get_memory_stats(self) -> Dict[str, Any]:
|
| 156 |
-
"""Get memory usage statistics."""
|
| 157 |
-
allocated_bytes = sum(block.size for block in self.memory_blocks.values())
|
| 158 |
-
free_bytes = self.total_memory_bytes - allocated_bytes
|
| 159 |
-
|
| 160 |
-
return {
|
| 161 |
-
"total_memory_gb": self.total_memory_bytes / (1024**3),
|
| 162 |
-
"allocated_bytes": allocated_bytes,
|
| 163 |
-
"free_bytes": free_bytes,
|
| 164 |
-
"allocated_blocks_count": len(self.allocated_blocks),
|
| 165 |
-
"free_block_ranges_count": len(self.free_block_ranges),
|
| 166 |
-
"utilization_percent": (allocated_bytes / self.total_memory_bytes) * 100 if self.total_memory_bytes > 0 else 0
|
| 167 |
-
}
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
class VRAM:
|
| 171 |
-
"""
|
| 172 |
-
Main VRAM class that provides the interface for the 500GB GDDR7 memory.
|
| 173 |
-
|
| 174 |
-
This class combines the MemoryManager for low-level memory operations
|
| 175 |
-
with higher-level abstractions like Framebuffers.
|
| 176 |
-
"""
|
| 177 |
-
|
| 178 |
-
def __init__(self, memory_size_gb: int = 500):
|
| 179 |
-
self.memory_manager = MemoryManager(memory_size_gb)
|
| 180 |
-
|
| 181 |
-
# Cache for frequently accessed data (simulates L1/L2 cache)
|
| 182 |
-
self.cache_size = 1000 # Number of cache entries
|
| 183 |
-
self.cache = OrderedDict()
|
| 184 |
-
|
| 185 |
-
# Framebuffer registry
|
| 186 |
-
self.framebuffers: Dict[str, Framebuffer] = {}
|
| 187 |
-
self.framebuffer_counter = 0
|
| 188 |
-
|
| 189 |
-
# Texture registry
|
| 190 |
-
self.textures: Dict[str, np.ndarray] = {}
|
| 191 |
-
self.texture_counter = 0
|
| 192 |
-
|
| 193 |
-
def create_framebuffer(self, width: int, height: int, channels: int = 3,
|
| 194 |
-
name: Optional[str] = None) -> str:
|
| 195 |
-
"""Create a new framebuffer and return its ID."""
|
| 196 |
-
if name is None:
|
| 197 |
-
name = f"framebuffer_{self.framebuffer_counter}"
|
| 198 |
-
self.framebuffer_counter += 1
|
| 199 |
-
|
| 200 |
-
framebuffer = Framebuffer(width, height, channels)
|
| 201 |
-
|
| 202 |
-
# Allocate memory for the framebuffer
|
| 203 |
-
memory_size = framebuffer.get_memory_usage()
|
| 204 |
-
address = self.memory_manager.allocate_block(memory_size)
|
| 205 |
-
|
| 206 |
-
if address is not None:
|
| 207 |
-
framebuffer.vram_address = address
|
| 208 |
-
self.framebuffers[name] = framebuffer
|
| 209 |
-
return name
|
| 210 |
-
else:
|
| 211 |
-
raise MemoryError("Failed to allocate memory for framebuffer")
|
| 212 |
-
|
| 213 |
-
def get_framebuffer(self, name: str) -> Optional[Framebuffer]:
|
| 214 |
-
"""Get a framebuffer by name."""
|
| 215 |
-
return self.framebuffers.get(name)
|
| 216 |
-
|
| 217 |
-
def delete_framebuffer(self, name: str) -> bool:
|
| 218 |
-
"""Delete a framebuffer and free its memory."""
|
| 219 |
-
if name in self.framebuffers:
|
| 220 |
-
framebuffer = self.framebuffers[name]
|
| 221 |
-
if framebuffer.vram_address is not None:
|
| 222 |
-
self.memory_manager.deallocate_block(framebuffer.vram_address)
|
| 223 |
-
del self.framebuffers[name]
|
| 224 |
-
return True
|
| 225 |
-
return False
|
| 226 |
-
|
| 227 |
-
def load_texture(self, texture_data: Union[np.ndarray, bytes], name: Optional[str] = None) -> str:
|
| 228 |
-
"""Load texture data into VRAM and return its ID."""
|
| 229 |
-
if name is None:
|
| 230 |
-
name = f"texture_{self.texture_counter}"
|
| 231 |
-
self.texture_counter += 1
|
| 232 |
-
|
| 233 |
-
size_bytes = 0
|
| 234 |
-
if isinstance(texture_data, np.ndarray):
|
| 235 |
-
size_bytes = texture_data.nbytes
|
| 236 |
-
elif isinstance(texture_data, bytes):
|
| 237 |
-
size_bytes = len(texture_data)
|
| 238 |
-
else:
|
| 239 |
-
raise TypeError("Texture data must be a NumPy array or bytes.")
|
| 240 |
-
|
| 241 |
-
# Allocate memory for the texture
|
| 242 |
-
address = self.memory_manager.allocate_block(size_bytes)
|
| 243 |
-
|
| 244 |
-
if address is not None:
|
| 245 |
-
self.memory_manager.write_data(address, texture_data) # Write actual data
|
| 246 |
-
self.textures[name] = texture_data # Store actual data for reference
|
| 247 |
-
return name
|
| 248 |
-
else:
|
| 249 |
-
raise MemoryError("Failed to allocate memory for texture")
|
| 250 |
-
|
| 251 |
-
def get_texture(self, name: str) -> Optional[np.ndarray]:
|
| 252 |
-
"""Get texture data by name."""
|
| 253 |
-
return self.textures.get(name)
|
| 254 |
-
|
| 255 |
-
def cache_read(self, address: int, size: int) -> Optional[np.ndarray]:
|
| 256 |
-
"""Read data with caching support."""
|
| 257 |
-
cache_key = (address, size)
|
| 258 |
-
|
| 259 |
-
# Check cache first
|
| 260 |
-
if cache_key in self.cache:
|
| 261 |
-
# Move to end (most recently used)
|
| 262 |
-
data = self.cache.pop(cache_key)
|
| 263 |
-
self.cache[cache_key] = data
|
| 264 |
-
return data.copy()
|
| 265 |
-
|
| 266 |
-
# Read from memory
|
| 267 |
-
data = self.memory_manager.read_data(address, size)
|
| 268 |
-
if data is not None:
|
| 269 |
-
# Add to cache
|
| 270 |
-
if len(self.cache) >= self.cache_size:
|
| 271 |
-
# Remove least recently used item
|
| 272 |
-
self.cache.popitem(last=False)
|
| 273 |
-
self.cache[cache_key] = data.copy()
|
| 274 |
-
|
| 275 |
-
return data
|
| 276 |
-
|
| 277 |
-
def transfer_from_ram(self, name: str, data: Union[np.ndarray, bytes],
|
| 278 |
-
delay_ms: float = 0.0) -> Optional[str]:
|
| 279 |
-
"""Transfer a block of data from RAM to VRAM."""
|
| 280 |
-
if isinstance(data, np.ndarray):
|
| 281 |
-
size_bytes = data.nbytes
|
| 282 |
-
data_to_store = data.flatten()
|
| 283 |
-
elif isinstance(data, bytes):
|
| 284 |
-
size_bytes = len(data)
|
| 285 |
-
data_to_store = np.frombuffer(data, dtype=np.uint8)
|
| 286 |
-
else:
|
| 287 |
-
raise TypeError("Data must be a NumPy array or bytes.")
|
| 288 |
-
|
| 289 |
-
# Simulate delay
|
| 290 |
-
if delay_ms > 0:
|
| 291 |
-
time.sleep(delay_ms / 1000.0)
|
| 292 |
-
|
| 293 |
-
# Allocate memory in VRAM
|
| 294 |
-
address = self.memory_manager.allocate_block(size_bytes)
|
| 295 |
-
|
| 296 |
-
if address is not None:
|
| 297 |
-
# Store data in VRAM
|
| 298 |
-
self.memory_manager.write_data(address, data_to_store)
|
| 299 |
-
|
| 300 |
-
# Register the transferred data as a texture/buffer in VRAM
|
| 301 |
-
# For simplicity, we\"ll register it as a texture for now
|
| 302 |
-
texture_id = f"ram_transfer_{self.texture_counter}"
|
| 303 |
-
self.texture_counter += 1
|
| 304 |
-
self.textures[texture_id] = data # Store actual data for reference
|
| 305 |
-
print(f"Transferred {size_bytes} bytes from RAM to VRAM at address {address} as {texture_id}")
|
| 306 |
-
return texture_id
|
| 307 |
-
else:
|
| 308 |
-
print(f"Failed to transfer {size_bytes} bytes from RAM to VRAM: Out of VRAM memory.")
|
| 309 |
-
return None
|
| 310 |
-
|
| 311 |
-
def get_stats(self) -> Dict[str, Any]:
|
| 312 |
-
"""Get comprehensive VRAM statistics."""
|
| 313 |
-
memory_stats = self.memory_manager.get_memory_stats()
|
| 314 |
-
|
| 315 |
-
framebuffer_memory = sum(fb.get_memory_usage() for fb in self.framebuffers.values())
|
| 316 |
-
texture_memory = sum(tex.nbytes for tex in self.textures.values())
|
| 317 |
-
|
| 318 |
-
return {
|
| 319 |
-
**memory_stats,
|
| 320 |
-
"framebuffers_count": len(self.framebuffers),
|
| 321 |
-
"textures_count": len(self.textures),
|
| 322 |
-
"framebuffer_memory_bytes": framebuffer_memory,
|
| 323 |
-
"texture_memory_bytes": texture_memory,
|
| 324 |
-
"cache_entries": len(self.cache),
|
| 325 |
-
"cache_hit_ratio": 0.0 # TODO: Implement cache hit tracking
|
| 326 |
-
}
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
if __name__ == "__main__":
|
| 330 |
-
# Test the VRAM module
|
| 331 |
-
vram = VRAM(memory_size_gb=1) # Use 1GB for testing
|
| 332 |
-
|
| 333 |
-
# Create a framebuffer
|
| 334 |
-
fb_id = vram.create_framebuffer(1920, 1080, 3)
|
| 335 |
-
print(f"Created framebuffer: {fb_id}")
|
| 336 |
-
|
| 337 |
-
# Get the framebuffer and modify it
|
| 338 |
-
fb = vram.get_framebuffer(fb_id)
|
| 339 |
-
if fb:
|
| 340 |
-
fb.clear((255, 0, 0)) # Clear to red
|
| 341 |
-
fb.set_pixel(100, 100, (0, 255, 0)) # Set a green pixel
|
| 342 |
-
print(f"Framebuffer size: {fb.width}x{fb.height}")
|
| 343 |
-
print(f"Pixel at (100, 100): {fb.get_pixel(100, 100)}")
|
| 344 |
-
|
| 345 |
-
# Load a test texture
|
| 346 |
-
test_texture = np.random.randint(0, 256, (256, 256, 3), dtype=np.uint8)
|
| 347 |
-
tex_id = vram.load_texture(test_texture)
|
| 348 |
-
print(f"Loaded texture: {tex_id}")
|
| 349 |
-
|
| 350 |
-
# Test transfer_from_ram
|
| 351 |
-
ram_data = b"\x01\x02\x03\x04\x05\x06\x07\x08"
|
| 352 |
-
transferred_id = vram.transfer_from_ram("test_ram_data", ram_data, delay_ms=10)
|
| 353 |
-
print(f"Transferred RAM data ID: {transferred_id}")
|
| 354 |
-
|
| 355 |
-
# Print statistics
|
| 356 |
-
stats = vram.get_stats()
|
| 357 |
-
print(f"VRAM Stats: {stats}")
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|