5

我目前正在将数十亿条二进制记录写入 ASCII 文件(呃)。我的工作正常,但如果可以的话,我想优化性能。问题是,允许用户选择要输出的任意数量的字段,所以我无法在编译时知道它们将包含 3-12 个字段中的哪一个。

有没有更快的方法来构造 ASCII 文本行?如您所见,字段的类型变化很大,我想不出绕过一系列 if() 语句的方法。输出的 ASCII 文件每条记录一行,所以我尝试使用用 arg 构造的模板QString,但这只会减慢大约 15% 的速度。

更快的解决方案不必使用 QTextStream,也不必直接写入文件,但输出太大而无法将整个内容写入内存。

这是一些示例代码:

QFile outfile(outpath);
if(!outfile.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
{
    qWarning("Could not open ASCII for writing!");
    return false;
} else
{
    /* compute XYZ precision */
    int prec[3] = {0, 0, 0}; //these non-zero values are determined programmatically

    /* set up the writer */
    QTextStream out(&outfile);
    out.setRealNumberNotation(QTextStream::FixedNotation);
    out.setRealNumberPrecision(3);
    QString del(config.delimiter); //the user chooses the delimiter character (comma, tab, etc) - using QChar is slower since it has to be promoted to QString anyway

    /* write the header line */
    out << "X" << del << "Y" << del << "Z";
    if(config.fields & INTFIELD)
        out << del << "IntegerField";
    if(config.fields & DBLFIELD)
        out << del << "DoubleField";
    if(config.fields & INTFIELD2)
        out << del << "IntegerField2";
    if(config.fields & TRIPLEFIELD)
        out << del << "Tri1" << del << "Tri2" << del << "Tri3";
    out << "\n";

    /* write out the points */
    for(quint64 ptnum = 0; ptnum < numpoints; ++ptnum)
    {
        pt = points.at(ptnum);
        out.setRealNumberPrecision(prec[0]);
        out << pt->getXYZ(0);
        out.setRealNumberPrecision(prec[1]);
        out << del << pt->getXYZ(1);
        out.setRealNumberPrecision(prec[2]);
        out << del << pt->getXYZ(2);
        out.setRealNumberPrecision(3);
        if(config.fields & INTFIELD)
            out << del << pt->getIntValue();
        if(config.fields & DBLFIELD)
            out << del << pt->getDoubleValue();
        if(config.fields & INTFIELD2)
            out << del << pt->getIntValue2();
        if(config.fields & TRIPLEFIELD)
        {
            out << del << pt->getTriple(0);
            out << del << pt->getTriple(1);
            out << del << pt->getTriple(2);
        }
        out << "\n";
    } //end for every point
outfile.close();
4

6 回答 6

4

(这不回答探查器问题。它试图回答原始问题,即性能问题。)

我建议在这种情况下完全避免使用 QTextStream 看看是否有帮助。它可能有助于提高性能的原因是涉及开销,因为文本在内部被编码为 UTF-16进行存储,然后在写出时再次解码为 ASCII 或 UTF-8。您有两个不需要的转换。

请尝试仅使用标准 C++std::ostringstream类。它与 QTextStream 非常相似,您的代码只需要进行微小的更改。例如:

#include <sstream>

// ...

QFile outfile(outpath);
if (!outfile.open(QIODevice::WriteOnly | QIODevice::Text
                | QIODevice::Truncate))
{
    qWarning("Could not open ASCII for writing!");
    return false;
}

/* compute XYZ precision */
int prec[3] = {0, 0, 0};

std::ostringstream out;
out.precision(3);
std::fixed(out);
// I assume config.delimiter is a QChar.
char del = config.delimiter.toLatin1();

/* write the header line */
out << "X" << del << "Y" << del << "Z";
if(config.fields & INTFIELD)
    out << del << "IntegerField";
if(config.fields & DBLFIELD)
    out << del << "DoubleField";
if(config.fields & INTFIELD2)
    out << del << "IntegerField2";

if(config.fields & TRIPLEFIELD)
    out << del << "Tri1" << del << "Tri2" << del << "Tri3";
out << "\n";

/* write out the points */
for(quint64 ptnum = 0; ptnum < numpoints; ++ptnum)
{
    pt = points.at(ptnum);
    out.precision(prec[0]);
    out << pt->getXYZ(0);
    out.precision(prec[1]);
    out << del << pt->getXYZ(1);
    out.precision(prec[2]);
    out << del << pt->getXYZ(2);
    out.precision(3);
    if(config.fields & INTFIELD)
        out << del << pt->getIntValue();
    if(config.fields & DBLFIELD)
        out << del << pt->getDoubleValue();
    if(config.fields & INTFIELD2)
        out << del << pt->getIntValue2();
    if(config.fields & TRIPLEFIELD)
    {
        out << del << pt->getTriple(0);
        out << del << pt->getTriple(1);
        out << del << pt->getTriple(2);
    }
    out << "\n";

    // Write out the data and empty the stream.
    outfile.write(out.str().data(), out.str().length());
    out.str("");
}
outfile.close();
于 2013-07-23T16:31:14.743 回答
1

鉴于您正在写出数十亿条记录,您可能会考虑使用 boostkarma库:

http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/karma.html

根据他们的基准测试,它在大多数编译器/库(包括 Visual C++ 2010)上的运行速度比 C++ 流甚至 sprintf 快得多:

http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/karma/performance_measurements/numeric_performance/format_performance.html

这将需要一些学习,但您将获得显着加速的回报。

于 2013-07-28T01:50:34.760 回答
1

使用多个核心(如果可用)!在我看来,您数据的每个点都独立于其他点。因此,您可以使用 QtConcurrent::mappedReduced 拆分预处理。例如:

  1. 将您的数据分成一系列由 N(例如 1000)个点组成的块,
  2. 然后让您的mapFunction将每个块处理到内存缓冲区中
  3. reduceFunction将缓冲区写入文件。

使用 OrderedReduce | SequentialReduce 作为选项。

这可以与其他优化一起使用!

于 2016-04-08T15:24:42.793 回答
0

如果您没有合适的分析器,但有一个调试器可以让您中断正在运行的应用程序,则可以选择手动分析: - 在调试器中启动应用程序,调用慢代码部分 - 在执行慢代码时随机中断执行部分 - 查看调用堆栈并注意哪个子程序处于活动状态 - 重复几次(大约 10 次左右)

现在,您在大多数情况下找到相同程序的可能性很高 - 这是您必须避免/加快速度以改进事情的程序

于 2013-07-28T15:00:26.220 回答
0

在这里,我使用标准 C 库重写了您的一段代码——也许这样更快。我没有测试,所以您可能需要阅读一些 fprintf 格式规范文档 - 根据您的编译器格式标志可能会有所不同。

注意 getTriple() 函数的返回类型 - 如果它不是浮点数,则必须更改前面格式规范中的 %f。

#include <stdio.h>

FILE* out;

out = fopen(outpath, "w");
if (out == NULL)
{
    qWarning("Could not open ASCII for writing!");
    return false;
} else {
    /* compute XYZ precision */
    int prec[3] = {0, 0, 0}; //these non-zero values are determined programmatically

    /* set up the writer */
    char del = config.delimiter;

    char s[255];        // or more if needed..
    /* write the header line */
    sprintf(s, "X%cY%cZ%c", del, del, del);
    fputs(s, out);
    if(config.fields & INTFIELD)
        fputs("IntegerField", out);
    if(config.fields & DBLFIELD)
        fputs("DoubleField", out);
    if(config.fields & INTFIELD2)
        fputs("IntegerField2", out);
    if(config.fields & TRIPLEFIELD) {
        sprintf(s, "%cTri1%cTri2%cTri3", del, del, del);
        fputs(s, out);
    }
    fputs("\n", out);

    /* write out the points */
    for(quint64 ptnum = 0; ptnum < numpoints; ++ptnum)
    {
        pt = points.at(ptnum);
        sprintf(s, "%.*f%c%.*f%c%.*f%c", prec[0], pt->getXYZ(0), del, prec[1], pt->getXYZ(1), del, prec[2], pt->getXYZ(2), del);
        fputs(s, out);            
        if(config.fields & INTFIELD)
            sprintf(s, "%d", pt->getIntValue());
        if(config.fields & DBLFIELD)
            sprintf(s, "%f", pt->getDoubleValue());
        if(config.fields & INTFIELD2)
            sprintf(s, "%d", pt->getIntValue2());
        fputs(s, out);
        if(config.fields & TRIPLEFIELD)
        {
            sprintf(s, "%c%f%c%f%c%f", del, pt->getTriple(0), del, pt->getTriple(1), del, pt->getTriple(2));    // assuming the getTriples() return double - need to adjust the %f to the real type
            fputs(s, out);
        }
        fputs("\n", out);
    } //end for every point
    fclose(out);
}
于 2013-07-28T15:31:05.180 回答
0

If using text output is not mandatory, you might want to use binary output with QDataStream. As there is no formatting to perform, the time to write or read your file will be strongly reduced.

void saveData(const QString & filename, const QVector<double> & iVect){
   QFile file(filename);
   if( !file.open(QIODevice::WriteOnly) )
      return;
   QDataStream out(file);
   for(int i=0;i<iVect.count();i++){
      out << iVect[i];
   file.close();
}
于 2019-03-07T18:22:06.867 回答