C++ 排序和连接百万或十亿STL向量的最快方法

C++ 排序和连接百万或十亿STL向量的最快方法,c++,vector,C++,Vector,将百万或十亿STL向量排序并连接到单个STL向量的最佳方法是什么。目前,我做这件事的方式是迭代向量并执行每个操作 这是伪代码 typedef unsigned long long int ULLInt; ULLInt N = 1000000; vector<vector<ULLInt> > vecVec( N, vector<ULLInt>() ); vector<ULLInt> concatVec; // ... // .

将百万或十亿STL向量排序并连接到单个STL向量的最佳方法是什么。目前,我做这件事的方式是迭代向量并执行每个操作

这是伪代码

typedef unsigned long long int ULLInt;

ULLInt N = 1000000;

vector<vector<ULLInt> > vecVec( N, vector<ULLInt>() );
vector<ULLInt>          concatVec;

// ...
// ... fill vectors inside vecVec here 
// ..  we also get here the total number of values inserted in all vectors (count)
// ...

// reserve the space
concatVec.reserve( count);

// sort each vector and concatenate them in sequence
for( ULLInt i=0; i<N; i++)
  sort( vecVec[i].begin(), vecVec[i].end() );
  concatVec.insert( concatVec.end(), vecVec[i].begin(), vecVec[i].end() );
end for
typedef无符号长整型ULLInt;
ULLInt N=1000000;
向量vec(N,向量());
向量concatVec;
// ...
// ... 在这里填充向量
// ..  这里我们还可以得到插入所有向量中的值的总数(计数)
// ...
//预留空间
浓缩储备(计数);
//对每个向量进行排序并按顺序连接它们

对于(ULLInt i=0;i每次代码插入其中一个向量的内容时,它必须确保目标向量有足够的内存来保存结果。这意味着它将经常为目标向量重新分配内存。这意味着复制其内容,代码最终会做很多次。它将很多次g> 更快地将目标向量的内存预分配到最终的完整大小。请阅读关于
vector::reserve()

如何:

  • 将矢量分割为核心桩。计算每个桩所需的尺寸
  • 在向量中为所有数据保留空间
  • 将此向量拆分为多个部分
  • 将零件和桩送入螺纹以合并
一些快速代码(可能无法编译,但您可能会明白这一点):

typedef向量多个向量;
无效合并(多个向量向量中的向量){
const int cores=16;
std::阵列桩=分割向量(向量的向量,芯);
标准::阵列尺寸=计算尺寸(桩、芯);
std::向量结果;
结果.储备(大小之和);
int=0;
int核=0;
用于(多个构件和桩:桩){
std::thread(merge_向量、pile、result.begin()+已使用);
使用+=尺寸[芯];
核心+=1;
}
}

如果vecVec中的向量是按升序填充的(正如我从聊天中了解到的-这是您的用例),那么您可以尝试使用一个向量而不是许多小向量,在单独的索引数组中维护每个向量的开始索引。 这将通过在适当的位置“构造”子向量来避免昂贵的浓缩

#include <vector>
#include <algorithm>
#include <cstdlib>
#include <iterator>

int main(int argc,char *argv[])
{
    using namespace std;
    typedef int Payload;
    vector<Payload> vec;
    vector<size_t> indices;
    for(unsigned i=0;i!=100;++i)
    {
        indices.push_back(vec.size()); // begin index of current vector
        // filling current sub-vector
        generate_n(back_inserter(vec),777+i,rand);
    }
    indices.push_back(vec.size()); // end of last vector, or begin of
                                   // one-past last vector

    // sorting each sub vector
    for(size_t i=0;i+1!=indices.size();++i)
    {
        // can be done in parallel. be aware of possible false sharing
        sort(vec.begin()+indices[i],vec.begin()+indices[i+1]);
    }
    return 0;
}
#包括
#包括
#包括
#包括
int main(int argc,char*argv[])
{
使用名称空间std;
有效载荷类型;
向量向量机;
向量指数;
for(无符号i=0;i!=100;++i)
{
index.push_back(vec.size());//开始当前向量的索引
//填充电流子向量
生成(反向插入器(vec),777+i,随机数);
}
index.push_back(vec.size());//最后一个向量的结尾或
//最后一个向量
//对每个子向量进行排序
对于(size_t i=0;i+1!=index.size();+i)
{
//可以并行完成。请注意可能的错误共享
排序(向量begin()+索引[i],向量begin()+索引[i+1]);
}
返回0;
}

我要做的一件事是询问您是否真的需要包含一百万个std::vectors。如果您将每个向量添加到一个列表中,并创建自己的迭代器来遍历每个向量中的每个元素,会怎么样?对于大多数算法,这与一个大规模向量是无法区分的。而且,根据负载的不同,在cust中完成的额外工作也不同om迭代器的工作量远小于实际连接所有向量所需的所有工作量。

如果这些向量不是空的,则需要大量内存。如果希望对最终结果也进行排序,请检查
std::merge
。@Kerrek SB您可以使用
std::inplace\u merge
。1.从伪Code-mergedVec不需要排序,是它的意图吗?2.你的目的是什么?你需要vecVec中单独的排序向量,还是只需要排序的mergedVec?如果不需要对mergedVec进行排序,那么你就不需要nor std::merge或std::inplace\U merge。对不起,“merge”在这里很混乱。使用“串联”可能更好?忘了提到多次重新分配可能会导致堆碎片化,导致一次较大的重新分配失败,即使在开始时分配全部数量会成功。对不起,实际上我已经在这样做了。我忘了在伪代码中提到。我只更正代码。你能解释一下核心是什么意思吗,堆和部分。这很混乱。@nurav检查编辑,只要问我的代码是否完全不可读。实际上,我需要最终将连接的数据复制到GPU上。因为GPU的任何指针跟踪数据都不合适。我使用数据的平面数组,在GPU上使用单独的索引数组来计算SPMV(使用OpenCL)。我使用CSR(“压缩稀疏行”)稀疏矩阵的格式。它工作得很好。我的想法是一样的。我实际上必须读取稀疏图形数据(相当于平方稀疏矩阵)从一个文件开始,由于一些大小甚至在运行时都不可用,我无法预先分配内存。因此我不得不求助于动态结构。向量在局部性方面优于列表,但在大小调整和数据复制方面存在开销。实际上,我认为生成排队向量优于向量向量向量,因为排队组合在一起链表和向量的属性。我正在评估哪种方法是好的。首先,考虑使用Booo::容器:DEQI-我记得它比MSVC STD::DeQue:第二,事实上,重新排序和重写可能比排序属性快。(想象一下随机访问对deque是如何工作的——至少还有一个额外的间接寻址级别)再分配。第三,您可以尝试保留大部分RAM—GPU在典型内存中的RAM比在主内存中的要少得多。此外,您可以尝试使用启发式方法确定总数据的大小,例如基于输入文件大小。另一个版本-,这里我逐元素推送。\u。结果:。尽管填充std::vector的速度较慢(由于重新配置)比boost::co
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <iterator>

int main(int argc,char *argv[])
{
    using namespace std;
    typedef int Payload;
    vector<Payload> vec;
    vector<size_t> indices;
    for(unsigned i=0;i!=100;++i)
    {
        indices.push_back(vec.size()); // begin index of current vector
        // filling current sub-vector
        generate_n(back_inserter(vec),777+i,rand);
    }
    indices.push_back(vec.size()); // end of last vector, or begin of
                                   // one-past last vector

    // sorting each sub vector
    for(size_t i=0;i+1!=indices.size();++i)
    {
        // can be done in parallel. be aware of possible false sharing
        sort(vec.begin()+indices[i],vec.begin()+indices[i+1]);
    }
    return 0;
}