【问题标题】:Returning copies of vector elements vs returning pointers to vector elements返回向量元素的副本与返回指向向量元素的指针
【发布时间】:2015-03-27 14:21:02
【问题描述】:

我有一个这样的对象:

class Node {
    float x, y, z;
    size_t tag;
    bool isFree;
    std::vector<size_t> connections; // Usually ~10-100 in length
};

只是为了让您了解大小。这些节点对象的列表包含数百万个实例,我将其称为std::vector&lt;Node&gt; masterNodes。我在其他地方有一个函数,它返回这些对象的容器,例如这个:

std::vector<Node> find_nodes()
{
    std::vector<Node> nodes;
    // copy some elements from masterNodes that meet our conditions
    return nodes;
}

我的问题是返回 Node* 的向量是否会更有效,或者我的编译器是否会对其进行足够优化,以使像我这样的对象的增益最小?例如

std::vector<Node*> find_nodes()
{
    std::vector<Node*> nodes;
    // point to some elements from masterNodes that meet our conditions
    return nodes;
}

我看到了一些回复(例如this one),这些回复表明复制可能几乎与返回指针一样有效,并承认返回指向向量元素的指针的危险。

【问题讨论】:

  • 如果每个Node 有一个std::vector 可能有100 个元素,而你有一个std::vector&lt;Node&gt;,那肯定会比std::vector&lt;Node*&gt; 大很多。只需确保您知道谁应该管理该内存,这样您就不会泄漏它,也不会持有悬空指针。
  • 对象向量很可能会更有效。但如果这很重要,您应该同时尝试并查看(但在您的基准测试中包括第二个版本所需的潜在内存管理。)
  • 另一种选择是返回索引,我想。但这将我与一个向量联系在一起,这意味着我必须将索引引用回 masterNodes。不过,这并不是一个完全没有吸引力的想法,因为索引也在问题域中,而不仅仅是实现。节点有索引。
  • 我认为“回归”在这里并不重要。从 masterNodes 复制节点是这里真正的问题。复制完成后,返回应该非常有效(感谢 RVO)。 RVO 可以申请,并且会使回报非常有效。但是我们不关心快速返回,因为find_nodes() 内的复制本身很慢。
  • 你对从find_nodes()检索到的节点向量做了什么?这是确定最适合您问题的解决方案的重要细节。

标签: c++ vector


【解决方案1】:

参考地点

现实生活中的性能在很大程度上取决于硬件,如果您知道如何使用它,就可以获得很多。

利用参考局部性可以获得最大的硬件性能提升之一。这意味着处理时间和空间上距离很近的数据可以更好地利用内置 CPU 缓存,它比使用主内存 (RAM) 快得多。

这就是为什么复制数据以允许连续的本地访问可以提高性能的原因。

与此相反的是使用间接。间接是使用引用或指针而不是值本身访问内存的能力。这可以让您避免复制内容,但是当硬件必须一直从主内存的不同位置获取每一位数据时,您可以充分利用 CPU 缓存。

性能必须经过测试

基本上,复制大数据会导致一次性性能损失,但如果您将大量使用数据,则可以使用参考局部性来弥补这一点。

但是,您必须自己进行测试才能知道最适合您的方法。在您的情况下,复制数据的成本可能会导致更大的性能损失,而不是更好地使用 CPU 缓存来弥补。

【讨论】:

  • 我的意思是在问题中承认这一点,对不起。从find_nodes() 返回的大小远小于调用find_nodes() 的次数。例如。 find_nodes() 将按masterNodes(百万)大小的顺序调用,但结果将在10-100个节点的顺序上。
  • @Fadecomic 好吧,当您开始处理数据时(在从find_nodes 返回之后),我的推理就发挥了作用。如果您所做的只是测量复制对象与指针所需的时间,那么复制指针当然会更快。
【解决方案2】:

返回Node* 的向量会更有效,因为您的nodes 是来自masterNodesNodes 的副本 向量,而您的Node比指针大得多。没有什么像返回值优化或移动语义可以帮助您拥有(并返回)副本向量这一事实。

顺便说一句,您可以返回vector&lt;vector&lt;Node&gt;::iterator&gt; 而不是vector&lt;Node*&gt;。它与Node* 一样高效,至少在发布版本中,但通常在调试版本中具有一些集成检查,这可能会有所帮助。

【讨论】:

    【解决方案3】:

    当您使用std::vector&lt;Node&gt; 作为方法返回时,您会复制所有数据,这需要时间。 使用std::vector&lt;Node*&gt; 允许您只拥有数据地址并且不进行重复。但是,如果您使用此选项,则必须小心修改数据,因为修改是在您的 masterNodes 中完成的。

    【讨论】:

      【解决方案4】:

      你应该尝试std::copy_if算法,根据参考:

      在实践中,std::copy 的实现避免了多次赋值,如果值类型为 TriviallyCopyable,则使用诸如 std::memmove 之类的批量复制函数。

      您可以使您的 Node 实现符合要考虑的要求 TriviallyCopyable(使用 std::array,而不是 std::vector 进行连接),因此使用 std::copy_if 应该非常快.

      另一方面,复制节点受到内存的限制,如果你没有足够的内存,你可能会得到一个内存不足的错误,如果你确定你永远不会返回超过 100 个节点,好吧,你有这个控制

      如果您使用指针,应用程序将不得不管理内存,这会减少使用的内存量,但会增加内存管理所需的时间。

      但是你会得到的最好的答案是你测试这两个选项。

      【讨论】:

        猜你喜欢
        • 2010-10-13
        • 2011-03-12
        • 1970-01-01
        • 2015-12-06
        • 2021-10-14
        • 2022-01-12
        • 2018-12-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多