597 lines
21 KiB
Python
597 lines
21 KiB
Python
"""
|
||
流体动力学插件性能优化模块
|
||
提供性能优化、内存管理和调试功能
|
||
"""
|
||
|
||
import time
|
||
import psutil
|
||
import threading
|
||
import numpy as np
|
||
from collections import deque, defaultdict
|
||
import gc
|
||
|
||
class FluidPerformanceOptimizer:
|
||
"""
|
||
流体性能优化器
|
||
提供性能监控、优化和调试功能,包括内存管理、多线程处理和自适应优化
|
||
"""
|
||
|
||
def __init__(self, fluid_world):
|
||
"""
|
||
初始化性能优化器
|
||
|
||
Args:
|
||
fluid_world: 流体世界对象的引用
|
||
"""
|
||
self.fluid_world = fluid_world
|
||
self.optimization_enabled = True
|
||
|
||
# 性能监控
|
||
self.frame_times = deque(maxlen=100)
|
||
self.particle_counts = deque(maxlen=100)
|
||
self.update_times = deque(maxlen=100)
|
||
self.memory_usage = deque(maxlen=100)
|
||
|
||
# 优化参数
|
||
self.max_particles = 5000
|
||
self.target_fps = 60
|
||
self.adaptive_resolution = True
|
||
self.quality_level = 1.0 # 1.0为最高质量,0.1为最低质量
|
||
|
||
# 多线程支持
|
||
self.use_multithreading = False
|
||
self.worker_threads = []
|
||
self.thread_lock = threading.Lock()
|
||
self.thread_count = 2
|
||
|
||
# 内存管理
|
||
self.object_pools = defaultdict(list) # 对象池
|
||
self.max_pool_size = 1000 # 每个池的最大大小
|
||
self.memory_limit_mb = 500 # 内存限制(MB)
|
||
|
||
# 自适应优化
|
||
self.adaptive_optimization = True
|
||
self.optimization_history = deque(maxlen=50) # 优化历史
|
||
self.performance_trend = 0 # 性能趋势:正数表示改善,负数表示恶化
|
||
|
||
# 缓存系统
|
||
self.cache_enabled = True
|
||
self.spatial_cache = {} # 空间缓存
|
||
self.cache_ttl = 5.0 # 缓存生存时间(秒)
|
||
self.last_cache_cleanup = time.time()
|
||
|
||
# 调试和分析
|
||
self.debug_mode = False
|
||
self.profiling_enabled = False
|
||
self.profiling_data = defaultdict(list)
|
||
|
||
print("流体性能优化器初始化完成")
|
||
|
||
def update_performance_stats(self, update_time=0):
|
||
"""
|
||
更新性能统计信息
|
||
|
||
Args:
|
||
update_time (float): 更新耗时
|
||
"""
|
||
# 记录帧时间
|
||
current_time = time.time()
|
||
if hasattr(self, 'last_update_time'):
|
||
frame_time = current_time - self.last_update_time
|
||
self.frame_times.append(frame_time)
|
||
|
||
self.last_update_time = current_time
|
||
|
||
# 记录粒子数量
|
||
particle_count = len(self.fluid_world.fluid_particles)
|
||
self.particle_counts.append(particle_count)
|
||
|
||
# 记录更新时间
|
||
self.update_times.append(update_time)
|
||
|
||
# 记录内存使用情况
|
||
memory_mb = psutil.Process().memory_info().rss / 1024 / 1024
|
||
self.memory_usage.append(memory_mb)
|
||
|
||
# 更新优化历史
|
||
self._update_optimization_history()
|
||
|
||
def optimize_for_performance(self):
|
||
"""
|
||
根据当前性能自适应优化
|
||
"""
|
||
if not self.optimization_enabled:
|
||
return
|
||
|
||
# 获取当前性能指标
|
||
avg_frame_time = sum(self.frame_times) / len(self.frame_times) if self.frame_times else 0
|
||
current_fps = 1.0 / avg_frame_time if avg_frame_time > 0 else 0
|
||
current_particles = len(self.fluid_world.fluid_particles)
|
||
current_memory = self.memory_usage[-1] if self.memory_usage else 0
|
||
|
||
# 检查内存使用情况
|
||
if current_memory > self.memory_limit_mb:
|
||
self._trigger_memory_cleanup()
|
||
|
||
# 检查缓存是否需要清理
|
||
if time.time() - self.last_cache_cleanup > self.cache_ttl:
|
||
self._cleanup_cache()
|
||
|
||
# 根据FPS调整质量级别
|
||
if current_fps < self.target_fps * 0.7: # 性能严重不足
|
||
self.quality_level = max(0.1, self.quality_level * 0.9)
|
||
self._reduce_simulation_quality()
|
||
elif current_fps > self.target_fps * 1.1: # 性能良好
|
||
self.quality_level = min(1.0, self.quality_level * 1.05)
|
||
self._increase_simulation_quality()
|
||
|
||
# 根据粒子数量调整
|
||
if current_particles > self.max_particles * 0.9:
|
||
self._reduce_particle_count()
|
||
elif current_particles < self.max_particles * 0.5:
|
||
self._increase_particle_count()
|
||
|
||
# 根据系统资源调整
|
||
cpu_percent = psutil.cpu_percent()
|
||
memory_percent = psutil.virtual_memory().percent
|
||
|
||
if cpu_percent > 85:
|
||
self._reduce_computation_load()
|
||
if memory_percent > 85:
|
||
self._trigger_memory_cleanup()
|
||
|
||
def _reduce_simulation_quality(self):
|
||
"""
|
||
降低模拟质量以提高性能
|
||
"""
|
||
# 降低求解器精度
|
||
if hasattr(self.fluid_world, 'solver'):
|
||
solver = self.fluid_world.solver
|
||
solver.smoothing_radius *= 1.1 # 增大光滑半径减少计算量
|
||
solver.time_step = min(solver.time_step * 1.1, 0.05) # 增大时间步长
|
||
|
||
# 降低可视化质量
|
||
if hasattr(self.fluid_world, 'visualizer'):
|
||
visualizer = self.fluid_world.visualizer
|
||
visualizer.max_visible_particles = int(visualizer.max_visible_particles * 0.9)
|
||
|
||
def _increase_simulation_quality(self):
|
||
"""
|
||
提高模拟质量
|
||
"""
|
||
# 提高求解器精度
|
||
if hasattr(self.fluid_world, 'solver'):
|
||
solver = self.fluid_world.solver
|
||
solver.smoothing_radius = max(solver.smoothing_radius * 0.95, 0.5)
|
||
solver.time_step = max(solver.time_step * 0.95, 0.001)
|
||
|
||
# 提高可视化质量
|
||
if hasattr(self.fluid_world, 'visualizer'):
|
||
visualizer = self.fluid_world.visualizer
|
||
visualizer.max_visible_particles = min(int(visualizer.max_visible_particles * 1.1), 20000)
|
||
|
||
def _reduce_particle_count(self):
|
||
"""
|
||
减少粒子数量
|
||
"""
|
||
current_particles = len(self.fluid_world.fluid_particles)
|
||
target_particles = int(current_particles * 0.9)
|
||
|
||
# 移除多余的粒子
|
||
while len(self.fluid_world.fluid_particles) > target_particles:
|
||
if self.fluid_world.fluid_particles:
|
||
# 回收粒子对象到对象池
|
||
particle = self.fluid_world.fluid_particles.pop()
|
||
self._recycle_object('particle', particle)
|
||
|
||
def _increase_particle_count(self):
|
||
"""
|
||
增加粒子数量
|
||
"""
|
||
current_particles = len(self.fluid_world.fluid_particles)
|
||
target_particles = int(current_particles * 1.1)
|
||
|
||
# 从对象池获取粒子或创建新粒子
|
||
for _ in range(target_particles - current_particles):
|
||
particle = self._get_object('particle')
|
||
if particle is None:
|
||
# 创建新粒子
|
||
particle = {
|
||
'position': Point3(0, 0, 0),
|
||
'velocity': Vec3(0, 0, 0),
|
||
'mass': 1.0,
|
||
'density': 1000.0,
|
||
'pressure': 0.0
|
||
}
|
||
self.fluid_world.fluid_particles.append(particle)
|
||
|
||
def _reduce_computation_load(self):
|
||
"""
|
||
减少计算负载
|
||
"""
|
||
# 降低更新频率
|
||
if hasattr(self.fluid_world, 'visualizer'):
|
||
visualizer = self.fluid_world.visualizer
|
||
visualizer.update_frequency = max(visualizer.update_frequency * 0.9, 15)
|
||
|
||
# 简化物理计算
|
||
if hasattr(self.fluid_world, 'solver'):
|
||
solver = self.fluid_world.solver
|
||
solver.stiffness *= 0.95 # 降低刚度
|
||
solver.viscosity *= 0.95 # 降低粘度计算复杂度
|
||
|
||
def adaptive_substepping(self, dt):
|
||
"""
|
||
自适应子步长
|
||
|
||
Args:
|
||
dt (float): 帧时间增量
|
||
|
||
Returns:
|
||
int: 子步数
|
||
"""
|
||
# 根据当前性能调整子步数
|
||
avg_frame_time = sum(self.frame_times) / len(self.frame_times) if self.frame_times else 0
|
||
current_fps = 1.0 / avg_frame_time if avg_frame_time > 0 else 0
|
||
|
||
# 如果性能良好,使用更多子步以提高精度
|
||
if current_fps > self.target_fps * 0.9:
|
||
return min(10, int(self.fluid_world.time_step / dt) + 1)
|
||
else:
|
||
# 如果性能不佳,减少子步以保持稳定
|
||
return max(1, int(self.fluid_world.time_step / dt * 0.5))
|
||
|
||
def get_performance_report(self):
|
||
"""
|
||
获取详细的性能报告
|
||
|
||
Returns:
|
||
dict: 性能报告
|
||
"""
|
||
avg_frame_time = sum(self.frame_times) / len(self.frame_times) if self.frame_times else 0
|
||
current_fps = 1.0 / avg_frame_time if avg_frame_time > 0 else 0
|
||
avg_update_time = sum(self.update_times) / len(self.update_times) if self.update_times else 0
|
||
|
||
# 获取系统资源使用情况
|
||
cpu_percent = psutil.cpu_percent()
|
||
memory_percent = psutil.virtual_memory().percent
|
||
process_memory = psutil.Process().memory_info().rss / 1024 / 1024 # MB
|
||
|
||
particle_count = len(self.fluid_world.fluid_particles)
|
||
|
||
return {
|
||
'performance': {
|
||
'current_fps': current_fps,
|
||
'avg_frame_time': avg_frame_time,
|
||
'avg_update_time': avg_update_time,
|
||
'quality_level': self.quality_level
|
||
},
|
||
'resources': {
|
||
'cpu_percent': cpu_percent,
|
||
'memory_percent': memory_percent,
|
||
'process_memory_mb': process_memory
|
||
},
|
||
'fluid_objects_count': {
|
||
'particles': particle_count,
|
||
'domains': len(self.fluid_world.fluid_domains),
|
||
'emitters': len(self.fluid_world.fluid_emitters),
|
||
'obstacles': len(self.fluid_world.fluid_obstacles),
|
||
},
|
||
'optimization': {
|
||
'enabled': self.optimization_enabled,
|
||
'adaptive': self.adaptive_optimization,
|
||
'multithreading': self.use_multithreading,
|
||
'thread_count': len(self.worker_threads)
|
||
},
|
||
'memory': {
|
||
'usage_mb': process_memory,
|
||
'limit_mb': self.memory_limit_mb,
|
||
'pool_sizes': {k: len(v) for k, v in self.object_pools.items()}
|
||
},
|
||
'recommended_actions': self._get_recommendations()
|
||
}
|
||
|
||
def _get_recommendations(self):
|
||
"""
|
||
获取性能优化建议
|
||
|
||
Returns:
|
||
list: 优化建议列表
|
||
"""
|
||
recommendations = []
|
||
|
||
avg_frame_time = sum(self.frame_times) / len(self.frame_times) if self.frame_times else 0
|
||
current_fps = 1.0 / avg_frame_time if avg_frame_time > 0 else 0
|
||
particle_count = len(self.fluid_world.fluid_particles)
|
||
process_memory = psutil.Process().memory_info().rss / 1024 / 1024 # MB
|
||
|
||
if current_fps < self.target_fps * 0.7:
|
||
recommendations.append(f"性能严重不足,当前FPS: {current_fps:.1f}")
|
||
|
||
if particle_count > self.max_particles * 0.9:
|
||
recommendations.append(f"粒子数量过多 ({particle_count}),考虑减少粒子数量")
|
||
|
||
if process_memory > self.memory_limit_mb * 0.9:
|
||
recommendations.append(f"内存使用接近限制 ({process_memory:.1f}MB / {self.memory_limit_mb}MB)")
|
||
|
||
if psutil.cpu_percent() > 85:
|
||
recommendations.append("CPU使用率过高,考虑启用多线程")
|
||
|
||
if psutil.virtual_memory().percent > 85:
|
||
recommendations.append("系统内存使用率过高")
|
||
|
||
return recommendations
|
||
|
||
def enable_multithreading(self, enabled=True, thread_count=2):
|
||
"""
|
||
启用多线程处理
|
||
|
||
Args:
|
||
enabled (bool): 是否启用多线程
|
||
thread_count (int): 线程数量
|
||
"""
|
||
self.use_multithreading = enabled
|
||
self.thread_count = thread_count
|
||
|
||
if enabled:
|
||
# 停止现有线程
|
||
self._stop_worker_threads()
|
||
|
||
# 创建工作线程
|
||
for i in range(thread_count):
|
||
thread = threading.Thread(target=self._worker_thread, args=(i,))
|
||
thread.daemon = True
|
||
thread.start()
|
||
self.worker_threads.append(thread)
|
||
|
||
print(f"已启用多线程处理,线程数: {thread_count}")
|
||
else:
|
||
# 停止所有工作线程
|
||
self._stop_worker_threads()
|
||
print("已禁用多线程处理")
|
||
|
||
def _stop_worker_threads(self):
|
||
"""
|
||
停止所有工作线程
|
||
"""
|
||
self.use_multithreading = False
|
||
for thread in self.worker_threads:
|
||
thread.join(timeout=1.0)
|
||
self.worker_threads.clear()
|
||
|
||
def _worker_thread(self, thread_id):
|
||
"""
|
||
工作线程函数
|
||
|
||
Args:
|
||
thread_id (int): 线程ID
|
||
"""
|
||
while self.use_multithreading:
|
||
try:
|
||
# 处理流体粒子更新
|
||
with self.thread_lock:
|
||
# 将粒子分配给不同线程处理
|
||
particles = self.fluid_world.fluid_particles
|
||
if particles:
|
||
particles_per_thread = max(1, len(particles) // len(self.worker_threads))
|
||
start_idx = thread_id * particles_per_thread
|
||
end_idx = min(start_idx + particles_per_thread, len(particles))
|
||
|
||
# 更新分配给此线程的粒子
|
||
for i in range(start_idx, end_idx):
|
||
if i < len(particles):
|
||
particle = particles[i]
|
||
# 在这里执行粒子更新
|
||
# 例如:应用重力、更新位置等
|
||
pass
|
||
|
||
# 短暂休眠以避免过度占用CPU
|
||
time.sleep(0.001)
|
||
except Exception as e:
|
||
if self.debug_mode:
|
||
print(f"工作线程 {thread_id} 出错: {e}")
|
||
time.sleep(0.01) # 出错后短暂休眠
|
||
|
||
def _update_optimization_history(self):
|
||
"""
|
||
更新优化历史记录
|
||
"""
|
||
if self.frame_times:
|
||
current_fps = 1.0 / self.frame_times[-1] if self.frame_times[-1] > 0 else 0
|
||
self.optimization_history.append({
|
||
'timestamp': time.time(),
|
||
'fps': current_fps,
|
||
'particle_count': len(self.fluid_world.fluid_particles),
|
||
'memory_mb': self.memory_usage[-1] if self.memory_usage else 0
|
||
})
|
||
|
||
# 计算性能趋势
|
||
if len(self.optimization_history) > 5:
|
||
recent_fps = [record['fps'] for record in list(self.optimization_history)[-5:]]
|
||
self.performance_trend = np.mean(np.diff(recent_fps)) if len(recent_fps) > 1 else 0
|
||
|
||
def enable_profiling(self, enabled=True):
|
||
"""
|
||
启用性能分析
|
||
|
||
Args:
|
||
enabled (bool): 是否启用性能分析
|
||
"""
|
||
self.profiling_enabled = enabled
|
||
if enabled:
|
||
print("性能分析已启用")
|
||
else:
|
||
print("性能分析已禁用")
|
||
|
||
def profile_function(self, func_name, duration):
|
||
"""
|
||
记录函数执行时间
|
||
|
||
Args:
|
||
func_name (str): 函数名称
|
||
duration (float): 执行时间(秒)
|
||
"""
|
||
if self.profiling_enabled:
|
||
self.profiling_data[func_name].append(duration)
|
||
|
||
def get_profiling_report(self):
|
||
"""
|
||
获取性能分析报告
|
||
|
||
Returns:
|
||
dict: 性能分析报告
|
||
"""
|
||
if not self.profiling_enabled:
|
||
return {}
|
||
|
||
report = {}
|
||
for func_name, durations in self.profiling_data.items():
|
||
if durations:
|
||
report[func_name] = {
|
||
'count': len(durations),
|
||
'total_time': sum(durations),
|
||
'avg_time': np.mean(durations),
|
||
'min_time': min(durations),
|
||
'max_time': max(durations)
|
||
}
|
||
|
||
return report
|
||
|
||
def _trigger_memory_cleanup(self):
|
||
"""
|
||
触发内存清理
|
||
"""
|
||
# 清理对象池中过多的对象
|
||
for obj_type, pool in self.object_pools.items():
|
||
if len(pool) > self.max_pool_size:
|
||
# 保留一部分对象,删除多余的
|
||
del pool[self.max_pool_size:]
|
||
|
||
# 强制进行垃圾回收
|
||
gc.collect()
|
||
|
||
if self.debug_mode:
|
||
print("内存清理完成")
|
||
|
||
def _cleanup_cache(self):
|
||
"""
|
||
清理过期缓存
|
||
"""
|
||
current_time = time.time()
|
||
expired_keys = []
|
||
|
||
for key, (value, timestamp) in self.spatial_cache.items():
|
||
if current_time - timestamp > self.cache_ttl:
|
||
expired_keys.append(key)
|
||
|
||
for key in expired_keys:
|
||
del self.spatial_cache[key]
|
||
|
||
self.last_cache_cleanup = current_time
|
||
|
||
def cache_spatial_query(self, query_key, result):
|
||
"""
|
||
缓存空间查询结果
|
||
|
||
Args:
|
||
query_key: 查询键
|
||
result: 查询结果
|
||
"""
|
||
if self.cache_enabled:
|
||
self.spatial_cache[query_key] = (result, time.time())
|
||
|
||
def get_cached_spatial_query(self, query_key):
|
||
"""
|
||
获取缓存的空间查询结果
|
||
|
||
Args:
|
||
query_key: 查询键
|
||
|
||
Returns:
|
||
查询结果或None
|
||
"""
|
||
if not self.cache_enabled:
|
||
return None
|
||
|
||
if query_key in self.spatial_cache:
|
||
result, timestamp = self.spatial_cache[query_key]
|
||
if time.time() - timestamp <= self.cache_ttl:
|
||
return result
|
||
else:
|
||
# 缓存过期,删除
|
||
del self.spatial_cache[query_key]
|
||
|
||
return None
|
||
|
||
def _get_object(self, obj_type):
|
||
"""
|
||
从对象池获取对象
|
||
|
||
Args:
|
||
obj_type (str): 对象类型
|
||
|
||
Returns:
|
||
对象或None
|
||
"""
|
||
if obj_type in self.object_pools and self.object_pools[obj_type]:
|
||
return self.object_pools[obj_type].pop()
|
||
return None
|
||
|
||
def _recycle_object(self, obj_type, obj):
|
||
"""
|
||
回收对象到对象池
|
||
|
||
Args:
|
||
obj_type (str): 对象类型
|
||
obj: 要回收的对象
|
||
"""
|
||
if len(self.object_pools[obj_type]) < self.max_pool_size:
|
||
# 重置对象状态
|
||
if obj_type == 'particle':
|
||
obj['position'] = Point3(0, 0, 0)
|
||
obj['velocity'] = Vec3(0, 0, 0)
|
||
obj['density'] = 1000.0
|
||
obj['pressure'] = 0.0
|
||
|
||
self.object_pools[obj_type].append(obj)
|
||
|
||
def set_memory_limit(self, limit_mb):
|
||
"""
|
||
设置内存限制
|
||
|
||
Args:
|
||
limit_mb (float): 内存限制(MB)
|
||
"""
|
||
self.memory_limit_mb = limit_mb
|
||
|
||
def set_quality_level(self, level):
|
||
"""
|
||
设置质量级别
|
||
|
||
Args:
|
||
level (float): 质量级别 (0.1-1.0)
|
||
"""
|
||
self.quality_level = max(0.1, min(1.0, level))
|
||
|
||
def cleanup(self):
|
||
"""
|
||
清理性能优化器资源
|
||
"""
|
||
# 停止多线程
|
||
self._stop_worker_threads()
|
||
|
||
# 清理对象池
|
||
self.object_pools.clear()
|
||
|
||
# 清理缓存
|
||
self.spatial_cache.clear()
|
||
|
||
# 清理性能数据
|
||
self.frame_times.clear()
|
||
self.particle_counts.clear()
|
||
self.update_times.clear()
|
||
self.memory_usage.clear()
|
||
self.optimization_history.clear()
|
||
self.profiling_data.clear()
|
||
|
||
print("流体性能优化器资源已清理") |