qwen_agent/utils/system_optimizer.py
朱潮 425f3c5bb4 chore: replace Chinese comments and log messages with English
Convert all Chinese comments, docstrings, logger/print output,
HTTPException detail messages, and API response messages to English
across the entire codebase. Functional zh/ja localized strings
(e.g. prompt templates, timezone display names, date formats) are
preserved as-is.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-30 19:45:35 +08:00

313 lines
11 KiB
Python

#!/usr/bin/env python3
"""
System configuration optimizations for improving concurrent performance.
"""
import os
import asyncio
import multiprocessing
import threading
import time
import resource
import logging
from typing import Dict, Any, Optional
from concurrent.futures import ThreadPoolExecutor
# Configure logging
logger = logging.getLogger('app')
class SystemOptimizer:
"""System optimizer.
Adjusts system parameters to improve concurrent performance.
"""
def __init__(self):
self.original_settings = {}
self.optimized = False
def optimize_system_settings(self):
"""Apply optimized system settings."""
if self.optimized:
return
# Back up original settings
self._backup_original_settings()
# 1. Optimize file descriptor limits.
try:
soft, hard = resource.getrlimit(resource.RLIMIT_NOFILE)
new_limit = min(65536, hard) # Increase to 65536 or the hard limit
resource.setrlimit(resource.RLIMIT_NOFILE, (new_limit, hard))
self.original_settings['RLIMIT_NOFILE'] = (soft, hard)
logger.info(f"File descriptor limit increased from {soft} to {new_limit}")
except (ValueError, OSError) as e:
logger.error(f"Failed to set file descriptor limit: {e}")
# 2. Optimize thread stack size.
try:
soft, hard = resource.getrlimit(resource.RLIMIT_STACK)
new_stack = min(8 * 1024 * 1024, hard) # 8 MB stack size
resource.setrlimit(resource.RLIMIT_STACK, (new_stack, hard))
self.original_settings['RLIMIT_STACK'] = (soft, hard)
logger.info(f"Thread stack size set to {new_stack // (1024*1024)}MB")
except (ValueError, OSError) as e:
logger.error(f"Failed to set thread stack size: {e}")
# 3. Optimize environment variables.
env_vars = {
# Python optimizations
'PYTHONUNBUFFERED': '1', # Disable output buffering
'PYTHONDONTWRITEBYTECODE': '1', # Do not write .pyc files
# Tokenizer optimizations - allow moderate parallelism
'TOKENIZERS_PARALLELISM': 'true', # Set to true to improve concurrency
'TOKENIZERS_FAST': '1', # Enable fast tokenizer
# OpenMP optimizations
'OMP_NUM_THREADS': str(min(8, multiprocessing.cpu_count())), # Limit OpenMP threads
'OMP_WAIT_POLICY': 'PASSIVE', # Passive wait policy
# Memory optimizations
'MALLOC_TRIM_THRESHOLD_': '100000', # Memory trimming threshold
# Network optimizations
'TCP_NODELAY': '1', # Disable Nagle's algorithm
# Hugging Face optimizations
'TRANSFORMERS_CACHE': '/tmp/transformers_cache', # Use tmpfs for faster cache access
'HF_OFFLINE': '0', # Online mode
# CUDA optimizations if GPU is used
'CUDA_LAUNCH_BLOCKING': '0', # Asynchronous CUDA launch
# asyncio optimizations
'UVLOOP_ENABLED': '1', # Enable uvloop when available
}
for key, value in env_vars.items():
if key not in os.environ:
os.environ[key] = value
logger.info(f"Set environment variable: {key}={value}")
self.optimized = True
logger.info("System optimization completed")
def _backup_original_settings(self):
"""Back up original settings."""
try:
self.original_settings['RLIMIT_NOFILE'] = resource.getrlimit(resource.RLIMIT_NOFILE)
self.original_settings['RLIMIT_STACK'] = resource.getrlimit(resource.RLIMIT_STACK)
except:
pass
# Back up important environment variables.
env_keys = [
'TOKENIZERS_PARALLELISM',
'PYTHONUNBUFFERED',
'PYTHONDONTWRITEBYTECODE',
'OMP_NUM_THREADS'
]
for key in env_keys:
if key in os.environ:
self.original_settings[key] = os.environ[key]
def restore_original_settings(self):
"""Restore original settings."""
if not self.original_settings:
return
logger.info("Restoring original system settings...")
# Restore resource limits.
if 'RLIMIT_NOFILE' in self.original_settings:
try:
resource.setrlimit(resource.RLIMIT_NOFILE, self.original_settings['RLIMIT_NOFILE'])
logger.info(f"Restored file descriptor limit")
except:
pass
if 'RLIMIT_STACK' in self.original_settings:
try:
resource.setrlimit(resource.RLIMIT_STACK, self.original_settings['RLIMIT_STACK'])
logger.info(f"Restored thread stack size")
except:
pass
# Restore environment variables.
for key, value in self.original_settings.items():
if key.startswith('TOKENIZERS_') or key in ['PYTHONUNBUFFERED', 'PYTHONDONTWRITEBYTECODE']:
if key in os.environ:
del os.environ[key]
logger.info(f"Removed environment variable: {key}")
elif key in ['OMP_NUM_THREADS'] and value is not None:
os.environ[key] = value
logger.info(f"Restored environment variable: {key}={value}")
self.optimized = False
logger.info("System settings restored")
class AsyncioOptimizer:
"""asyncio optimizer."""
@staticmethod
def setup_event_loop_policy():
"""Set an optimized event loop policy."""
try:
# Try to use uvloop if available.
import uvloop
asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())
logger.info("Using uvloop event loop policy")
except ImportError:
logger.info("Using default event loop policy")
# Set the thread pool size recommendation.
cpu_count = multiprocessing.cpu_count()
thread_pool_size = min(32, cpu_count * 4) # 4 threads per CPU core, up to 32
# Note: the default thread pool executor cannot be set here because
# no event loop is running yet. This should be configured during app startup.
logger.info(f"Recommended thread pool size: {thread_pool_size}")
@staticmethod
def optimize_gunicorn_settings() -> Dict[str, Any]:
"""Get optimized Gunicorn settings."""
cpu_count = multiprocessing.cpu_count()
return {
# Worker configuration
'workers': min(8, cpu_count + 1), # Number of worker processes
'worker_class': 'uvicorn.workers.UvicornWorker', # Use Uvicorn worker
'worker_connections': 2000, # Connections per worker
'max_requests': 5000, # Restart worker after max requests
'max_requests_jitter': 500, # Random jitter
'preload_app': True, # Preload application
# Timeout settings
'timeout': 120, # Worker timeout
'keepalive': 5, # Keep-Alive timeout
'graceful_timeout': 30, # Graceful shutdown timeout
# Performance optimizations
'worker_tmp_dir': '/dev/shm', # Use memory-backed filesystem
# Logging settings
'accesslog': '-', # Standard output
'errorlog': '-', # Standard error output
'loglevel': 'info',
}
def setup_system_optimizations():
"""Set up system optimizations."""
# 1. System-level optimizations
system_optimizer = SystemOptimizer()
system_optimizer.optimize_system_settings()
# 2. asyncio optimizations
asyncio_optimizer = AsyncioOptimizer()
asyncio_optimizer.setup_event_loop_policy()
return system_optimizer
def create_performance_monitor() -> Dict[str, Any]:
"""Create performance monitoring configuration."""
return {
'monitor_interval': 60, # Monitoring interval in seconds
'metrics': {
'memory_usage': True,
'cpu_usage': True,
'disk_io': True,
'network_io': True,
'active_connections': True,
'request_latency': True,
'cache_hit_rate': True,
'error_rate': True,
},
'alerts': {
'memory_threshold': 0.9, # Alert at 90% memory usage
'cpu_threshold': 0.8, # Alert at 80% CPU usage
'disk_threshold': 0.9, # Alert at 90% disk usage
'error_threshold': 0.05, # Alert at 5% error rate
'latency_threshold': 5.0, # Alert at 5 seconds latency
}
}
def get_optimized_worker_config() -> Dict[str, Any]:
"""Get optimized worker configuration."""
cpu_count = multiprocessing.cpu_count()
memory_gb = os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') / (1024.0 ** 3)
# Configuration constrained by available system resources.
max_workers = min(
16, # Maximum worker count
max(2, cpu_count), # At least 2 workers, at most the CPU core count
int(memory_gb / 2) # Memory-based worker limit, 2 GB per worker
)
return {
'max_workers': max_workers,
'worker_connections': 1000, # Connections per worker
'connection_pool_size': 100, # Connection pool size
'buffer_size': 8192, # Buffer size
'timeout': 120, # Timeout in seconds
'keepalive_timeout': 30, # Keep-Alive timeout
}
# Predefined optimization profiles
OPTIMIZATION_CONFIGS = {
'low_memory': {
'max_workers': 2,
'worker_connections': 500,
'buffer_size': 4096,
'cache_size': 500,
},
'balanced': {
'max_workers': 4,
'worker_connections': 1000,
'buffer_size': 8192,
'cache_size': 1000,
},
'high_performance': {
'max_workers': 8,
'worker_connections': 2000,
'buffer_size': 16384,
'cache_size': 2000,
}
}
def apply_optimization_profile(profile_name: str) -> Dict[str, Any]:
"""Apply an optimization profile."""
if profile_name not in OPTIMIZATION_CONFIGS:
raise ValueError(f"Unknown optimization profile: {profile_name}")
config = OPTIMIZATION_CONFIGS[profile_name].copy()
# Add system-specific configuration.
config.update({
'profile_name': profile_name,
'applied_at': time.time(),
'cpu_count': multiprocessing.cpu_count(),
'memory_gb': os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') / (1024.0 ** 3)
})
return config
# Global system optimizer instance
_global_system_optimizer: Optional[SystemOptimizer] = None
def get_global_system_optimizer() -> SystemOptimizer:
"""Get the global system optimizer."""
global _global_system_optimizer
if _global_system_optimizer is None:
_global_system_optimizer = SystemOptimizer()
return _global_system_optimizer