这个std :: vector分区的性能很慢

时间:2015-12-09 20:29:45

标签: c++

我已经实现了一个有效的partition函数,它的行为类似于Clojure中同名的有用工具。但是,我的很慢。虽然Clojure函数可以在几毫秒内完全分割一百万个元素,但即使使用50K元素也需要几秒钟。 (注意:我没有比较我的Clojure中可用的惰性函数调用,如评论中所述;我在谈论完全实现懒惰不适用)。

它和它的助手是:

template <typename T>
std::vector<T> take(int size, const std::vector<T>& coll){
    if (size<0) return std::vector<T>();
    auto sized = size > coll.size() ? coll.size() : size;
    typename std::vector<T>::const_iterator first = coll.begin();
    typename std::vector<T>::const_iterator last = first + sized;
    return std::vector<T>(first,last);
}

template <typename T>
std::vector<T> drop(int size, const std::vector<T>& coll){
    if (size<0) return std::vector<T>();
    auto sized = size > coll.size() ? coll.size() : size;
    typename std::vector<T>::const_iterator first = coll.begin()+sized;
    typename std::vector<T>::const_iterator last = coll.end();
    return std::vector<T>(first,last);
}

template <typename T>
std::vector<std::vector<T>> partition(int size, int step, const std::vector<T>& coll, bool showPartialEnd=false){
    std::vector<std::vector<T>> ret;
    ret.reserve(coll.size());
    if (size<1||step<1) return ret;
    std::vector<T> temp;
    std::vector<T> remain=coll;
    auto building=true;
        do {
            temp=std::move(take(size, remain));
            building=(showPartialEnd?(temp.size()>0):(temp.size()==size));
            if (building==true){
                ret.push_back(temp);
                remain=std::move(drop(step, remain));
            }
        } while (building==true);
    return ret;
}

我喜欢关于我可以在哪里优化它的一些指示,所以它表现得更快。我尝试保留大小,因此push_back不必每次都分配,但这没有区别(实际上我已经为大多数用例分配了大小)。我也认为showPartialEnd?条件可能是一个障碍,因为它并不是真的需要在每个循环中发生,而是分裂成两个循环而只需要一次迭代条件来选择循环没有区别。

以下是一个使用示例:

void examples(){
    auto x=range(30);
    auto y=partition(3, 2, x);
    std::for_each(y.begin(), y.end(), printVector<int>);
}

输出:

0 1 2 
2 3 4 
4 5 6 
6 7 8 
8 9 10 
10 11 12 
12 13 14 
14 15 16 
16 17 18 
18 19 20 
20 21 22 
22 23 24 
24 25 26 
26 27 28 

1 个答案:

答案 0 :(得分:1)

性能问题的原因是您的drop函数将算法转换为O(n^2)而非线性时间,因为它必须不断创建包含所有元素的新remain容器那个天堂还没有被分割出来。

我仍然试图了解代码的目的,以便提供有关如何修复/改进版本的建议。

完全未经测试(现在时间紧张),但我认为这应该是接近的:

std::vector<std::vector<T>> partition(int size, int step, const std::vector<T>& coll, bool showPartialEnd=false)
{
    std::vector<std::vector<T>> ret;

    if (size<1||step<1) return ret;

    ret.reserve(coll.size() / step + 1);

    std::vector<T>::const_iterator iter = coll.begin();
    for(; ; iter += step)
    {
        // If you have enough elements left for an entire chunk, push it.
        if((coll.end() - iter) >= size)
        {
            ret.push_back(std::vector<T>(iter, iter + size));
        }
        else if(showPartialEnd)
        {
            ret.push_back(std::vector<T>(iter, coll.end()));
            break;   // Remove if you want *all* partial ends instead of just the first one.
        }
        else break;
    }

    return ret;
}