我目前正在向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();
(这不会回答探查器问题。它试图回答原始问题,这是性能问题。)
我建议在这种情况下完全避免使用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 << "\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();
鉴于您正在编写数十亿条记录,您可以考虑使用boost karma
库:
http://www.boost.org/doc/libs/1_54_0/libs/spirit/doc/html/spirit/karma.html
根据他们的基准测试,它运行速度比C ++流快得多,甚至sprintf也适用于大多数编译器/库,包括Visual C ++ 2010:
这将需要一些学习,但你将获得显着的加速奖励。
使用多个核心(如果可用)!在我看来,您的数据的每个点都独立于其他点。因此,您可以使用QtConcurrent :: mappedReduced拆分预处理。例如。:
使用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("\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);
}
如果使用文本输出不是必需的,您可能希望将二进制输出与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();
}