最近我的任务是进行一些速度检查,以便我可以判断使用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