mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
- 将所有小写的命名空间导入更正为首字母大写格式 - 统一 GFramework 框架的命名空间引用规范 - 修复 core、ecs、godot 等模块的命名空间导入错误 - 标准化文档示例代码中的 using 语句格式 - 确保所有文档中的命名空间引用保持一致性 - 更新 global using 语句以匹配正确的命名空间格式
35 KiB
35 KiB
性能优化指南
全面的性能优化策略和最佳实践,帮助你构建高性能的游戏应用。
📋 目录
概述
性能优化是游戏开发中的关键环节。良好的性能不仅能提供流畅的用户体验,还能降低设备功耗,延长电池寿命。本指南将介绍 GFramework 中的性能优化策略和最佳实践。
性能优化的重要性
- 用户体验 - 流畅的帧率和快速的响应时间
- 设备兼容性 - 在低端设备上也能良好运行
- 资源效率 - 降低内存占用和 CPU 使用率
- 电池寿命 - 减少不必要的计算和内存分配
核心概念
1. 性能瓶颈
性能瓶颈是指限制系统整体性能的关键因素:
- CPU 瓶颈 - 过多的计算、复杂的逻辑
- 内存瓶颈 - 频繁的 GC、内存泄漏
- GPU 瓶颈 - 过多的绘制调用、复杂的着色器
- I/O 瓶颈 - 频繁的文件读写、网络请求
2. 性能指标
关键的性能指标:
- 帧率 (FPS) - 每秒渲染的帧数,目标 60 FPS
- 帧时间 - 每帧的处理时间,目标 <16.67ms
- 内存占用 - 应用程序使用的内存量
- GC 频率 - 垃圾回收的频率和耗时
- 加载时间 - 场景和资源的加载时间
3. 优化策略
性能优化的基本策略:
- 测量优先 - 先测量,再优化
- 找到瓶颈 - 使用性能分析工具定位问题
- 渐进优化 - 逐步优化,避免过早优化
- 权衡取舍 - 在性能和可维护性之间找到平衡
对象池优化
对象池是减少 GC 压力的有效手段,通过复用对象避免频繁的内存分配和释放。
1. 使用对象池系统
// ✅ 好的做法:使用对象池
public class BulletPoolSystem : AbstractObjectPoolSystem<string, Bullet>
{
protected override Bullet Create(string key)
{
// 创建新的子弹对象
var bullet = new Bullet();
bullet.Initialize(key);
return bullet;
}
}
public class Bullet : IPoolableObject
{
public string Type { get; private set; }
public Vector2 Position { get; set; }
public Vector2 Velocity { get; set; }
public bool IsActive { get; private set; }
public void Initialize(string type)
{
Type = type;
}
public void OnAcquire()
{
// 从池中获取时重置状态
IsActive = true;
Position = Vector2.Zero;
Velocity = Vector2.Zero;
}
public void OnRelease()
{
// 归还到池中时清理状态
IsActive = false;
}
public void OnPoolDestroy()
{
// 对象被销毁时的清理
}
}
// 使用对象池
public class CombatSystem : AbstractSystem
{
private BulletPoolSystem _bulletPool;
protected override void OnInit()
{
_bulletPool = GetSystem<BulletPoolSystem>();
// 预热对象池
_bulletPool.Prewarm("normal_bullet", 50);
_bulletPool.Prewarm("fire_bullet", 20);
}
private void FireBullet(string bulletType, Vector2 position, Vector2 direction)
{
// 从池中获取子弹
var bullet = _bulletPool.Acquire(bulletType);
bullet.Position = position;
bullet.Velocity = direction * 10f;
// 使用完毕后归还
// bullet 会在生命周期结束时自动归还
}
}
// ❌ 避免:频繁创建和销毁对象
public class CombatSystem : AbstractSystem
{
private void FireBullet(string bulletType, Vector2 position, Vector2 direction)
{
// 每次都创建新对象,产生大量 GC
var bullet = new Bullet();
bullet.Type = bulletType;
bullet.Position = position;
bullet.Velocity = direction * 10f;
// 使用完毕后直接丢弃,等待 GC 回收
}
}
2. StringBuilder 池
GFramework 提供了 StringBuilderPool 用于高效的字符串构建:
// ✅ 好的做法:使用 StringBuilderPool
public string FormatPlayerInfo(Player player)
{
using var sb = StringBuilderPool.GetScoped();
sb.Value.Append("Player: ");
sb.Value.Append(player.Name);
sb.Value.Append(", Level: ");
sb.Value.Append(player.Level);
sb.Value.Append(", HP: ");
sb.Value.Append(player.Health);
sb.Value.Append("/");
sb.Value.Append(player.MaxHealth);
return sb.Value.ToString();
}
// 或者手动管理
public string FormatPlayerInfo(Player player)
{
var sb = StringBuilderPool.Rent();
try
{
sb.Append("Player: ").Append(player.Name);
sb.Append(", Level: ").Append(player.Level);
sb.Append(", HP: ").Append(player.Health).Append("/").Append(player.MaxHealth);
return sb.ToString();
}
finally
{
StringBuilderPool.Return(sb);
}
}
// ❌ 避免:频繁的字符串拼接
public string FormatPlayerInfo(Player player)
{
// 每次拼接都会创建新的字符串对象
return "Player: " + player.Name +
", Level: " + player.Level +
", HP: " + player.Health + "/" + player.MaxHealth;
}
3. ArrayPool 优化
使用 ArrayPool 避免频繁的数组分配:
// ✅ 好的做法:使用 ArrayPool
public void ProcessEntities(List<Entity> entities)
{
using var scopedArray = ArrayPool<Entity>.Shared.GetScoped(entities.Count);
var array = scopedArray.Array;
// 复制到数组进行处理
entities.CopyTo(array, 0);
// 处理数组
for (int i = 0; i < entities.Count; i++)
{
ProcessEntity(array[i]);
}
// 自动归还到池中
}
// ❌ 避免:频繁创建数组
public void ProcessEntities(List<Entity> entities)
{
// 每次都创建新数组
var array = entities.ToArray();
foreach (var entity in array)
{
ProcessEntity(entity);
}
// 数组等待 GC 回收
}
4. 对象池统计
监控对象池的使用情况:
public class PoolMonitorSystem : AbstractSystem
{
private BulletPoolSystem _bulletPool;
protected override void OnInit()
{
_bulletPool = GetSystem<BulletPoolSystem>();
}
public void LogPoolStatistics(string poolKey)
{
var stats = _bulletPool.GetStatistics(poolKey);
Logger.Info($"Pool Statistics for '{poolKey}':");
Logger.Info($" Available: {stats.AvailableCount}");
Logger.Info($" Active: {stats.ActiveCount}");
Logger.Info($" Total Created: {stats.TotalCreated}");
Logger.Info($" Total Acquired: {stats.TotalAcquired}");
Logger.Info($" Total Released: {stats.TotalReleased}");
Logger.Info($" Total Destroyed: {stats.TotalDestroyed}");
// 检查是否需要调整池大小
if (stats.TotalDestroyed > stats.TotalCreated * 0.5)
{
Logger.Warning($"Pool '{poolKey}' has high destruction rate, consider increasing max capacity");
}
}
}
事件系统优化
事件系统是游戏架构的核心,优化事件处理可以显著提升性能。
1. 避免事件订阅泄漏
using GFramework.Core.Abstractions.Controller;
using GFramework.SourceGenerators.Abstractions.Rule;
// ✅ 好的做法:正确管理事件订阅
[ContextAware]
public partial class PlayerController : Node, IController
{
private IUnRegisterList _unRegisterList = new UnRegisterList();
private PlayerModel _playerModel;
public void Initialize()
{
_playerModel = this.GetModel<PlayerModel>();
// 使用 UnRegisterList 管理订阅
this.RegisterEvent<PlayerDamagedEvent>(OnPlayerDamaged)
.AddTo(_unRegisterList);
_playerModel.Health.Register(OnHealthChanged)
.AddTo(_unRegisterList);
}
public void Cleanup()
{
// 统一取消所有订阅
_unRegisterList.UnRegisterAll();
}
private void OnPlayerDamaged(PlayerDamagedEvent e) { }
private void OnHealthChanged(int health) { }
}
// ❌ 避免:忘记取消订阅
[ContextAware]
public partial class PlayerController : Node, IController
{
public void Initialize()
{
// 订阅事件但从不取消订阅
this.RegisterEvent<PlayerDamagedEvent>(OnPlayerDamaged);
var playerModel = this.GetModel<PlayerModel>();
playerModel.Health.Register(OnHealthChanged);
// 当对象被销毁时,这些订阅仍然存在,导致内存泄漏
}
}
2. 使用结构体事件
使用值类型事件避免堆分配:
// ✅ 好的做法:使用结构体事件
public struct PlayerMovedEvent
{
public string PlayerId { get; init; }
public Vector2 OldPosition { get; init; }
public Vector2 NewPosition { get; init; }
public float DeltaTime { get; init; }
}
public class MovementSystem : AbstractSystem
{
private void NotifyPlayerMoved(string playerId, Vector2 oldPos, Vector2 newPos, float deltaTime)
{
// 结构体在栈上分配,无 GC 压力
SendEvent(new PlayerMovedEvent
{
PlayerId = playerId,
OldPosition = oldPos,
NewPosition = newPos,
DeltaTime = deltaTime
});
}
}
// ❌ 避免:使用类事件
public class PlayerMovedEvent
{
public string PlayerId { get; set; }
public Vector2 OldPosition { get; set; }
public Vector2 NewPosition { get; set; }
public float DeltaTime { get; set; }
}
// 每次发送事件都会在堆上分配对象
3. 批量事件处理
对于高频事件,考虑批量处理:
// ✅ 好的做法:批量处理事件
public class DamageSystem : AbstractSystem
{
private readonly List<DamageInfo> _pendingDamages = new();
private float _batchInterval = 0.1f;
private float _timeSinceLastBatch = 0f;
public void Update(float deltaTime)
{
_timeSinceLastBatch += deltaTime;
if (_timeSinceLastBatch >= _batchInterval)
{
ProcessDamageBatch();
_timeSinceLastBatch = 0f;
}
}
public void QueueDamage(string entityId, int damage, DamageType type)
{
_pendingDamages.Add(new DamageInfo
{
EntityId = entityId,
Damage = damage,
Type = type
});
}
private void ProcessDamageBatch()
{
if (_pendingDamages.Count == 0)
return;
// 批量处理所有伤害
foreach (var damageInfo in _pendingDamages)
{
ApplyDamage(damageInfo);
}
// 发送单个批量事件
SendEvent(new DamageBatchProcessedEvent
{
DamageCount = _pendingDamages.Count,
TotalDamage = _pendingDamages.Sum(d => d.Damage)
});
_pendingDamages.Clear();
}
}
// ❌ 避免:每次都立即处理
public class DamageSystem : AbstractSystem
{
public void ApplyDamage(string entityId, int damage, DamageType type)
{
// 每次伤害都立即处理并发送事件
ProcessDamage(entityId, damage, type);
SendEvent(new DamageAppliedEvent { EntityId = entityId, Damage = damage });
}
}
4. 事件优先级优化
合理使用事件优先级避免不必要的处理:
// ✅ 好的做法:使用优先级控制事件传播
public class InputSystem : AbstractSystem
{
protected override void OnInit()
{
// UI 输入处理器优先级最高
this.RegisterEvent<InputEvent>(OnUIInput, priority: 100);
}
private void OnUIInput(InputEvent e)
{
if (IsUIHandlingInput())
{
// 停止事件传播,避免游戏逻辑处理
e.StopPropagation();
}
}
}
public class GameplayInputSystem : AbstractSystem
{
protected override void OnInit()
{
// 游戏逻辑输入处理器优先级较低
this.RegisterEvent<InputEvent>(OnGameplayInput, priority: 50);
}
private void OnGameplayInput(InputEvent e)
{
// 如果 UI 已经处理了输入,这里不会执行
if (!e.IsPropagationStopped)
{
ProcessGameplayInput(e);
}
}
}
协程优化
协程是处理异步逻辑的强大工具,但不当使用会影响性能。
1. 避免过度嵌套
// ✅ 好的做法:扁平化协程结构
public IEnumerator<IYieldInstruction> LoadGameSequence()
{
// 显示加载界面
yield return ShowLoadingScreen();
// 加载配置
yield return LoadConfiguration();
// 加载资源
yield return LoadResources();
// 初始化系统
yield return InitializeSystems();
// 隐藏加载界面
yield return HideLoadingScreen();
}
private IEnumerator<IYieldInstruction> LoadConfiguration()
{
var config = await ConfigManager.LoadAsync();
ApplyConfiguration(config);
yield break;
}
// ❌ 避免:深度嵌套
public IEnumerator<IYieldInstruction> LoadGameSequence()
{
yield return ShowLoadingScreen().Then(() =>
{
return LoadConfiguration().Then(() =>
{
return LoadResources().Then(() =>
{
return InitializeSystems().Then(() =>
{
return HideLoadingScreen();
});
});
});
});
}
2. 协程池化
对于频繁启动的协程,考虑复用:
// ✅ 好的做法:复用协程逻辑
public class EffectSystem : AbstractSystem
{
private readonly Dictionary<string, IEnumerator<IYieldInstruction>> _effectCoroutines = new();
public CoroutineHandle PlayEffect(string effectId, Vector2 position)
{
// 复用协程逻辑
return this.StartCoroutine(EffectCoroutine(effectId, position));
}
private IEnumerator<IYieldInstruction> EffectCoroutine(string effectId, Vector2 position)
{
var effect = CreateEffect(effectId, position);
// 播放效果
yield return new Delay(effect.Duration);
// 清理效果
DestroyEffect(effect);
}
}
3. 合理使用 WaitForFrames
// ✅ 好的做法:使用 WaitForFrames 分帧处理
public IEnumerator<IYieldInstruction> ProcessLargeDataSet(List<Data> dataSet)
{
const int batchSize = 100;
for (int i = 0; i < dataSet.Count; i += batchSize)
{
int end = Math.Min(i + batchSize, dataSet.Count);
// 处理一批数据
for (int j = i; j < end; j++)
{
ProcessData(dataSet[j]);
}
// 等待一帧,避免卡顿
yield return new WaitOneFrame();
}
}
// ❌ 避免:一次性处理大量数据
public void ProcessLargeDataSet(List<Data> dataSet)
{
// 一次性处理所有数据,可能导致帧率下降
foreach (var data in dataSet)
{
ProcessData(data);
}
}
4. 协程取消优化
// ✅ 好的做法:及时取消不需要的协程
public class AnimationController : AbstractSystem
{
private CoroutineHandle _currentAnimation;
public void PlayAnimation(string animationName)
{
// 取消当前动画
if (_currentAnimation.IsValid)
{
this.StopCoroutine(_currentAnimation);
}
// 播放新动画
_currentAnimation = this.StartCoroutine(AnimationCoroutine(animationName));
}
private IEnumerator<IYieldInstruction> AnimationCoroutine(string animationName)
{
var animation = GetAnimation(animationName);
while (!animation.IsComplete)
{
animation.Update(Time.DeltaTime);
yield return new WaitOneFrame();
}
}
}
资源管理优化
高效的资源管理可以显著减少加载时间和内存占用。
1. 资源预加载
// ✅ 好的做法:预加载常用资源
public class ResourcePreloader : AbstractSystem
{
private IResourceManager _resourceManager;
protected override void OnInit()
{
_resourceManager = GetUtility<IResourceManager>();
}
public async Task PreloadCommonResources()
{
// 预加载 UI 资源
await _resourceManager.PreloadAsync<Texture>("ui/button_normal");
await _resourceManager.PreloadAsync<Texture>("ui/button_pressed");
await _resourceManager.PreloadAsync<Texture>("ui/button_hover");
// 预加载音效
await _resourceManager.PreloadAsync<AudioClip>("sfx/button_click");
await _resourceManager.PreloadAsync<AudioClip>("sfx/button_hover");
// 预加载特效
await _resourceManager.PreloadAsync<ParticleSystem>("effects/hit_effect");
await _resourceManager.PreloadAsync<ParticleSystem>("effects/explosion");
}
}
2. 异步加载
// ✅ 好的做法:使用异步加载避免阻塞
public class SceneLoader : AbstractSystem
{
private IResourceManager _resourceManager;
public async Task LoadSceneAsync(string sceneName)
{
// 显示加载进度
var progress = 0f;
UpdateLoadingProgress(progress);
// 异步加载场景资源
var sceneData = await _resourceManager.LoadAsync<SceneData>($"scenes/{sceneName}");
progress += 0.3f;
UpdateLoadingProgress(progress);
// 异步加载场景依赖的资源
await LoadSceneDependencies(sceneData);
progress += 0.5f;
UpdateLoadingProgress(progress);
// 初始化场景
await InitializeScene(sceneData);
progress = 1f;
UpdateLoadingProgress(progress);
}
private async Task LoadSceneDependencies(SceneData sceneData)
{
var tasks = new List<Task>();
foreach (var dependency in sceneData.Dependencies)
{
tasks.Add(_resourceManager.LoadAsync<object>(dependency));
}
await Task.WhenAll(tasks);
}
}
// ❌ 避免:同步加载阻塞主线程
public class SceneLoader : AbstractSystem
{
public void LoadScene(string sceneName)
{
// 同步加载会阻塞主线程,导致卡顿
var sceneData = _resourceManager.Load<SceneData>($"scenes/{sceneName}");
LoadSceneDependencies(sceneData);
InitializeScene(sceneData);
}
}
3. 资源引用计数
// ✅ 好的做法:使用资源句柄管理引用
public class EntityRenderer : AbstractSystem
{
private readonly Dictionary<string, IResourceHandle<Texture>> _textureHandles = new();
public void LoadTexture(string entityId, string texturePath)
{
// 获取资源句柄
var handle = _resourceManager.GetHandle<Texture>(texturePath);
if (handle != null)
{
_textureHandles[entityId] = handle;
}
}
public void UnloadTexture(string entityId)
{
if (_textureHandles.TryGetValue(entityId, out var handle))
{
// 释放句柄,自动管理引用计数
handle.Dispose();
_textureHandles.Remove(entityId);
}
}
protected override void OnDestroy()
{
// 清理所有句柄
foreach (var handle in _textureHandles.Values)
{
handle.Dispose();
}
_textureHandles.Clear();
}
}
4. 资源缓存策略
// ✅ 好的做法:使用合适的释放策略
public class GameResourceManager : AbstractSystem
{
private IResourceManager _resourceManager;
protected override void OnInit()
{
_resourceManager = GetUtility<IResourceManager>();
// 设置自动释放策略
_resourceManager.SetReleaseStrategy(new AutoReleaseStrategy());
}
public void OnSceneChanged()
{
// 场景切换时卸载未使用的资源
UnloadUnusedResources();
}
private void UnloadUnusedResources()
{
var loadedPaths = _resourceManager.GetLoadedResourcePaths().ToList();
foreach (var path in loadedPaths)
{
// 检查资源是否仍在使用
if (!IsResourceInUse(path))
{
_resourceManager.Unload(path);
}
}
}
private bool IsResourceInUse(string path)
{
// 检查资源引用计数
return false; // 实现具体逻辑
}
}
ECS 性能优化
Entity Component System (ECS) 是高性能游戏架构的关键。
1. 组件设计优化
// ✅ 好的做法:使用值类型组件
public struct Position
{
public float X;
public float Y;
public float Z;
}
public struct Velocity
{
public float X;
public float Y;
public float Z;
}
public struct Health
{
public int Current;
public int Max;
}
// ❌ 避免:使用引用类型组件
public class Position
{
public float X { get; set; }
public float Y { get; set; }
public float Z { get; set; }
}
2. 查询优化
// ✅ 好的做法:使用高效的查询
public class MovementSystem : ArchSystemAdapter
{
private QueryDescription _movementQuery;
public override void Initialize()
{
// 预先构建查询
_movementQuery = new QueryDescription()
.WithAll<Position, Velocity>()
.WithNone<Frozen>();
}
public override void Update(float deltaTime)
{
// 使用预构建的查询
World.Query(in _movementQuery, (ref Position pos, ref Velocity vel) =>
{
pos.X += vel.X * deltaTime;
pos.Y += vel.Y * deltaTime;
pos.Z += vel.Z * deltaTime;
});
}
}
// ❌ 避免:每帧构建查询
public class MovementSystem : ArchSystemAdapter
{
public override void Update(float deltaTime)
{
// 每帧都构建新查询,性能差
var query = new QueryDescription()
.WithAll<Position, Velocity>()
.WithNone<Frozen>();
World.Query(in query, (ref Position pos, ref Velocity vel) =>
{
pos.X += vel.X * deltaTime;
pos.Y += vel.Y * deltaTime;
pos.Z += vel.Z * deltaTime;
});
}
}
3. 批量处理
// ✅ 好的做法:批量处理实体
public class DamageSystem : ArchSystemAdapter
{
private readonly List<(EntityReference entity, int damage)> _pendingDamages = new();
public void QueueDamage(EntityReference entity, int damage)
{
_pendingDamages.Add((entity, damage));
}
public override void Update(float deltaTime)
{
if (_pendingDamages.Count == 0)
return;
// 批量应用伤害
foreach (var (entity, damage) in _pendingDamages)
{
if (World.IsAlive(entity))
{
ref var health = ref World.Get<Health>(entity);
health.Current = Math.Max(0, health.Current - damage);
if (health.Current == 0)
{
World.Destroy(entity);
}
}
}
_pendingDamages.Clear();
}
}
4. 避免装箱
// ✅ 好的做法:避免装箱操作
public struct EntityId : IEquatable<EntityId>
{
public int Value;
public bool Equals(EntityId other) => Value == other.Value;
public override bool Equals(object obj) => obj is EntityId other && Equals(other);
public override int GetHashCode() => Value;
}
// 使用泛型避免装箱
public class EntityCache<T> where T : struct
{
private readonly Dictionary<EntityId, T> _cache = new();
public void Add(EntityId id, T data)
{
_cache[id] = data; // 无装箱
}
public bool TryGet(EntityId id, out T data)
{
return _cache.TryGetValue(id, out data); // 无装箱
}
}
// ❌ 避免:导致装箱的操作
public class EntityCache
{
private readonly Dictionary<int, object> _cache = new();
public void Add(int id, object data)
{
_cache[id] = data; // 值类型会装箱
}
}
内存优化
减少内存分配和 GC 压力是性能优化的重要方面。
1. 使用 Span<T>
// ✅ 好的做法:使用 Span 避免分配
public void ProcessData(ReadOnlySpan<byte> data)
{
// 直接在栈上处理,无堆分配
Span<int> results = stackalloc int[data.Length];
for (int i = 0; i < data.Length; i++)
{
results[i] = ProcessByte(data[i]);
}
// 使用结果
UseResults(results);
}
// 解析字符串避免分配
public bool TryParseValue(ReadOnlySpan<char> input, out int result)
{
return int.TryParse(input, out result);
}
// ❌ 避免:不必要的数组分配
public void ProcessData(byte[] data)
{
// 创建新数组,产生 GC
var results = new int[data.Length];
for (int i = 0; i < data.Length; i++)
{
results[i] = ProcessByte(data[i]);
}
UseResults(results);
}
2. 结构体优化
// ✅ 好的做法:使用 readonly struct
public readonly struct Vector2D
{
public readonly float X;
public readonly float Y;
public Vector2D(float x, float y)
{
X = x;
Y = y;
}
public float Length() => MathF.Sqrt(X * X + Y * Y);
public Vector2D Normalized()
{
var length = Length();
return length > 0 ? new Vector2D(X / length, Y / length) : this;
}
}
// ❌ 避免:可变结构体
public struct Vector2D
{
public float X { get; set; }
public float Y { get; set; }
// 可变结构体可能导致意外的复制
}
3. 避免闭包分配
// ✅ 好的做法:避免闭包捕获
public class EventProcessor : AbstractSystem
{
private readonly Action<PlayerEvent> _cachedHandler;
public EventProcessor()
{
// 缓存委托,避免每次分配
_cachedHandler = HandlePlayerEvent;
}
protected override void OnInit()
{
this.RegisterEvent(_cachedHandler);
}
private void HandlePlayerEvent(PlayerEvent e)
{
ProcessEvent(e);
}
}
// ❌ 避免:每次都创建新的闭包
public class EventProcessor : AbstractSystem
{
protected override void OnInit()
{
// 每次都创建新的委托和闭包
this.RegisterEvent<PlayerEvent>(e =>
{
ProcessEvent(e);
});
}
}
4. 字符串优化
// ✅ 好的做法:减少字符串分配
public class Logger
{
private readonly StringBuilder _sb = new();
public void LogFormat(string format, params object[] args)
{
_sb.Clear();
_sb.AppendFormat(format, args);
Log(_sb.ToString());
}
// 使用字符串插值的优化版本
public void LogInterpolated(ref DefaultInterpolatedStringHandler handler)
{
Log(handler.ToStringAndClear());
}
}
// 使用 string.Create 避免中间分配
public string CreateEntityName(int id, string type)
{
return string.Create(type.Length + 10, (id, type), (span, state) =>
{
state.type.AsSpan().CopyTo(span);
span = span.Slice(state.type.Length);
span[0] = '_';
state.id.TryFormat(span.Slice(1), out _);
});
}
// ❌ 避免:频繁的字符串拼接
public string CreateEntityName(int id, string type)
{
return type + "_" + id.ToString(); // 创建多个临时字符串
}
最佳实践
1. 性能测试
// ✅ 使用性能测试验证优化效果
[TestFixture]
public class PerformanceTests
{
[Test]
[Performance]
public void ObjectPool_Performance_Test()
{
var pool = new BulletPoolSystem();
pool.Prewarm("bullet", 1000);
Measure.Method(() =>
{
var bullet = pool.Acquire("bullet");
pool.Release("bullet", bullet);
})
.WarmupCount(10)
.MeasurementCount(100)
.Run();
}
[Test]
public void CompareAllocationMethods()
{
// 测试对象池
var poolTime = MeasureTime(() =>
{
var pool = ArrayPool<int>.Shared;
var array = pool.Rent(1000);
pool.Return(array);
});
// 测试直接分配
var allocTime = MeasureTime(() =>
{
var array = new int[1000];
});
Assert.Less(poolTime, allocTime, "Object pool should be faster");
}
private long MeasureTime(Action action)
{
var sw = Stopwatch.StartNew();
for (int i = 0; i < 10000; i++)
{
action();
}
sw.Stop();
return sw.ElapsedMilliseconds;
}
}
2. 性能监控
// ✅ 实现性能监控系统
public class PerformanceMonitor : AbstractSystem
{
private readonly Dictionary<string, PerformanceMetric> _metrics = new();
private float _updateInterval = 1.0f;
private float _timeSinceUpdate = 0f;
public void Update(float deltaTime)
{
_timeSinceUpdate += deltaTime;
if (_timeSinceUpdate >= _updateInterval)
{
UpdateMetrics();
_timeSinceUpdate = 0f;
}
}
public void RecordMetric(string name, float value)
{
if (!_metrics.TryGetValue(name, out var metric))
{
metric = new PerformanceMetric(name);
_metrics[name] = metric;
}
metric.AddSample(value);
}
private void UpdateMetrics()
{
foreach (var metric in _metrics.Values)
{
Logger.Info($"{metric.Name}: Avg={metric.Average:F2}ms, " +
$"Min={metric.Min:F2}ms, Max={metric.Max:F2}ms");
metric.Reset();
}
}
}
public class PerformanceMetric
{
public string Name { get; }
public float Average => _count > 0 ? _sum / _count : 0;
public float Min { get; private set; } = float.MaxValue;
public float Max { get; private set; } = float.MinValue;
private float _sum;
private int _count;
public PerformanceMetric(string name)
{
Name = name;
}
public void AddSample(float value)
{
_sum += value;
_count++;
Min = Math.Min(Min, value);
Max = Math.Max(Max, value);
}
public void Reset()
{
_sum = 0;
_count = 0;
Min = float.MaxValue;
Max = float.MinValue;
}
}
3. 性能分析工具
使用性能分析工具定位瓶颈:
- Unity Profiler - Unity 内置的性能分析工具
- Godot Profiler - Godot 的性能监控工具
- dotTrace - JetBrains 的 .NET 性能分析器
- PerfView - Microsoft 的性能分析工具
4. 性能优化清单
在优化性能时,遵循以下清单:
- 使用对象池减少 GC 压力
- 正确管理事件订阅,避免内存泄漏
- 使用结构体事件避免堆分配
- 合理使用协程,避免过度嵌套
- 异步加载资源,避免阻塞主线程
- 使用 ECS 架构提高数据局部性
- 使用 Span<T> 和 stackalloc 减少分配
- 避免装箱操作
- 缓存常用的委托和对象
- 批量处理高频操作
- 定期进行性能测试和监控
常见问题
Q1: 什么时候应该使用对象池?
A: 当满足以下条件时考虑使用对象池:
- 对象创建成本高(如包含复杂初始化逻辑)
- 对象频繁创建和销毁(如子弹、特效粒子)
- 对象生命周期短暂但使用频繁
- 需要减少 GC 压力
Q2: 如何判断是否存在内存泄漏?
A: 观察以下指标:
- 内存使用持续增长,不会下降
- GC 频率增加但内存不释放
- 事件订阅数量持续增加
- 对象池中的活跃对象数量异常
使用内存分析工具(如 dotMemory)定位泄漏源。
Q3: 协程和 async/await 如何选择?
A: 选择建议:
- 协程 - 游戏逻辑、动画序列、分帧处理
- async/await - I/O 操作、网络请求、文件读写
协程更适合游戏帧循环,async/await 更适合异步 I/O。
Q4: 如何优化大量实体的性能?
A: 使用 ECS 架构:
- 使用值类型组件减少堆分配
- 预构建查询避免每帧创建
- 批量处理实体操作
- 使用并行处理(如果支持)
Q5: 什么时候应该进行性能优化?
A: 遵循以下原则:
- 先测量,再优化 - 使用性能分析工具找到真正的瓶颈
- 优先优化热点 - 集中优化占用时间最多的代码
- 避免过早优化 - 在功能完成后再进行优化
- 保持可读性 - 不要为了微小的性能提升牺牲代码可读性
Q6: 如何减少 GC 压力?
A: 采取以下措施:
- 使用对象池复用对象
- 使用值类型(struct)而非引用类型(class)
- 使用 Span<T> 和 stackalloc 进行栈分配
- 避免闭包捕获和装箱操作
- 缓存常用对象和委托
- 使用 StringBuilder 而非字符串拼接
总结
性能优化是一个持续的过程,需要:
- ✅ 测量优先 - 使用工具定位真正的瓶颈
- ✅ 合理使用对象池 - 减少 GC 压力
- ✅ 优化事件系统 - 避免内存泄漏和不必要的处理
- ✅ 高效使用协程 - 避免过度嵌套和不必要的等待
- ✅ 智能资源管理 - 异步加载和合理缓存
- ✅ ECS 架构优化 - 提高数据局部性和处理效率
- ✅ 内存优化 - 减少分配,使用栈内存
- ✅ 持续监控 - 建立性能监控系统
记住,性能优化要在可维护性和性能之间找到平衡,不要为了微小的性能提升而牺牲代码质量。
文档版本: 1.0.0 更新日期: 2026-03-07