将对象传递给比较函数会使排序变慢?

时间:2016-03-30 17:59:51

标签: c++ sorting

我的程序中有以下代码。

//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机器上运行时。它需要几分钟而且还没有完成。

我搜索此问题并在此处找到以下解决方案Can I prevent std::sort from copying the passed comparison object

基本上修改我的代码就可以解决问题:

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

在此之后,我的mac和ubuntu上的运行时具有可比性。非常快。

我知道这有效,但我试图理解为什么?我知道上面的慢代码是由于复制了图形和SortByFutureVolume。你为什么需要std :: ref()?这个解决方案是否正确,是否有更好的方法来做到这一点?

4 个答案:

答案 0 :(得分:4)

如果Graph只能是SortByFutureVolume,那么您应该拥有Graph &const Graph &,而不是在g中拥有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; };

SortByFutureVolume

正如Benjamin Lindley在评论中指出的那样,如果您更改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作为变量名称,因为它不以大写字母开头,但不使用前导下划线是一个好习惯。这在全球空间中是真实的,function ContestantInfoDropdown() { var ContestantTest = ['Contestant 1', 'Contestant 2', 'Contestant 3', 'Contestant 4']; var option = []; for (var i=0;i<ContestantTest.length;i++){ option.push({value: ContestantTest[i] }); } option.push({value: 'Contestant 5' }); return option }; var createContestantsDropdownlist = ContestantInfoDropdown(); console.log(createContestantsDropdownlist); 将是一个无效的标识符,因为它是为实现保留的。

答案 1 :(得分:3)

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

该选项可以在分拣机对象中进行Graph g a(const)引用。

答案 2 :(得分:2)

每次复制时,SortByFutureVolume都会复制整个图形,而std :: sort按比较函数对象的值执行大量复制。

见这里:

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

对于简单的std::vector<int>排序,它在内部进行了SortByFutureVolume类的20个实例化。您的图表可能被复制的次数相同。

std::ref仅复制对比较函数对象的引用 - 这会删除所有深层副本,因此也可以加快整个过程。

答案 3 :(得分:2)

您正在调用的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定义构建临时文件需要图表的深层副本。然后可能会有第二个副本,因为临时值是按值传递的。如果此参数在sort中按值传递,则将进一步复制。

当你使用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();
});