最近我的任務是進行一些速度檢查,以便我可以判斷使用php/php-cli或C++將特定數量的行插入數據庫的速度是否更快。MySQL的插入速度比PHP更快,這是預期的嗎?
在我們開始之前,讓我告訴你一些細節,讓一切都清楚了:
- PHP的一部分是通過運行Apache,在瀏覽器中直接提出要求。
- 正在運行的硬盤驅動器測試是SSD驅動器。我猜在普通硬盤中事情會變慢。機器本身沒什麼特別的,六歲左右。
- 所有插入操作都是通過準備好的語句完成的。我們使用php上的mysqli和mysqlcppconcon(由Oracle提供的mysql C++連接器)。
- 所有插入都按條目輸入。我知道我們可以堆疊它們,但是我們正在測試。
- 時間通過microtime在php中顯示,並通過C++頭文件顯示。
- 當然,代碼本身並不等同。稍後更多。
- 所有文本都是UTF-8。那裏有俄羅斯,中國,阿拉伯,西班牙,英國和各種瘋狂的東西。 mysql表格在utf8_4mb中。
- C++代碼的數字是使用g ++編譯的std :: vector和-O2 levels的結果(向量優於maps,unordered_maps和std :: arrays)。
所以,這是過程:
- 連接到數據庫。
- 用N行打開一個文本文件。
- 閱讀文件的一行。
- 拆分分隔符上的行。
- 使用分割線的某些部分來獲取插入值(例如,0,1和3索引)。
- 將這些零件發送到準備好的語句以插入它們。
- 重複,直到完全讀取文件。
兩個代碼的工作方式與預期完全相同。下面是導致數字:
PHP:
- 5000個條目:1.42 - 1.27秒。
- 20000條目:5.53-6.18秒。
- 50000條目:14.43-15.69秒。
C++:
- 5000個條目:1.78 - 1.81秒。
- 20000條目:7.19 - 7.22秒。
- 50000條目:18.52-18.84秒
php優於C++,因爲文件中的行數增加......起初,我懷疑行分裂函數:在PHP中的分裂是用「爆炸」完成的。該算法與C++一樣天真......容器通過引用傳遞,其內容隨時更改。該容器僅被遍歷一次。我確保容器「保留()」所有必要的空間(記住,我最終選擇向量),這是固定的。容器在主函數上創建,然後通過代碼通過引用傳遞。它永遠不會被清空或調整大小:只有其內容發生變化。
template<typename container> void explode(const std::string& p_string, const char p_delimiter, container& p_result)
{
auto it=p_result.begin();
std::string::const_iterator beg=p_string.begin(), end=p_string.end();
std::string temp;
while(beg < end)
{
if((*beg)==p_delimiter)
{
*(it)=temp;
++it;
temp="";
}
else
{
temp+=*beg;
}
++beg;
}
*(it)=temp;
}
如前所述,執行的任務是等效的,但生成它的代碼不是。 C++代碼具有通常的try-catch塊來控制mysql的交互。至於其餘部分,主循環一直運行到EOF到達,並且每次迭代檢查插入是否失敗(無論是在C++還是php中)。
我已經看到C++在處理文件和它們的內容方面大大優於php,所以我期望在這裏適用。不知何故,我懷疑分裂算法,但也許它只是數據庫連接器速度較慢(仍然,當我禁用數據庫交互PHP仍然處理更快)或我的代碼是sub par ...
至於分析, gprof的吐出了這一點,關於C++代碼:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls ns/call ns/call name
60.00 0.03 0.03 50000 600.00 600.00 void anc_str::explotar_cadena<std::vector<std::string, std::allocator<std::string> > >(std::string const&, char, std::vector<std::string, std::allocator<std::string> >&)
40.00 0.05 0.02 insertar(sql::PreparedStatement*, std::string const&, std::vector<std::string, std::allocator<std::string> >&)
0.00 0.05 0.00 1 0.00 0.00 _GLOBAL__sub_I__ZN7anc_str21obtener_linea_archivoERSt14basic_ifstreamIcSt11char_traitsIcEE
其中「explotar_cadena」是「爆炸」和「insertar」是「拆分此行並設置事先準備好的聲明瞭」。正如你所看到的,有60%的時間花在那裏(並不令人驚訝......它運行了50000次,並且做了這個瘋狂的分裂事情)。 「obtener_linea_archivo」只是「請將下一行轉儲到字符串中」。
沒有MySQL的相互作用(只是加載文件,讀取線和分割他們)我得到這些測量:
PHP
- 5000個條目:0.019 - 0.036秒。
- 20000個條目:0.09-0.10秒。
- 50000個條目:0.14-0.17秒
C++
- 5000個條目:0.07 - 0.10秒。
- 20000個條目:0.25 - 0.26秒。
- 50000個條目:0.49-0.55秒。
好吧,這兩個時代都很好,現實生活中還很難察覺,我很驚訝...所以這裏的問題是:我應該期待這個嗎?有經驗的人願意伸出援助之手嗎?
在此先感謝。
編輯:這裏是一個包含輸入文件,C++代碼和php代碼的精簡版的快速鏈接[http://www.datafilehost.com/d/d31034d6]。注意,沒有sql交互:只有文件打開,字符串分割和時間測量。請原諒匆忙完成的屠殺代碼和半西班牙語評論和變量名稱。另外,請注意上面的gprof結果:我不是專家,但我認爲我們試圖找到一種更好的分割字符串的方法。
你能否認爲用[Very Sleepy](http://www.codersnotes.com/sleepy)來測試你的C++程序並在這裏添加結果。 – 2014-12-04 10:29:24
不是窗戶的人在這裏,對不起...我試過gprof。將編輯帖子以反映這一點。 – 2014-12-04 10:35:51
從代碼中刪除定時器,使用控制檯上的系統時間命令來獲取測量結果。您不包括PHP的啓動和關閉時間,結果偏差。 – 2014-12-04 10:57:23