EG/plugins/user/fluid_dynamics/utils/performance_utils.py
2025-10-30 11:46:41 +08:00

597 lines
21 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
流体动力学插件性能优化模块
提供性能优化、内存管理和调试功能
"""
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("流体性能优化器资源已清理")