具有非常快的迭代速度和良好的添加和删除速度的集合
我正在寻找一个可以快速迭代的集合。我还将定期添加项目和删除(特定)项目,因此理想情况下希望这些操作也能快速进行。
我正在 xbox 上进行开发,因此仅限于紧凑框架(或多或少)。将垃圾和对象分配保持在最低限度非常重要,因此任何可以为我的对象预先分配空间的东西都会很棒。
我将在集合中存储 uint
(但如果需要的话可以是 int
)。不过,通用的解决方案会很好,因为我确信我将来会有需要。
.net 集合将是理想的,否则轻量级和开源的东西会很棒。
有没有适合我需要的集合类?如果没有,我将如何创建一个?
详细地说,它们是类应该处理每个帧的对象 ID。它们通常会按升序添加,中间有间隙。没有上限。不过,任何内容都可以删除,这会留下间隙。
迭代顺序并不完全重要,但如果顺序一致,它将非常有用(特别是对于调试)。
I'm after a collection that I can iterate through very fast. I'll also be adding items and removing (specific) items fairly regularly and so ideally would like those operations to be fast too.
I'm developing on the xbox and so am restricted to the compact framework (more or less). It's very important that I keep garbage and object allocations to a minimum, so anything where i can pre-allocate space for my objects would be great.
I'll be storing uint
s (but can be int
s if needed) in the collection. A generic solution would be good though, as i'm sure i'll have a need in the future.
A .net collection would be ideal, failing that something light-weight and open source would be great.
Is there a collection class that would suit my needs? If not, how would I go about creating one?
To elaborate a bit, they are object id's that a class should process each frame. They'll generally be added in ascending order, with gaps. There's no upper limit. Any could be removed though, which would leave gaps.
Iteration order isn't completely important, but it would be very useful (particularly for debugging) if it was in a consistent order.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我有两个建议可以尝试:
首先,使用 Reflector 或 ILSpy 来查看 Generic
List
内部怎么样?我假设 CF 中没有实现,或者您可以使用它。通用List
支持数组,并使用从长度为 4 的数组开始的加倍算法,每次调用 .Add 都会导致容量加倍,并执行 Array.Copy 到新数组中。除非您明确将“容量”设置为零,否则它不会调整大小,因此从内存的角度要小心。添加是一回事,但每次删除都会导致数组在被删除的索引之后被复制并左移。第二个建议是这样的 - 创建一个自定义类来包装一个整数数组来处理这个问题,它使用与通用
List
类似的双精度算法(或四倍)(以处理调整大小),但是从大小 256 开始。您可以按照您喜欢的速度将对象整数 id 添加到其中,而且它不会经常加倍。您还可以通过将 (int)-1 或 uint.MaxValue 指定为“空索引”来模拟删除,这意味着删除时没有 Array.Copy。然后,在绘制之前对每帧应用某种快速排序来对对象索引数组进行排序。如果按升序排序,所有 -1 将出现在开头(或 uint.MaxValues 在末尾),并且可以被忽略。您可以通过在单独的线程上调整大小和删除前面的-1
来定期“收集”索引数组(注意 - 使用锁定;)您觉得怎么样?只是想你会每帧抵消一些计算以进行快速排序(在 xbox 上并不昂贵,而在每个删除和一些添加上进行内存分配/收集(昂贵)。
更新 - BlockArray - List其中 T 对此的进一步评论我最近正在尝试动态大小
列表的最有效的数据结构,并通过实验发现数组块 - 一个由 T[] 列表支持的类。其中每个T[] 是固定大小块的数组,例如 512、4096、8192,与普通 List相比有几个优点
我发现 List对于 List来说,其性能远远优于 Add(),尤其是当总大小变大时,这是由于 List的加倍算法需要一个值。新数组的大小是旧数组的 2 倍,每当超过大小时,memcpy 都会覆盖旧数组,
预分配(预定义容量)比 List慢得多。对于小块大小(512),但对于大块大小(8192)仅稍微慢一些。删除是有问题的,因为需要复制/左移许多块。
有趣的是 List中的(块列表),您可以缓存/池化块。如果足够小,T[] 的块适合小对象堆(有利于压缩,更快的分配),可以适合 L2 缓存,并且可以预先分配和池化许多块
I've got two suggestions to try:
Firstly, what about using Reflector or ILSpy to look inside Generic
List<T>
? I assume that implementation isn't in the CF or you could use it. GenericList<T>
is array backed and uses a doubling algorithm starting at length 4 array, every call to .Add over the Capacity causes it to double and perform an Array.Copy into the new array. It doesn't resize unless you explicitly set Capacity to zero so beware from a memory point of view. Adds are one thing but each Remove will cause the array to be copied and left-shifted after the index that was removed.The second suggestion is this - what about about creating a custom class which wraps an integer array to handle this which uses a similar double algorithm (or quadrupling) to Generic
List<T>
(to handle resizing) but starts at say size 256. You could add object integer id's to this out of order as fast as you like and it won't double too often. You could also simulate a remove by designating (int)-1 or uint.MaxValue as a "null index" meaning no Array.Copy on remove. Then, apply some sort of fast sorting per frame to sort the object index array before drawing. If you sort ascending all your -1s will appear at the start (or uint.MaxValues at end) and can be ignored. You can periodically "collect" the index array by resizing and removing the preceeding-1
's on a separate thread (beware - use locking ;)What do you think? Just thinking you will offset some computation once per frame for fast sorting (not expensive on xbox vs. memory allocation/collection on each Remove and some Adds (expensive).
UPDATE - BlockArray - List<T[]> where T is fixed size array
A further comment on this. I was recently experimenting with the most efficient data-structure for dynamically sized lists and discovered by experimentation that array blocks - a class which is backed by a List of T[], where each T[] was an array of fixed size block, e.g. 512, 4096, 8192 as several advantages over a plain List<T>.
I found that an implementation of Add() (where size is unknown) in a List<T[]> vastly outperformed Add() for List<T>, especially when the total size became larger. This is due to List<T>'s doubling algorithm which requests a new array 2x the size of the old, and memcpy's the old array over whenever size is exceeded.
Iterating speed is similar. Pre-allocation (pre-defined capacity) was much slower than List<T> for small block sizes (512), but only slightly slower for large block sizes (8192). Removes are problematic, as require copying/left shifting many blocks.
What's interesting is in a List<T[]> (block list), you can cache/pool the blocks. If small enough, blocks of T[] fit into the small object heap (favouring compaction, quicker allocation), may fit into the L2 cache and a number of blocks could be pre-allocated and pooled
使用 Mono 的
HashSet
怎么样?https://github .com/mono/mono/blob/master/mcs/class/System.Core/System.Collections.Generic/HashSet.cs
它已获得 MIT X11 的许可,这是一个许可。
迭代顺序可能会出现问题,具体取决于
T
如何实现GetHashCode
,但在使用uint
时这不应该是问题。另一方面,GetHashCode
的默认实现在不同实例上返回不同的值,这可能会导致不同的迭代顺序。迭代顺序还可能取决于添加项目的顺序。即,如果以不同的顺序添加项目,则包含相同项目的两个集合可能会以不同的顺序进行迭代。
还有一个
SortedSet
集合,但不知道它有什么性能特点。How about using Mono's
HashSet<T>
?https://github.com/mono/mono/blob/master/mcs/class/System.Core/System.Collections.Generic/HashSet.cs
It's licensed under MIT X11, which is a permissive license.
Iteration order might be a problem depending on how
T
implementsGetHashCode
, but that shouldn't be a problem when usinguint
. The default implementation ofGetHashCode
on the other hand returns different values on different instances, which might lead to different iteration order.Iteration order might also depend on the order items are added. i.e. two collections containing the same items might iterate in a different order if the items were added in a different order.
There is also a
SortedSet<T>
collection, but I don't know what performance characteristics it has.自定义类 SparseList怎么样:
我实现了这个(尚未测试),但我存储的是对我的游戏实体的实际引用而不是 id,因此您需要以某种方式针对 int 或 Nullable 进行调整。 (好的,为了确保我回答你的问题的 int/uint 要求,我还添加了一个稍有不同的 SparseValueList,使用 default(T) 而不是 null。这意味着你不能在列表中使用 0。)你也许可以如果您认为不需要,请删除版本控制——大多数游戏可能不需要。
How about a custom class, SparseList<T>:
I implemented this (not yet tested) but I'm storing actual references to my game entities instead of id's, so you'll need to adapt this for int's or Nullable somehow. (Ok to make sure I answer your question's int/uint requirement I also added a SparseValueList<T> that's slightly different, using default(T) instead of null. This means you can't use 0 in the list.) You could perhaps take out versioning if you don't think you need it -- most games may not.