将对象传递给compare函数会使排序变慢

Passing object to compare function makes sorting slow?

本文关键字:排序 函数 compare 对象      更新时间:2023-10-16

我的程序中有以下代码。

//Compare class
class SortByFutureVolume
{
public:
        SortByFutureVolume(const Graph& _g): g(_g){}
        bool operator() (const Index& lhs, const Index& rhs){
            return g.getNode(lhs).futureVolume() > g.getNode(rhs).futureVolume();
        }
    private:
        Graph g;
};

然后我用它进行排序,如下所示:

    std::sort(nodes.begin(), nodes.end(),SortByFutureVolume(g));

当我在mac电脑上对大小为23K的矢量运行上述代码时,它只需几秒钟就完成了。然而,当我在我的ubuntu 14机器上运行时。这需要几分钟的时间,甚至还没有完成。

我搜索了这个问题,在这里找到了以下解决方案。我可以防止std::sort复制传递的比较对象吗

基本上修改我的代码,这样可以修复问题:

SortByFutureVolume s(g);
std::sort(_nodes.begin(), _nodes.begin()+ end, std::ref(s));

在这之后,我的mac和ubuntu上的运行时间是相当的。速度快多了。

我知道这是有效的,但我想明白为什么?我知道上面的代码很慢是因为复制了图和SortByFutureVolume。为什么需要std::ref()?这个解决方案是正确的吗?有更好的方法吗?

如果g可以是只读的,则应该有Graph &const Graph &,而不是在SortByFutureVolume中有Graph数据成员。这样,无论何时复制SortByFutureVolume,都不会复制Graph

class SortByFutureVolume
{
public:
        SortByFutureVolume(const Graph& _g): g(_g){}
        bool operator() (const Index& lhs, const Index& rhs){
            return g.getNode(lhs).futureVolume() > g.getNode(rhs).futureVolume();
        }
    private:
        Graph& g;
        // or
        const Graph& g;
};

正如Benjamin Lindley在评论中指出的那样,如果将SortByFutureVolume更改为存储指向Graph的指针而不是引用,则SortByFutureVolume将变为可复制分配,因为可以分配指针,但不能分配引用。那会给你

class SortByFutureVolume
{
public:
        SortByFutureVolume(const Graph& _g): g(&_g){}
        bool operator() (const Index& lhs, const Index& rhs){
            return g->getNode(lhs).futureVolume() > g->getNode(rhs).futureVolume();
        }
    private:
        const Graph * g;
};

另一方面,在函数参数中使用_g作为变量名是可以的,因为它不以大写字母开头,但不使用前导下划线是一个好习惯。在全局空间中,_g将是一个无效的标识符,因为它是为实现保留的。

std::ref是一个伪装的指针。代码所做的是,它不是复制一个重SortByFutureVolume对象,而是围绕指向同一对象的指针进行复制——这显然要快得多。

选项是使Graph g成为分类器对象内部的(常量)引用。

每次复制SortByFutureVolume时,它都会复制整个图,std::sort会根据比较函数对象的值进行大量复制。

请参阅此处:

http://coliru.stacked-crooked.com/a/49b9cdad8eb3bc06

对于简单的CCD_ 17排序,它在内部对CCD_。您的图形可能被复制了相同的次数。

std::ref只复制对比较函数对象的引用——这会删除所有深层复制,从而加快整个过程。

您正在调用的std::sort变体的原型是

template< class RandomIt, class Compare >
void sort( RandomIt first, RandomIt last, Compare comp );

(参见http://en.cppreference.com/w/cpp/algorithm/sort)

因此,编译器推断,当您传递一个不合格的SortByFutureVolume(g)时,您传递的是值。用SortByFutureVolume的定义构造临时需要图形的深度副本。然后可能会有第二个副本,因为临时值是通过值传递的。如果此参数是按排序中的值传递的,则将进行进一步的复制。

当您使用std::ref()时,编译器可以推断出第三个参数是引用,因此该过程变为通过引用,从而消除了图的二次副本。

正如其他人所指出的,解决方案是将成员g作为引用,并通过引用使构造函数接受。

class SortByFutureVolume {
    const Graph& g;
public:
    SortByFutureVolume(const Graph& g_) : g(g_) {}
    bool operator() (const Index& lhs, const Index& rhs){
        return g.getNode(lhs).futureVolume() > g.getNode(rhs).futureVolume();
    }
};

当然,如果您有一个兼容C++11的编译器,您可以只使用lambda:

std::sort(nodes.begin(), nodes.end(), [&g](const Index& lhs, const Index& rhs) {
    return g.getNode(lhs).futureVolume() > g.getNode(rhs).futureVolume();
});