我假设您无法遵循 SomeDude 关于使用数据库的非常好的建议,并且您已经执行了可以执行的硬件调整(例如增加缓存、增加 RAM 以避免交换抖动、购买 SSD 驱动器)。
我会尝试将文件生成卸载到不同的进程。
你可以例如安装 Redis 并将文件内容存储到 keystore 中,速度非常快。然后,一个不同的并行进程可以从密钥库中提取数据,将其删除,然后写入磁盘文件。
这会从 PHP 主进程中移除所有磁盘 I/O,并让您监控积压(有多少密钥对仍未刷新:理想情况下为零)并专注于内容生成的瓶颈。您可能需要一些额外的 RAM。
另一方面,这与写入 RAM 磁盘并没有太大区别。您还可以将数据输出到 RAM 磁盘,它可能会更快:
# As root
mkdir /mnt/ramdisk
mount -t tmpfs -o size=512m tmpfs /mnt/ramdisk
mkdir /mnt/ramdisk/temp
mkdir /mnt/ramdisk/ready
# Change ownership and permissions as appropriate
在 PHP 中:
$fp = fopen("/mnt/ramdisk/temp/{$file}", "w");
fwrite($fp, $data);
fclose($fp);
rename("/mnt/ramdisk/temp/{$file}", "/mnt/ramdisk/ready/{$file}");
然后有一个不同的进程(crontab?或持续运行的守护进程?)将文件从 RAM 磁盘的“就绪”目录移动到磁盘,然后删除 RAM 就绪文件。
文件系统
创建文件所需的时间取决于目录中文件的数量,各种依赖函数本身都依赖于文件系统。 ext4、ext3、zfs、btrfs 等将表现出不同的行为。具体来说,如果文件数量超过某个数量,您可能会遇到明显的减速。
因此,您可能想尝试在一个目录中创建大量示例文件的时间,看看这个时间如何随着数量的增长而增长。请记住,访问不同目录会降低性能,因此再次不建议直接使用大量子目录。
<?php
$payload = str_repeat("Squeamish ossifrage. \n", 253);
$time = microtime(true);
for ($i = 0; $i < 10000; $i++) {
$fp = fopen("file-{$i}.txt", "w");
fwrite($fp, $payload);
fclose($fp);
}
$time = microtime(true) - $time;
for ($i = 0; $i < 10000; $i++) {
unlink("file-{$i}.txt");
}
print "Elapsed time: {$time} s\n";
在我的系统上创建 10000 个文件需要 0.42 秒,但创建 100000 个文件 (10x) 需要 5.9 秒,而不是 4.2 秒。另一方面,在 8 个单独的目录中创建其中八分之一的文件(我发现的最佳折衷方案)需要 6.1 秒,因此不值得。
但是假设创建 300000 个文件需要 25 秒而不是 17.7 秒;将这些文件分成十个目录可能需要 22 秒,并且使目录拆分值得。
并行处理:r 策略
TL;DR 这在我的系统上运行不佳,尽管您的里程可能会有所不同。如果要完成的操作冗长(这里它们不是)并且与主进程的绑定不同,那么将它们分别卸载到不同的线程可能是有利的,前提是您不产生太多很多线程。
您需要安装pcntl functions。
$payload = str_repeat("Squeamish ossifrage. \n", 253);
$time = microtime(true);
for ($i = 0; $i < 100000; $i++) {
$pid = pcntl_fork();
switch ($pid) {
case 0:
// Parallel execution.
$fp = fopen("file-{$i}.txt", "w");
fwrite($fp, $payload);
fclose($fp);
exit();
case -1:
echo 'Could not fork Process.';
exit();
default:
break;
}
}
$time = microtime(true) - $time;
print "Elapsed time: {$time} s\n";
(花哨的名字r strategy取自生物学)。
在这个例子中,如果与每个孩子需要做的相比,产卵时间是灾难性的。因此,整体处理时间猛增。有了更复杂的孩子,事情会变得更好,但你必须小心不要把脚本变成叉子炸弹。
如果可能的话,一种可能性是将要创建的文件划分为例如每个 10% 的块。然后,每个孩子将使用 chdir() 更改其工作目录,并在不同的目录中创建其文件。这将抵消在不同子目录中写入文件的惩罚(每个子目录写入 其 当前目录),同时受益于写入更少的文件。在这种情况下,在子节点中使用非常轻量级和 I/O 绑定的操作,该策略再次不值得(我得到双倍的执行时间)。
并行处理:K策略
TL;DR 这更复杂但运行良好......在我的系统上。您的里程可能会有所不同。
虽然 r 策略涉及 许多 即发即弃的线程,但 K 策略需要一个经过精心培养的有限(可能是一个)孩子。在这里,我们将所有文件的创建卸载到一个并行线程,并通过套接字与其通信。
$payload = str_repeat("Squeamish ossifrage. \n", 253);
$sockets = array();
$domain = (strtoupper(substr(PHP_OS, 0, 3)) == 'WIN' ? AF_INET : AF_UNIX);
if (socket_create_pair($domain, SOCK_STREAM, 0, $sockets) === false) {
echo "socket_create_pair failed. Reason: ".socket_strerror(socket_last_error());
}
$pid = pcntl_fork();
if ($pid == -1) {
echo 'Could not fork Process.';
} elseif ($pid) {
/*parent*/
socket_close($sockets[0]);
} else {
/*child*/
socket_close($sockets[1]);
for (;;) {
$cmd = trim(socket_read($sockets[0], 5, PHP_BINARY_READ));
if (false === $cmd) {
die("ERROR\n");
}
if ('QUIT' === $cmd) {
socket_write($sockets[0], "OK", 2);
socket_close($sockets[0]);
exit(0);
}
if ('FILE' === $cmd) {
$file = trim(socket_read($sockets[0], 20, PHP_BINARY_READ));
$len = trim(socket_read($sockets[0], 8, PHP_BINARY_READ));
$data = socket_read($sockets[0], $len, PHP_BINARY_READ);
$fp = fopen($file, "w");
fwrite($fp, $data);
fclose($fp);
continue;
}
die("UNKNOWN COMMAND: {$cmd}");
}
}
$time = microtime(true);
for ($i = 0; $i < 100000; $i++) {
socket_write($sockets[1], sprintf("FILE %20.20s%08.08s", "file-{$i}.txt", strlen($payload)));
socket_write($sockets[1], $payload, strlen($payload));
//$fp = fopen("file-{$i}.txt", "w");
//fwrite($fp, $payload);
//fclose($fp);
}
$time = microtime(true) - $time;
print "Elapsed time: {$time} s\n";
socket_write($sockets[1], "QUIT\n", 5);
$ok = socket_read($sockets[1], 2, PHP_BINARY_READ);
socket_close($sockets[1]);
这在很大程度上取决于系统配置。例如,在单处理器、单核、非线程 CPU 上,这太疯狂了 - 您至少会使总运行时间增加一倍,但更有可能会慢三到十倍.
所以这绝对是不是在旧系统上运行某些东西的方法。
在现代多线程 CPU 上并假设主要内容创建循环受 CPU 限制,您可能会遇到相反的情况 - 脚本可能会快十倍。
在我的系统上,上述“分叉”解决方案的运行速度略低于 三倍。我期待更多,但你来了。
当然,性能是否值得增加复杂性和维护,还有待评估。
坏消息
在进行上述实验时,我得出的结论是,在 Linux 中配置合理且性能良好的机器上创建文件快得要命,因此不仅很难获得更多性能,而且如果你'再次遇到缓慢,它很可能与 不 文件相关。尝试详细说明您是如何创建该内容的。