IPixel
我看到的方式是,如果您在非常精细的粒度对象(如抽象接口)级别使用多态性,那么对象级别的多态性本质上是昂贵的。在这种情况下,从效率的角度来看,围绕IPixel
依赖关系旋转的视频处理软件将非常糟糕,因为它没有喘息的空间来优化。除了每个像素的动态调度成本之外,甚至这里所需的虚拟指针也可能比整个像素本身大,内存使用量增加一倍或三倍。此外,我们不能再以超出单个像素的方式使用像素表示,而且最可怕的是,图像中的相邻像素甚至可能不会在内存中连续表示。
同时IImage
可以提供足够的优化空间,因为图像建模像素的集合/容器,并且仍然具有足够的灵活性(例如:每种像素格式的不同具体图像表示)。现在每个图像的动态调度很便宜,并且虚拟指针的大小对于整个图像可以忽略不计。我们还可以探索如何以允许我们一次有效地处理多个像素的方式将像素表示为我们内心的内容。因此,与您类似,我认为它是在适当的粗略水平上设计对象的问题,这通常意味着事物的集合,以减少所有开销和优化所面临的障碍。
对象不存储自己的数据成员,它们只存储对集合的引用,它们的数据成员按顺序存储在自己的容器中,并且它们的成员方法从这些容器返回数据,这样不需要的数据结束的可能性应该减少到 CPU 的路上,并且增加在不久的“未来”需要的数据的几率。
我喜欢这个想法,但如果你对多态上下文太过分了,你可以回到自定义内存分配器和排序基指针。我经常发现这种设计的用途是在需要聚合以提高效率的情况下提高使用单个元素的便利性(一种情况是使用 SoA 表示的容器,另一种我将在下面介绍)。
多态情况不一定有那么多好处,因为固有的问题在于一次一个地处理颗粒事物的非同质处理。为了恢复效率,我们必须恢复关键循环的同质性。
非同质关键循环
举个例子,Orc
继承Creature
和Human
继承Creature
,Elf
继承Elves
,但是人类和兽人和精灵有不同的大小/字段,不同的对齐要求和不同的vtables。在这种情况下,当客户端代码想要处理它们的非同质列表时,它们存储指向生物的多态基指针,如下所示:
for each creature in creatures:
creature.do_something();
...与这会牺牲多态性相反:
for each orc in orcs:
orc.do_something();
for each human in humans:
humans.do_something();
for each elf in elves:
elves.do_something();
...如果我们每次引入一种新型生物时都需要在许多地方这样做,这将是一个真正的 PITA 扩展...
...然后,如果我们想保持多态解决方案但仍以非同质方式一次处理每个生物,那么无论每个生物是否仅存储反向指针,我们最终仍会丢失时间和空间局部性到容器与否。我们失去了 vtable 的时间局部性,因为我们可能在一次迭代中访问一个 vtable,然后在下一次迭代中访问另一个 vtable。这里的内存访问模式也可以是随机的和零星的,导致空间局部性的丢失,因此我们最终会出现大量的缓存未命中。
因此,在这种情况下,如果您想要继承和多态性,我的解决方案是在容器级别进行抽象:Orcs
继承Creatures
、Humans
继承Creatures
。Elves
继承Creatures
. 当它想要表达对特定生物执行的操作时,这会将一些额外的复杂性转移到客户端代码,但是现在上面的顺序循环可以这样编写:
for each creatures in creature_types:
creatures.do_something();
在第一次迭代中,这可能会对整个兽人列表(可能就像存储在数组中的一百万兽人)做一些事情。现在该列表中的所有兽人都可以连续存储,我们正在对该列表中的所有兽人应用同构功能。在这种情况下,我们有大量的喘息空间可以在不改变设计的情况下进行优化。
我们在这里仍然有一个利用多态性的非同质循环,但现在这样便宜得多,因为我们只为整个生物容器支付开销,而不是为每个单独的生物支付开销。处理单个生物的循环现在是同质的。这类似于使用抽象IImage
来照亮一堆图像(一堆像素容器),而不是对一次实现IPixel
的一个抽象像素对象这样做。
同构循环和表示
因此,这将繁重的关键循环从非同质循环转移到一次处理各种不同数据的非同质循环,并将它们转移到同质循环处理连续存储的同质数据。
这是我对界面设计的一般策略。如果它容易以难以优化的方式产生热点,那么在我看来,固有的问题是界面设计的层次太细(Creature
,不是Creatures
)。
因此,如果希望使用 OOP,这就是我解决此问题的方法。我认为您的设计理念类型可能有用的地方是简化客户端代码必须表达仅适用于一个特定生物的操作的情况,此时它们可以通过某种指向容器的代理对象工作,并且可能存储指向特定条目的索引或指针以方便使用,例如引用抽象容器CreatureHandle
之一中的特定条目。Creatures