如何使用QTextStream优化ASCII输出

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用QTextStream优化ASCII输出相关的知识,希望对你有一定的参考价值。

我目前正在向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 << "
";

    /* 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 << "
";
    } //end for every point
outfile.close();
答案

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

我建议在这种情况下完全避免使用QTextStream,看看是否有帮助。它可能对性能有帮助的原因是涉及到开销,因为文本是encoded internally to 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 << "
";

/* 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 << "
";

    // Write out the data and empty the stream.
    outfile.write(out.str().data(), out.str().length());
    out.str("");
}
outfile.close();
另一答案

鉴于您正在编写数十亿条记录,您可以考虑使用boost karma库:

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

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

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

这将需要一些学习,但你将获得显着的加速奖励。

另一答案

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

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

使用OrderedReduce | SequentialReduce作为选项。

除了其他优化之外,还可以使用它!

另一答案

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

现在你在大多数情况下找到相同程序的概率很高 - 那就是为了改善事物而必须避免/加快的程序

另一答案

在这里,我使用标准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("
", 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("
", out);
    } //end for every point
    fclose(out);
}
另一答案

如果使用文本输出不是必需的,您可能希望将二进制输出与QDataStream一起使用。由于没有要执行的格式化,因此写入或读取文件的时间将大大减少。

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();
}

以上是关于如何使用QTextStream优化ASCII输出的主要内容,如果未能解决你的问题,请参考以下文章

6.4 QTextStream处理文件数据流

QFile / QTextStream 在写入删除文件时不显示错误

如何使用 QTextStream 而不是 QDataStream 从 QTableView 进行加载保存?

使用 QTextStream 读取字符串的第一行

代写DNN程序作业代作DNN的资源分配框架

c中如何输出一个字符的ASCII码