【问题标题】:C++ - Performance of vector of pointer to objects, vs performance of objectsC++ - 指向对象的向量的性能与对象的性能
【发布时间】:2014-05-07 08:58:09
【问题描述】:

在这种情况下,问题场景是一个游戏,所以所有资源都在开始时分配,然后迭代一个级别。

存储在向量中的对象是复杂类的实例,当然,在加载时将它们实际复制到向量中是耗时的,但关注度较低。

但如果我主要关心的是在运行时对类对象的迭代速度,我会更好地将类对象本身存储在向量中,而不是像传统建议的那样仅指向类对象的指针吗?

在这个例子中我不关心内存管理,只关心迭代速度。

【问题讨论】:

  • 如果您谈论的是已构建的向量,性能差异将是微不足道的,但 可能 有利于对象向量,因为消除了一个间接级别.和往常一样,同时介绍两者并亲自查看。
  • 但是,在对象向量场景中,连续的数据难道不会固有地加快迭代速度吗?
  • 它们都是同一类型吗?如果不是,那么您必须使用指针来避免切片。
  • @metamorphosis 除其他原因外,是的。但很可能你的“对象”也有它自己的动态成员(大多数都有;字符串、其他向量等)。如果它是真正的 POD 类型并且足够小以在数据缓存中有很好的负载,那么毫无疑问,对象向量会表现得更好,再次假设我们正在谈论一个已经构建的向量。但就像我说的,profile 它。
  • 另请注意,如果这是一个问题,可以通过为向量提供自定义分配器来使指针数组更加缓存友好,以便从连续内存的内存池中分配对象。

标签: c++ class object vector iteration


【解决方案1】:

我回答这个问题很晚,但性能方面很重要,到目前为止,网上的答案都是纯粹的理论和/或专注于内存管理方面。所以这里是我最近尝试的三个相关场景的一些实际基准测试信息。您的结果可能会有所不同,但至少对实际应用中的结果有所了解。

这里引用的A类有大约10个成员字段,其中一半是原语,另一半是std::stringstd::vector<int>和其他动态大小的容器。该应用程序已经进行了相当优化,因此我们想看看现在哪种架构可以让我们在A 的集合上进行最快的循环。 A 对象的任何成员字段的值可能会在应用程序生命周期内发生变化,但向量中 A 对象的数量不会随着我们执行的多次重复迭代而改变(这种持续迭代约占其中的 95%)应用程序的执行时间)。在所有场景中,循环都是使用典型的std::iteratorstd::const_iterator 执行的。每个枚举的A 对象至少有几个成员字段被访问。

场景 1 — 对象指针向量

虽然是最简单的,std::vector<A*> 的这种架构最终还是比其他架构稍慢。

场景 2 — 对象指针向量,使用 Placement New 分配对象

这种方法背后的想法是,我们可以通过强制将对象分配到连续的内存空间来提高缓存的局部性。因此,std::vector 实现保证了对象指针的std::vector<A*> 是连续的,并且A 对象本身在堆上也将是连续的,因为我们使用了placement new 习惯用法。我使用了answer 中概述的相同方法;更多关于安置新的信息可以找到here

此方案比方案 1 快 2.7%。

场景 3 — 对象向量

这里我们直接使用std::vector<A>std::vector 实现保证我们的A 对象在内存中是连续的。请注意,对象的std::vector 确实涉及A 的移动和复制构造函数的考虑。为避免不必要的移动和/或重建,最好提前std::vector.reserve() 可能需要的最大大小(如果可能),然后尽可能使用std::vector.emplace_back()(而不是push_back())。循环这个结构是最快的,因为我们能够消除一级指针间接。

这种方法比场景 1 快 6.4%。

与另一个问题相关的answer 还表明,普通对象(作为类成员)可以比各自的指针(作为类成员)快得多。

【讨论】:

    【解决方案2】:

    第一件事是指针应该用于存储大容量的东西。因为如果您使用对象数组,如果会创建 n 个庞大的对象并在每次存储时复制每个对象(这也是一个很大的成本) 第二件事,如果你使用向量(STL),向量的大小在每次内存满时都会增长。 主要成本是复制第一到第二的数据,这实际上是主要成本,即复制。 此外,如果使用内置,此成本是最低的成本。

    【讨论】:

    • 这不能回答我的问题。这是“标准”答案,与内存管理相关,但与迭代速度无关。
    • "如果您使用向量 (STL),每次充满内存时,向量的大小都会增长两倍"——我不知道您从哪里得到这些信息,但标准没有指定类似的东西。允许实现具有任何类型的算法来重新分配和选择新的更大尺寸。
    • 你是对的,是我的错误,它没有每次都增长两次
    【解决方案3】:

    不,她没有错,她绝对正确,虽然你问的只是快速迭代,但这与内存有很多联系......更多的内存堆栈更慢将是访问......

    我有一个现场演示...

    #include <iostream>
    #include <string>
    #include <vector>
    #include "CHRTimer.h"
    
    struct Items
    {
        std::string name;
        int id;
        float value;
        float quantity;
    };
    
    void main()
    {
    
        std::vector<Items> vecItems1;
        for(int i = 0; i < 10000; i++)
        {
            Items newItem;
            newItem.name = "Testing";
            newItem.id = i + 1;
            newItem.value = 10.00;
            newItem.quantity = 1.00;
    
            vecItems1.push_back(newItem);
        }
    
        CHRTimer g_timer;
        g_timer.Reset();
        g_timer.Start();
        for(int i = 0; i < 10000; i++)
        {
            Items currentItem = vecItems1[i];
        }
        g_timer.Stop();
        float elapsedTime1 = g_timer.GetElapsedSeconds();
        std::cout << "Time Taken to load Info from Vector of 10000 Objects -> " << elapsedTime1 << std::endl;
    
        std::vector<Items*> vecItems;
        for(int i = 0; i < 100000; i++)
        {
            Items *newItem = new Items();
            newItem->name = "Testing";
            newItem->id = i + 1;
            newItem->value = 10.00;
            newItem->quantity = 1.00;
    
            vecItems.push_back(newItem);
        }
    
        g_timer.Reset();
        g_timer.Start();
        for(int i = 0; i < 100000; i++)
        {
            Items *currentItem = vecItems[i];
        }
        g_timer.Stop();
        float elapsedTime = g_timer.GetElapsedSeconds();
        std::cout << "\nTime Taken to load Info from Vector of 100000 pointers of Objects -> " << elapsedTime;
    }
    

    【讨论】:

    • 第一种情况是 STL 向量,对象只有 10000 次迭代,但仍需要 0.0269 秒
    • 第二个是 10 次,即 100000 次迭代,但仍需要 0.005 秒
    • 内存与速度有直接联系,就像在您的计算机中一样...当您新建一个窗口时,它的速度超快....当您开始安装软件时,访问速度变慢并且您的计算机变慢
    • 这不是关系,user3471203。而“她”又是谁?你在说什么?
    猜你喜欢
    • 2014-09-13
    • 2011-10-01
    • 2011-02-11
    • 2021-09-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多