【问题标题】:Fastest way to write vector <pair <double,double> to file将向量 <pair <double,double> 写入文件的最快方法
【发布时间】:2013-07-17 08:56:47
【问题描述】:

我正在尝试将大量 x/y 双点写入文件。

我想出了以下函数,这是迄今为止最快的解决方案。

还有其他方法可以加快这个过程吗?

先写入字符串流,然后再打开文件,可以大大提高速度。

bool printPoints(const vector <pair <double,double> > &points, const string &file)
{
    if(points.empty())
        return false;

    vector <pair <double,double> > const_iterator i;

    if(file != "")
    {
        stringstream ss;
        for(i=points.begin(); i != points.end();++i )
        {
           ss << i->first << " " << i->second << "\n";
        }

        ofstream out(file.c_str());
        if(out.fail())
        {
            out.close();
            return false;
        }
        out << ss.str();
        out.close();
    }
    return true;
}

【问题讨论】:

  • 直接尝试流式传输 stringstream 的缓冲区,看看是否有所不同:out &lt;&lt; ss.rdbuf();.
  • 另外,尝试使用 C I/O (FILE*)。但我认为不可能有任何重大改进。
  • 我试过rdbuf()而不是.str(),但没有发现任何区别
  • 你实际上有多少个坐标?
  • 我有 2-10 千,但以 100k 为基准

标签: c++ file vector std-pair


【解决方案1】:

我对此进行了测试。写信给stringstream 几乎不会给你带来任何好处。使用FILE * 而不是fstream 确实会带来合理的改进。

这是我的测试代码:

#include <vector>
#include <utility>
#include <fstream>
#include <iostream>
#include <sstream>
#include <cstdio>

using namespace std;

bool printPoints(const vector <pair <double,double> > &points, const string &file)
{
    if(points.empty())
        return false;

    vector <pair <double,double> >::const_iterator i;

    if(file != "")
    {
        stringstream ss;
        for(i=points.begin(); i != points.end();++i )
        {
           ss << i->first << " " << i->second << "\n";
        }

        ofstream out(file.c_str());
        if(out.fail())
        {
            out.close();
            return false;
        }
        out << ss.str();
        out.close();
    }
    return true;
}

bool printPoints2(const vector <pair <double,double> > &points, const string &file)
{
    if(points.empty())
        return false;

    vector <pair <double,double> >:: const_iterator i;

    if(file != "")
    {
        ofstream out(file.c_str());
        if(out.fail())
        {
            out.close();
            return false;
        }
        for(i=points.begin(); i != points.end();++i )
        {
           out << i->first << " " << i->second << "\n";
        }

        out.close();
    }
    return true;
}


bool printPoints3(const vector <pair <double,double> > &points, const string &file)
{
    if(points.empty())
        return false;

    vector <pair <double,double> >:: const_iterator i;

    if(file != "")
    {
    FILE *out = fopen(file.c_str(), "w");
        if(!out)
        {
            return false;
        }
        for(i=points.begin(); i != points.end();++i )
        {
        fprintf(out, "%f %f", i->first, i->second);
        }

        fclose(out);
    }
    return true;
}

static __inline__ unsigned long long rdtsc(void)
{
    unsigned hi, lo;
    __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi));
    return ( (unsigned long long)lo)|( ((unsigned long long)hi)<<32 );
}

int main()
{
    vector <pair <double,double> >  v;
    unsigned long long t1, t2;

    for(int i = 1; i <= 10000000; i++)
    {
    v.push_back(make_pair<double, double>((double)i, 1.0/i)); 
    }
    t1 = rdtsc();
    printPoints(v, "points.txt");
    t2 = rdtsc();
    cout << "time = " << t2 - t1 << endl;
    t1 = rdtsc();
    printPoints2(v, "points2.txt");
    t2 = rdtsc();
    cout << "time = " << t2 - t1 << endl;
    t1 = rdtsc();
    printPoints3(v, "points3.txt");
    t2 = rdtsc();
    cout << "time = " << t2 - t1 << endl;
}   

Results:
time = 55363637480
time = 54413392112
time = 33069402767

显然,结果可能会因处理器类型、内存类型、硬盘系统(或网络驱动器存储)等而有所不同。但我过去对此进行了测试,发现了类似的结果。

【讨论】:

  • 解决方案 3 最适合我。在我的测试中,本机解决方案大约需要 46 秒,我的 22 秒和你的第三个大约 19 秒。谢谢。
【解决方案2】:

坐标的序列化可以通过将输入划分到多个线程然后连接它们的返回值来并行化。然后将返回值写入文件。这样我们可以加快进程。

【讨论】:

    【解决方案3】:

    您可以通过一次以二进制格式写入所有坐标来提高速度

    ofstream out(file.c_str(),std::ios_base::binary);
    out.write(reinterpret_cast<const char*>(points.begin()),sizeof(double)*2*points.size());
    

    如果点不是连续存储在内存中的pair(对于向量来说),它可能不起作用,那么你可以将它复制到向量中的double first (x,y,x,y...)的单个向量双倍,接下来写入磁盘。

    【讨论】:

    • 这显然是假设后面使用的格式是“灵活的”。
    • 我不希望它们是二进制的。有什么方法可以使用您的解决方案并且仍然具有可读性?
    • 最接近的方法是使用 atoi 将双精度转换为 char,将它们附加到字符串,然后二进制写入字符串到文件 - 这将使用相同的方法避免流,但是,我不确定有多少收益你会得到
    【解决方案4】:

    你会考虑内存映射文件吗?只需将必要的数据(当然是序列化表示)复制到内存文件映射返回的内存区域,然后关闭映射。根据我过去的经验,这是将大量数据从 STL 结构传输到文件的非常快速的方法。

    【讨论】:

    • 便携性怎么样?内存对齐,字节序等? C++ 中的序列化不像 Java 中那么容易。
    • 你能告诉我更多吗?还没用过内存映射。
    • MMF 允许您分配带有特殊操作系统标记的内存块。如果您将文件与该内存关联,操作系统会将它们同步在一起,如果没有文件关联,则内存块将与系统交换关联msdn.microsoft.com/en-us/library/ms810613.aspx
    【解决方案5】:
    typedef pair<double, double> PDD;
    
    namespace std {
    
      inline
      ostream&
      operator<<(ostream& os, const PDD& p)
      {
        return os << p.first << ' ' << p.second;
      }
    
    }
    
    bool
    PrintPoints(const vector<PDD>& points, const string& file)
    {
      if (points.empty() || file.empty())
        return false;
    
      ofstream fout(file.c_str());
      if (!fout)
        return false;
    
      copy(points.begin(), points.end(), ostream_iterator<PDD>(fout, "\n"));
    
      return true;
    }
    

    ofstreamdtor 将调用 Close。

    【讨论】:

    • 可能看起来不错,但不会提高速度。
    • 我同意,但我认为这已经很清楚了:您可以得到的唯一一致的速度提升是按照 Mats 的建议通过 C I/O 实现它。此外,这种差异可能仅在快速硬件(如 SSD)上才能观察到,否则可能会因硬件带宽的更大影响而被隐藏。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-08
    • 1970-01-01
    • 1970-01-01
    • 2017-09-05
    相关资源
    最近更新 更多