本节导读:深入理解vLLM的内存管理机制,包括PagedAttention实现、内存分配策略和性能优化技巧,掌握高效LLM推理的内存管理方法。
vLLM的内存管理采用分层设计,确保高效的内存利用和性能。整个架构从上到下分为:应用层、管理层、设备层,每一层都有明确的职责和优化策略。
import torch import numpy as np import time import threading from typing import Dict, List, Optional class SimpleMemoryManager: """简单的内存管理器实现""" def __init__(self, total_memory_mb: int = 8000): self.total_memory = total_memory_mb * 1024 * 1024 # 转换为字节 self.allocated_memory = 0 self.memory_blocks = [] # 内存块列表 self.lock = threading.Lock() # 内存池优化 self.memory_pool = [] self.pool_lock = threading.Lock() print(f"初始化内存管理器,总内存: {total_memory_mb}MB") def allocate(self, size_bytes: int, align: int = 1024) -> Optional[int]: """分配内存""" with self.lock: # 计算对齐后的大小 aligned_size = ((size_bytes + align - 1) // align) * align if self.allocated_memory + aligned_size > self.total_memory: return None # 尝试从内存池分配 with self.pool_lock: for i, (start, end, used) in enumerate(self.memory_pool): if not used and end - start >= aligned_size: self.memory_pool[i] = (start, start + aligned_size, True) self.allocated_memory += aligned_size print(f"从内存池分配 {aligned_size/1024/1024:.1f}MB") return start # 没有合适的内存池块,创建新块 start_addr = self.allocated_memory self.allocated_memory += aligned_size self.memory_blocks.append((start_addr, start_addr + aligned_size)) return start_addr def deallocate(self, addr: int, size_bytes: int): """释放内存""" with self.lock: # 查找并标记为未使用 with self.pool_lock: for i, (start, end, used) in enumerate(self.memory_pool): if start == addr and used: self.memory_pool[i] = (start, end, False) break print(f"释放内存 {size_bytes/1024/1024:.1f}MB") def get_memory_info(self) -> Dict: """获取内存信息""" return { 'total_memory': self.total_memory, 'allocated_memory': self.allocated_memory, 'free_memory': self.total_memory - self.allocated_memory, 'usage_percent': (self.allocated_memory / self.total_memory) * 100 }
class PagedAttentionSimulator: """PagedAttention模拟器""" def __init__(self, num_pages: int = 256, page_size: int = 16): self.num_pages = num_pages self.page_size = page_size # 每页大小(MB) self.pages = [None] * num_pages # 页面数据 self.page_refs = [0] * num_pages # 页面引用计数 self.page_access_time = [0] * num_pages # 最后访问时间 self.lock = threading.Lock() print(f"初始化PagedAttention: {num_pages}页,每页{page_size}MB") def allocate_page(self, page_id: int) -> bool: """分配页面""" with self.lock: if page_id < 0 or page_id >= self.num_pages: return False if self.pages[page_id] is None: # 初始化页面数据 self.pages[page_id] = np.zeros(self.page_size * 1024 * 1024, dtype=np.float16) self.page_refs[page_id] = 0 self.page_access_time[page_id] = time.time() print(f"分配页面 {page_id}") return True return False def access_page(self, page_id: int) -> Optional[np.ndarray]: """访问页面""" with self.lock: if 0 <= page_id < self.num_pages and self.pages[page_id] is not None: self.page_refs[page_id] += 1 self.page_access_time[page_id] = time.time() return self.pages[page_id].copy() return None def free_page(self, page_id: int) -> bool: """释放页面""" with self.lock: if 0 <= page_id < self.num_pages and self.pages[page_id] is not None: self.pages[page_id] = None self.page_refs[page_id] = 0 print(f"释放页面 {page_id}") return True return False
import torch import numpy as np import time import threading from typing import Dict, List, Optional class VLLMMemoryManager: """vLLM内存管理器实现""" def __init__(self, max_memory_gb: float = 16.0): self.max_memory = int(max_memory_gb * 1024**3) # 转换为字节 self.allocated_memory = 0 self.memory_blocks = [] self.kv_cache = {} self.lock = threading.Lock() # 统计信息 self.stats = { 'total_allocations': 0, 'total_deallocations': 0, 'cache_hits': 0, 'cache_misses': 0, 'page_faults': 0, 'page_evictions': 0 } # 内存池优化 self.memory_pool = [] self.pool_size = int(0.1 * self.max_memory) # 10%作为内存池 print(f"初始化vLLM内存管理器,最大内存: {max_memory_gb}GB") def allocate(self, size: int, align: int = 1024) -> Optional[int]: """分配内存""" with self.lock: # 对齐大小 aligned_size = ((size + align - 1) // align) * aligned_size if self.allocated_memory + aligned_size > self.max_memory: return None # 尝试从内存池分配 for i, (start, end, used) in enumerate(self.memory_pool): if not used and end - start >= aligned_size: self.memory_pool[i] = (start, start + aligned_size, True) self.allocated_memory += aligned_size self.stats['total_allocations'] += 1 print(f"从内存池分配 {aligned_size/1024**2:.1f}MB") return start # 分配新块 start_addr = self.allocated_memory self.allocated_memory += aligned_size self.memory_blocks.append((start_addr, start_addr + aligned_size)) self.stats['total_allocations'] += 1 return start_addr def deallocate(self, addr: int, size: int): """释放内存""" with self.lock: # 标记内存池为未使用 for i, (start, end, used) in enumerate(self.memory_pool): if start == addr and used: self.memory_pool[i] = (start, end, False) break self.allocated_memory -= size self.stats['total_deallocations'] += 1 def add_to_kv_cache(self, key: str, value: np.ndarray) -> bool: """添加到KV缓存""" with self.lock: # 简单的LRU缓存 if len(self.kv_cache) >= 100: # 缓存大小限制 # 删除最老的键 oldest_key = next(iter(self.kv_cache)) del self.kv_cache[oldest_key] self.kv_cache[key] = value return True def get_from_kv_cache(self, key: str) -> Optional[np.ndarray]: """从KV缓存获取""" with self.lock: if key in self.kv_cache: self.stats['cache_hits'] += 1 return self.kv_cache[key].copy() else: self.stats['cache_misses'] += 1 return None