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