当您有 100k 到 100 万个对象/索引时,维护脏对象或索引列表的有效方法是什么?



我必须实时迭代超过100,000个对象(对于电子游戏)。

为了使事情更快,我实现了一个HashSet<int> DirtyIndexes,每当一个对象被修改时,我执行DirtyIndexes.Add(index)并只迭代那些索引。

不幸的是,HashSet.Add()的重复检查有点慢,在每帧10k+对象的情况下,添加这么多索引的过程杀死了使用这个系统的任何优势。

我希望找到一个集合,允许多个对象共享一个键,并返回与搜索键匹配的所有内容的数组,但是找不到任何东西。

另一个想法是使用与对象数量相同大小的int数组,并将0设置为干净,1设置为脏,但随后我必须在代码中添加几个if来过滤索引,这也会减慢速度。

我也可以将脏对象添加到DirtyObject列表中,但这样我就必须再次防止重复,并以同样的问题结束。

有人有解决办法吗?

为了提高效率,最好使用数组而不是列表。如果您想使用List类的特性,但是在需要时使用数组,那么您可以使用具有数组和列表作为类变量的类,并且仅在列表更改时更新数组,如下所示:

using System;
using System.Collections.Generic;
public class CustomList<T>
{
//private
bool _isDirty;
List<T> _list;
T[] _array;
//public
T[] getArray() {
if (_isDirty)
_array = _list.ToArray();
return _array;
}
//handler methods
public CustomList() {
_list = new List<T>();
}
public void Add(T item) {
_list.Add(item);
_isDirty = true;
}
public void Remove(T item) {
_list.Remove(item);
_isDirty = true;
}
public void RemoveAt(int i) {
_list.RemoveAt(i);
_isDirty = true;
}
public bool Contains(T item) {
return _list.Contains(item);
}
public int Count() {
return _list.Count;
}
public T this[int index] {
get { return _list[index]; }
set { _list[index] = value; }
}
public void Clear() {
_list.Clear();
_isDirty = true;
}
public void RemoveAll(Predicate<T> predicate) {
_list.RemoveAll(predicate);
_isDirty = true;
}
public T Find(Predicate<T> predicate) {
return _list.Find(predicate);
}
public void Insert(int index, T item) {
_list.Insert(index, item);
_isDirty = true;
}
public void ForEach(Action<T> action) {
_list.ForEach(action);
}
}

您不妨为DirtyObjects使用List<int>,因为List<T>本质上只是一个具有生活质量特征的数组,并且您可以非常快速地遍历它。

为了防止重复,您可以在每个对象中维护一个bool isDirty标志,并且仅在其标志尚未设置时才将对象添加到列表中:

private void OnChanged() {
if (!this.isDirty) {
this.isDirty = true;
DirtyObjects.Add(this.index);
}
}

这将防止任何对象被添加到DirtyObjects不止一次。(在这个过程中,它不需要做任何类型的查找!)这个逻辑也不需要特别快,因为它只在一个干净的对象变成脏的时候执行。

不管代码是什么,最终都会"清理"。脏对象可以将它们的isDirty标志设置回false,循环继续。

你们提出了很好的建议,但是要处理这么多实体,我必须想出别的办法。

秘诀是使用两个数组。一个用于值,第二个用于它的脏标志(0或1)。这可以防止重复(如果值多次设置为脏也没关系)并且速度更快。这两个数组都提前初始化为我的池大小。

的想法是使用这样的一组数组的每一个值,我想要迭代。这样做可以以不同的速率处理数据,同时保持与Unity的作业系统的兼容性。

爆发是惊人的,矢量化,你是下一个!

编辑:为了澄清,我在渲染时对脏列表进行排序(我已经必须迭代所有内容),并在下一帧处理脏数据。处理中的1帧延迟允许提前规划工作,并且在游戏中无法检测到。

最新更新