我想知道对象指针的std :: vector如何影响程序的性能,而使用直接包含对象的std :: vector。具体来说,我指的是程序的速度。
我被教导使用std :: vector而不是其他STL,例如std :: list,因为它的所有数据都是连续存储在内存中,而不是支离破碎。这意味着迭代元素很快,但我的想法是,如果我的向量包含指向对象的指针,那么对象仍然可以存储在内存中的任何位置,并且只有指针是连续存储的。我想知道在迭代向量和访问对象时这会如何影响程序的性能。
我当前的项目设计使用指针向量,以便我可以利用虚拟函数,但是我不确定这是否值得我的向量变得非常大时可能遇到的速度命中。谢谢你的帮助!
答案 0 :(得分:2)
如果你需要多态性,正如人们所说,你应该将指针存储到基数。如果稍后您认为此代码很热并且需要优化它的cpu缓存使用率,则可以通过使对象完全适合缓存通道和/或使用自定义分配器来确保解除引用数据的代码局部性。
切片是指按值存储对象时基础和复制构造或分配到它们中的Derived,Derived将被切片,复制构造函数或分配器只接受一个Base并将忽略Derived中的任何数据,没有足够的空间在Base中分配以获取Derived的完整大小。即如果Base是8个字节且Derived是16,那么即使你提供了一个显式获取Dervived的拷贝构造函数/分配器,目标值中只剩下Base的8个字节的空间。
我应该说,如果你大量使用虚拟化而不被优化器忽略,那么真的不值得考虑数据缓存的一致性。指令高速缓存未命中比数据高速缓存未命中更令人失望,虚拟化可能导致指令高速缓存未命中,因为它必须在将函数加载到指令高速缓存之前查找vtable指针,因此不能预先加载它们。
CPU倾向于将尽可能多的数据预加载到缓存中,如果加载地址,整个缓存通道(~64字节)将被加载到缓存通道中,并且通常它还将加载缓存通道。之后人们就如此热衷于数据本地化。
所以在你的指针向量场景中,当你加载第一个指针时,你会立即在缓存中获得大量指针,通过指针加载将触发缓存未命中并加载该对象周围的数据,如果你的实际粒子是16个字节,彼此本地,你不会失去太多。如果它们遍布整个堆并且很大,那么在每次迭代时都会得到非常高速缓存,并且在处理粒子时相对正常。
传统上,粒子系统往往非常热,并且喜欢紧密打包数据,通常会看到16字节的普通旧数据粒子系统,您可以使用非常可预测的分支线性迭代。这意味着您通常可以依赖每个缓存通道4个粒子,并使预取器远远超出您的代码。
我还应该说cpu缓存是cpu依赖的,我专注于intel x86。例如,手臂往往远远落后于英特尔和管道不那么复杂,预取器的能力较差,因此缓存未命中可能不那么具有破坏性。