Qt字符串格式化性能比較 Qt字符串格式方法有三種, QString::arg(), QString::sprinft()和QStringList::join(). 今天我作了個簡單的性能測試, 但願對各位有所幫助.
調用QString::arg()一次:
- QString s("1234567890");
// 開始計時 for (int i = 0; i < 10000; ++i) { QString str("%1"); str.arg(s); } // 中止計時
調用QString::arg()十次:
- QString s("1234567890");
// 開始計時 for (int i = 0; i < 10000; ++i) { QString str("%1%2%3%4%5%6%7%8%9%10"); str .arg(s) .arg(s) .arg(s) .arg(s) .arg(s) .arg(s) .arg(s) .arg(s) .arg(s) .arg(s); } // 中止計時
調用QString::sprinft()一次:
- char s2[] = {"1234567890"};
// 開始計時 for (int i = 0; i < times; ++i) { QString().sprintf("%d", s2); } // 中止計時
調用QString::sprinft()十次:
- char s2[] = {"1234567890"};
// 開始計時 for (int i = 0; i < times; ++i) { QString().sprintf("%d%d%d%d%d%d%d%d%d%d", s2, s2, s2, s2, s2, s2, s2, s2, s2, s2); } // 中止計時
調用QStringList::join()一次:
- QString s("1234567890");
// 開始計時 for (int i = 0; i < times; ++i) { QStringList strList; strList.append(s); strList.join(QString()); } // 中止計時
調用QStringList::join()十次:
- QString s("1234567890");
// 開始計時 for (int i = 0; i < times; ++i) { QStringList strList; strList.append(s); strList.append(s); strList.append(s); strList.append(s); strList.append(s); strList.append(s); strList.append(s); strList.append(s); strList.append(s); strList.append(s); strList.join(QString()); } // 中止計時
測試結果: 運行一次的耗時: 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()了.
寫了這麼多, 但願能對各位有所幫助.
補充: 很是謝謝dbzhang800" data-card-url="pw_ajax.php?action=smallcard&type=showcard&uid=" target="_blank" onclick="return checkUrl(this)" id="url_1">dbzhang800的補充, QString::arg()的模板函數版本平時真的沒怎麼用過, 哈哈. 因爲QString::arg()的模板版本不接受>=10的參數, 因此把十個參數改爲九個參數進行比較. 下面是最新的性能比較結果. 運行 運行一次的耗時: QStringBuilder 0.362納秒 < QString::arg() 0.636納秒 < QString::arg() 模板版本 0.637納秒 < QStringList::join() 0.821納秒 < QString::sprinft() 1.275納秒; 運行十次的耗時: QStringBuilder 2.126納秒 < QStringList::join() 3.781納秒 < QString::arg() 模板版本 5.088納秒 < QString::arg() 6.290納秒 < QString::sprinft() 8.933納秒;
從上面的運行結果來看, QString::arg()模板版本只是略微強於QString::arg()的屢次調用版本. 函數內部仍然採用的是追加字符串的方式, 因此這種處理方式仍然面臨屢次分配內存的操做. 並且QString::arg()模板版本其實並非模板函數, 只是一次接受多個QString類型的參數而已, 並且只能接受九個, 實際上它已經丟失了QString::arg()普通版本的一大優點--基本類型通殺. 也就是說, 若是你要使用QString::arg()的模板版本(姑且這麼說吧), 那麼你不得不將數據都本身轉換成QString類型的, 不然你就沒辦法使用. 牛叉的總要放在後面說, 是吧. dbzhang800" data-card-url="pw_ajax.php?action=smallcard&type=showcard&uid=" target="_blank" onclick="return checkUrl(this)" id="url_2">dbzhang800翻譯的文章中已經對這個有詳細的介紹了. 使用很是簡單, #include <QStringBuilder>, 這個是內建的, 而後注意字符串鏈接時候的鏈接符是%, 而不是+.
再次感謝dbzhang800" data-card-url="pw_ajax.php?action=smallcard&type=showcard&uid=" target="_blank" onclick="return checkUrl(this)" id="url_4">dbzhang800的幫助, 但願你們可以繼續補充! |
|