Qt字符串格式化性能比较

Qt字符串格式方法有三种, QString::arg(), QString::sprinft()和QStringList::join().
今天我做了个简单的性能测试, 希望对各位有所帮助.


调用QString::arg()一次:
  1. QString s("1234567890");
  2. // 开始计时
  3. for (int i = 0; i < 10000; ++i) {
  4.     QString str("%1");
  5.     str.arg(s);
  6. }
  7. // 停止计时



调用QString::arg()十次:
  1. QString s("1234567890");
  2. // 开始计时
  3. for (int i = 0; i < 10000; ++i) {
  4.     QString str("%1%2%3%4%5%6%7%8%9%10");
  5.     str
  6.         .arg(s)
  7.         .arg(s)
  8.         .arg(s)
  9.         .arg(s)
  10.         .arg(s)
  11.         .arg(s)
  12.         .arg(s)
  13.         .arg(s)
  14.         .arg(s)
  15.         .arg(s);
  16. }
  17. // 停止计时



调用QString::sprinft()一次:
  1. char s2[] = {"1234567890"};
  2.     // 开始计时
  3.     for (int i = 0; i < times; ++i) {
  4.         QString().sprintf("%d", s2);
  5.     }
  6.     // 停止计时



调用QString::sprinft()十次:
  1. char s2[] = {"1234567890"};
  2.     // 开始计时
  3.     for (int i = 0; i < times; ++i) {
  4.         QString().sprintf("%d%d%d%d%d%d%d%d%d%d", s2, s2, s2, s2, s2, s2, s2, s2, s2, s2);
  5.     }
  6.     // 停止计时



调用QStringList::join()一次:
  1. QString s("1234567890");
  2.     // 开始计时
  3.     for (int i = 0; i < times; ++i) {
  4.         QStringList strList;
  5.         strList.append(s);
  6.         strList.join(QString());
  7.     }
  8.     // 停止计时




调用QStringList::join()十次:
  1. QString s("1234567890");
  2.     // 开始计时
  3.     for (int i = 0; i < times; ++i) {
  4.         QStringList strList;
  5.         strList.append(s);
  6.         strList.append(s);
  7.         strList.append(s);
  8.         strList.append(s);
  9.         strList.append(s);
  10.         strList.append(s);
  11.         strList.append(s);
  12.         strList.append(s);
  13.         strList.append(s);
  14.         strList.append(s);
  15.         strList.join(QString());
  16.     }
  17.     // 停止计时



测试结果:
    运行一次的耗时: QString::arg() 0.412纳秒 < QStringList::join() 0.625纳秒 < QString::sprinft() 1.136纳秒;
    运行十次的耗时: QStringList::join() 2.426纳秒 < QString::arg() 5.175纳秒 < QString::sprinft() 9.232纳秒;


现在让我们来看看这些函数是怎么完成各自的任务的.


    先来看看QString::arg(). 这个函数的实现非常简单,  它先将传入的参数转换成QString格式的字符串参数, 然后调用内部函数replaceArgEscapes来替换源字符串里的替换符(比如%1, %2, %3什么的). replaceArgEscapes首先计算一下新的字符串将会有多长, 然后预先创建一个足够长的临时字符串, 再将源字符串和字符串参数合并到临时字符串中. 一次操作就这么完成了.
    在这个过程中, 比较消耗时间的是创建了一个临时字符串. 随着累加调用QString::arg(), 当调用十次的时候就需要花费5.175纳秒, 其中至少有1.500纳秒是消耗在创建10个临时字符串当中. 还有重复多次的内存拷贝. 所以QString::arg()累加调用的越多, 它的执行效率越低.


    下面再来看看QString::sprintf(), 这个函数是仿照C函数的sprintf(), 使用方法上一致, 这个函数也不复杂, 进去之后调用QString::vsprintf(), 在QString::vsprintf()中先创建个临时字符串, 然后类似于replaceArgEscapes的方式循环追加到这个临时字符串当中, 唯一的不同是, 这里没有计算最终字符串的大小, 调用的是QString::append(). 这个函数在缓存够大的时候直接追加数据, 如果不够大则重新分配足够大的内存. 所以QString::vsprintf()和QString::arg()本质上并没有太大区别. 因为每次都会生成个新的字符串, 并将数据拷贝进去.


    好的东西都要放到最后面再说, 现在我们来看看QStringList::join()这个方法, 这个方法为什么合并10个数据只用了2.426纳秒的? 看了源代码就会豁然开朗了. 它先循环获取每个字符串的长度, 这样就可以计算出整个字符串的长度, 注意, 是整个, 不是一部分. 实际上不管QStringList里面有多少数据, 它合并的效率永远都是O(1).


那么说到这里, QString::arg()和QString::sprintf()的方法基本一样, 为什么差距那么大呢? 嘿嘿, 你们有没有发现. 我给arg传递的是已经构建完成的QString(), 而给QString::sprintf传递的是char *, 所以arg执行的时候不需要再把参数转换成字符串参数了, 这样省了10次构建QString()的代价, 而QString::sprintf不得不将char*类型的字符串转换成为QString(). 这也是没办法的事儿, 因为QString::sprintf()根本不接受QString()类型的参数.


现在我把QString::arg()和QString::join()里面预先构建的QString()字符串都替换成临时构建. 让我们来看看运行结果.

    运行一次的耗时: QString::arg() 0.642纳秒 < QStringList::join() 0.792纳秒 < QString::sprinft() 1.146纳秒;
    运行十次的耗时: QStringList::join() 4.363纳秒 < QString::arg() 7.171纳秒 < QString::sprinft() 9.192纳秒;


这次算是公平的了, 从上面的数据可以看出来, 为什么官方不推荐使用QString::sprintf()这个函数了, 首先是这么调用不太符合Qt的代码风格, 除了兼容纯C/C++程序员的使用习惯. 基本上可以说一无是处了. 那么为啥也没有推荐使用QStringList.join()呢? 主要是这个函数的适应性不太好, 打个比方, "My father %1 has two bros, %2 was killed by malaria, %3 is still alive.", 如果用QString::arg()是如此简单, 如果用QStringList::join(), 那就要打断成这样: "My father " << "Old John" << " has two bros, " << "one" << " was killed by malaria, " << "another" << " is still alive", 一个是替换三个, 一个是合并7个.


我希望能通过这次测试各位从事Qt的朋友们, QString::sprintf()完全可以放弃了, 认为这个性能会如何如何好的, 现在应该知道了. 的确是后娘没人爱的一个函数; 剩下99%的时候都应该使用QString::arg() , 因为各位不会一下子格式化十个八个参数的, 一般都是一两个的, 这个的性能还是非常高的. 参数超过十个则要果断选择QStringList::join()了.


写了这么多, 希望能对各位有所帮助.
阅读更多
文章标签: qt join 测试 任务 c
个人分类: Qt QString Format
想对作者说点什么? 我来说一句

cpu性能比较 cpu性能比较

2011年04月05日 409KB 下载

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭