将(很多!)数字转换为字符串的正确方法,无需在 Qt 中分配

Posted

技术标签:

【中文标题】将(很多!)数字转换为字符串的正确方法,无需在 Qt 中分配【英文标题】:Proper way to convert (many!) numbers to strings without allocations in Qt 【发布时间】:2014-05-03 11:56:12 【问题描述】:

tl;博士

我想每秒多次调用QString::number(int)。它很慢:似乎每次都分配一个新字符串。尝试在同一字符串上使用setNum,仍然没有乐趣。


原始的长问题:

问题

我有一大堆数字(比如整数),我想将它们格式化为文本,然后(可能不会立即)写入文件。天真的方式看起来大约1如下:

QString allData;
foreach(const int & value, values) 
    allData += QString::number(value);
    allData += '\n';

我的机器上 150000 个整数大约需要 280ms,这对我来说似乎很多。我想这是因为 QString::number 被调用了 150000 次,并且每次都分配新的字符串。当我尝试改用itoa(不分配内存)时,这被证实是问题的根源。

可能,但不是 Qt [not-cute] 解决方案

QString allData;
char buffer[100];                               // <-------
foreach(const int & value, values) 
    _itoa_s(value, buffer, sizeof(buffer), 10); // <-------
    allData += buffer;
    allData += '\n';

对于相同的 150000 个整数,这大约需要 70ms(大约快 4x),这对我来说现在是可以接受的(我我想我也可以用字符串连接做一些事情,但让我们把这个问题放在这个问题之外)

但是我不喜欢我必须使用一些不标准的、可能已弃用、可能不可移植的2 函数(并不是说这看起来很丑)。

然后我想起还有一个instance方法:QString::setNum。我希望我可以使用与itoa 相同的模式:只分配一个字符串并每次修改它。

理想,但不起作用解决方案

QString allData;
QString number;                       // <-------
foreach(const int & value, values) 
    number.setNum(value);             // <-------
    allData += number;
    allData += '\n';

不幸的是,这与QString::number 并没有太大区别:又是大约 280 毫秒,好吧,也许 250 毫秒,但仍然太多了。

那么,恭喜你到达这里 :) 最后......

问题

    什么 Qt 专家会建议我做什么?闭嘴并使用itoa,尽管在其他芬芳的 C++/Qt 代码中有明显的 C 气味? 或者我可以说“来吧,Qstring,把这个数字吃到你身上”? 我想知道为什么setNum 没有成功

脚注:

1 在实际代码中,我不仅有 150000 个整数,还有 50000 个整数的三元组,我还在它们之间添加了 '\t'。这是与我的实际代码的唯一区别,我想这并不重要:这里我只对QString::numberitoa 的性能感兴趣。

2 实际上,我很惊讶 MinGW 也有 _itoa_s,其行为就像 Visual Studio 的一样,但我仍然有一些尴尬的感觉,在我优美的 Qt 代码中使用这样一个肮脏的函数会减少它的便携性。如果我错了,请纠正我。

【问题讨论】:

我投票给 2)。如果您使用 Qt 和 QString,我不会抱怨性能,因为它本质上是一个比性能优化的框架慢的框架,至少在大多数情况下是这样。我永远不会在性能关键系统中使用 Qt。至于setNum,你试过reserve吗? @LaszloPapp,正如我所希望的那样,这里实际上似乎不是 Qt 很慢,但我为此任务使用了错误的类:应该使用 QByteArray 而不是 QString(感谢 @SalvatoreAvanzo )。至于您的建议:我尝试使用reserve,但没有任何影响。似乎QStringsetNum 的实现只比QByteArray 的慢。可能是因为 QByteArray 只是对 char* 的某种包装,而 QString 则要复杂得多(编码等)。 好吧,你问的是 QString,所以像我这样的人假设你插入了其他非数字,在这种情况下 QByteArray 是没有实际意义的。事情仍然存在,我真的不会亲自将 Qt 用于性能关键的事情,尤其是在毫秒之后。这对我来说只是个坏主意。 在添加所有这些数字之前,您是否完成了QString::reserve()?您知道每个数字至少需要两个字符,最多 10 个(?),并且您可能有一个合理的估计,您平均有多少位数。 @MSalters 是的,当然 :) 但我坚持:问题不在于最初分配的字节数不足。我在@SalvatoreAvanzo 的回答中添加了一些注释来解释QString::setNum 的问题。 【参考方案1】:

您可以尝试使用共享相同 QString 接口但更适合性能问题的 QByteArray。我使用此代码获得 36 毫秒(qt 5.2 clang)与您原来的 57 毫秒(在我的机器上):

QByteArray allDatab;
foreach(const int & value, values) 
    allDatab += QByteArray::number(value);
    allDatab += '\n';

QString result(allDatab);

这个版本需要 29 毫秒(这可能会证实您对 setNum 的假设):

QByteArray allDatad;
QByteArray number;                       
foreach(const int & value, values) 
    number.setNum(value);             
    allDatad += number;
    allDatad += '\n';

【讨论】:

哇,除了QByteArray 爆发了这么多! O_o 在我的机器上,第二个变体的时间为 57 毫秒,因此它比 QString+itoa 更快。太好了,解决了我的问题。 请记住,如果您必须在 QString'ish 之前和/或之后插入其他东西,这将不会很好。希望这不是您的用例。 @LaszloPapp ,重要的评论,谢谢。在我的情况下,只要我完成添加数字就将 QByteArray 转换为 QString 就足够了,然后才添加标题等:此字符串的所有后续操作都很小,因此它们对性能没有影响。 好吧,一个完整的答案可以解释为什么它很慢,那就是 QString 不仅仅是一个字节数组。它处理 utf、语言环境等等。 @LaszloPapp:Qt 是一个有据可查的框架。 There 你可以比我更好地找到 QString 和 QByteArray 之间差异的解释。但是,如果您能编辑我的帖子以为此解决方案添加一些见解,我会很高兴。【参考方案2】:

使用 STL 怎么样?

我测试了你的代码(修改循环以简化)

int main() 
  stringstream ss;
  for(int i=0; i<2000000; ++i) 
     ss << i << "\n";
  

我得到

time ./ss_test
  real  0m0.146s
  user  0m0.139s
  sys   0m0.006s

用Qt版本(在我的机器上)

int main() 
  QString allData;
  for(int i=0; i<2000000; ++i) 
    allData += QString::number(i);
    allData += '\n';
  

我得到

time ./qtstring_test 
   real 0m0.516s
   user 0m0.508s
   sys  0m0.008s

【讨论】:

谢谢你的想法!虽然@SalvatoreAvanzo 的 QByteArray 解决方案在我的情况下具有更好的整体性能(可能是因为它不需要跨框架转换),但我可以在其他地方使用仍然是个好主意。

以上是关于将(很多!)数字转换为字符串的正确方法,无需在 Qt 中分配的主要内容,如果未能解决你的问题,请参考以下文章

Django - 将列表转换为 Q 对象的组合 OR 的正确方法 [重复]

在 JavaScript 中将数字转换为字符串的最佳方法是啥?

错误的输出 - 将 Java 字符串转换为数组。插入数组之前数字是不是正确? [复制]

VBA 如何将数字转换为中文大写

C语言,ASCII码怎么转换为字符?

Java入门视频教程!java将字符转换为数字