排列5走势图首页    注册   登录
排列5走势图 = way to explore
排列5走势图 是一个排列5走势图关于 分享和探索的地方
现在注册
已注册用户请  登录
排列5走势图  ›  PHP

排列5走势图优化 PHP 大文件排列5走势图下载 速度至万兆,让 Nextcloud 支持万兆网络

  raysonx · 8 天前 · 5787 次点击

背景

最近在 HP Microserver Gen8 上重新搭建了 Nextcloud (在虚拟机里面容器里,基于 PHP 7.2 ),可惜通过 virtio 虚拟万兆网络进行排列5走势图下载 ,SSD 上文件的排列5走势图下载 速度不超过 260MiB/s,机械硬盘上文件的排列5走势图下载 速度不超过 80MB/s。要知道直接排列5走势图本地 访问时,SSD 能达到 550MB/s 左右,机械硬盘平均 130MB/s,不甘心(这时 PHP 进程的 CPU 占用率很低,说明根本没有达到 CPU 执行瓶颈)。

排除了网络问题后,排列5走势图我 在存储目录上搭建了一个 Nginx 进行测试,发现通过 Ngninx 直接排列5走势图下载 文件几乎能达到排列5走势图本地 直接访问的性能。于是,排列5走势图下载 速度慢的锅就落在了 PHP 的性能上。

调研

经过一番调研,Nextcloud 的 WebDav 排列5走势图服务 是基于 sabre/dav 的框架开发的。于是找到了 sabre/dav 的源码,最后定位到排列5走势图下载 文件代码的位置:3rdparty/sabre/http/lib/Sapi.php 。原来 sabre/dav 是通过调用 stream_copy_to_stream 将要排列5走势图下载 的文件拷贝到 HTTP 输出的:直接把文件流和 PHP 的输出流进行对拷,之前并没有其他的读写操作,说明瓶颈就在这一行代码。

// 3rdparty/sabre/http/lib/Sapi.php
// ...
 if (is_resource($body) && 'stream' == get_resource_type($body)) {
                if (PHP_INT_SIZE !== 4) {
                    // use the dedicated function on 64 Bit systems
                    stream_copy_to_stream($body, $output, (int) $contentLength);
                } else {
// ...

排列5走势图我 本人并不是 PHP 程序员,于是开始了漫长的排列5走势图搜索 。Google 娘告诉排列5走势图我 PHP 专门提供了fpassthru函数提供高性能文件排列5走势图下载 ,于是排列5走势图我 修改代码把 stream_copy_to_stream 换成了fpassthru

// 3rdparty/sabre/http/lib/Sapi.php
// ...
 if (is_resource($body) && 'stream' == get_resource_type($body)) {
                if (PHP_INT_SIZE !== 4) {
                    // use the dedicated function on 64 Bit systems
                    // stream_copy_to_stream($body, $output, (int) $contentLength);
                    fpassthru($body); // 改动这一行
                } else {
// ...

测试了一下,发现排列5走势图下载 速度直接打了鸡血,440-470 MiB/s。可惜 fpassthru 只能把文件输出到结尾,不能只输出文件的一部分(为了支持断点续传和分片排列5走势图下载 )。另外翻了一下 sabre/dav 的 issues,发现 sabre/dav 不用fpassthru的另外一个原因是有些版本的 PHP 中fpassthru函数存在 BUG。

继续深入

那为什么stream_copy_to_stream速度和fpassthru差距大得不科学呢?只能去读 PHP 的源码了,幸好 C 语言是排列5走势图我 的强项。 排列5走势图我 发现,fpassthru函数和stream_copy_to_stream函数实现是及其类似的:先尝试把源文件创建为内存映射文件(通过调用 mmap ),如果成功则直接从内存映射文件拷贝到目的流,否则就读到内存中进行传统的手动拷贝。差别来了,stream_copy_to_stream的第三个参数是要拷贝的字节数,可惜如果这个值大于 4MiB,PHP 就拒绝创建内存映射文件,直接回退到传统拷贝。

解决排列5走势图方法

在循环中调用stream_copy_to_stream,每次最多拷 4MiB:

// 3rdparty/sabre/http/lib/Sapi.php
// ...
 if (is_resource($body) && 'stream' == get_resource_type($body)) {
                if (PHP_INT_SIZE !== 4) {
                    // use the dedicated function on 64 Bit systems
                    // 下面是改动的部分:
                    // allow PHP to use mmap by copying in 4MiB chunks
                    $chunk_size = 4 * 1024 * 1024;
                    stream_set_chunk_size($output, $chunk_size);
		    $left = $contentLength;
		    while ($left > 0) {
		        $left -= stream_copy_to_stream($body, $output, min($left, $chunk_size));
		    }
               } else {
 // ...

测试了一下,结果令人震惊:排列5走势图下载 速度几乎和排列5走势图本地 读取无异了:SSD 文件的排列5走势图下载 速度超过了 500 MB/s,甚至超过了 fpassthru 的速度(大概是因为缓冲区开的比fpassthru大)。

排列5走势图我 又试着创建了一个 10G 大小的 sparse 文件 ( truncate -s 10G 10G.bin ),Linux 在读取 sparse 文件时可以立即完成,可以用来模拟如果硬盘速度足够快的情况。继续测试,发现排列5走势图下载 速度超过了 700MiB/s,已经接近万兆网络的传输极限。这时 PHP 进程的 CPU 占用率已经达到 100%,说明瓶颈在 CPU 性能上了。

总结

stream_copy_to_stream 拷贝流时,如果 source 是文件并且每次拷贝小于 4MiB,PHP 会用内存映射文件对拷贝进行加速。超过 4MiB 后就会回退到传统读取机制。

后续

向 Sabre 项目提了 PR:http://github.com/sabre-io/http/pull/119。如果各位也在玩 Nextcloud 并且遇到了排列5走势图下载 速度瓶颈,可以试着打一下排列5走势图我 这个补丁。

第 1 条附言  ·  8 天前
感谢各位的支持和建议,先统一回复一些内容:

1. 部分小伙伴提到了 Nginx 的 X-accel:如果 web 排列5走势图服务 器用的是 Nginx 的话,可以通过设置一个 HTTP header 将文件输出转交给 Nginx,可获得更快的排列5走势图下载 速度。这确实是个可行的排列5走势图优化 思路,毕竟 Nginx 性能优越,而且输出文件的实现基于 sendfile 系统调用,理论上效率更高。目前不知道 Nextcloud 或者 Sabre 有没有计划针对 Nginx 做排列5走势图优化 ,后续有时间可以试着跟进一下。

2. stream
第 2 条附言  ·  8 天前
2. 目前看来 PHP 的 stream_copy_to_stream 的性能不佳,可排列5走势图优化 的地方非常多,可以考虑对这个函数进行排列5走势图优化 ,贡献给上游社区。

3. 有小伙伴担心对 sabre 的“魔改”会对其他地方造成影响。排列5走势图我 觉得这应该不是魔改吧。。。。
第 3 条附言  ·  8 天前
忘了在正文中补充机械硬盘排列5走势图下载 速度的提升:
机械硬盘的排列5走势图下载 速度从排列5走势图优化 前的 70-80MB/s 提升到了 100 - 120 MB/s。排列5走势图我 没有仔细去研究机械硬盘排列5走势图下载 速度提升的原因,可能是排列5走势图我 调大了拷贝的缓存区大小所致。
第 4 条附言  ·  7 天前
补充:PHP 核心源码中拒绝映射 4MiB 以上文件的代码在这里: http://github.com/php/php-src/blob/623911f993f39ebbe75abe2771fc89faf6b15b9b/main/streams/mmap.c#L34
89 回复  |  直到 2019-04-19 11:13:09 +08:00
    1
zk8802   8 天前 via iPhone   ♥ 1
赞楼主刨根问底的精神!
    2
HiCode   8 天前
厉害!楼主专研精神真棒!
    3
zhs227   8 天前
没玩过这么高级的装备,不过非常佩服楼主,顶一下友情支持
另外不清楚有没有人知道,nginx 的那个 sendfile 和这个 mmap 的拷贝机制是不是一回事
    4
raysonx   8 天前   ♥ 1
@zhs227 是的`sendfile`的性能更高,直接让内核对拷两个文件描述符,连内核态 /用户态拷贝都不用。但是 PHP 至今没有利用`sendfile`,包括`fpassthru`。
    5
jinyang656   8 天前 via Android
佩服佩服,真 极客
    6
falcon05   8 天前 via iPhone
厉害啊
    7
sxcccc   8 天前 via iPhone
aws 的 ec2 高端配置 东京节点 排列5走势图首页 500kb 打开速度一流 内容分发都是 4gb 大包依然能迅速排列5走势图下载 参考 www.dxqq.net
    8
lzxgh621   8 天前 via iPhone
排列5走势图我 这边 程序本体都跑不利索
    9
shuimugan   8 天前
很棒,最近在团队内部推 nextcloud,以及基于 Collabora 的办公文档协作,先收藏留作备用了.
    10
tony601818   8 天前 via Android
厉害,难得有真正有意义的话题了!
    11
lihongming   8 天前 via iPhone
赞,可以考虑测一下 2M 一个循环,看是不是会更早达到 CPU 瓶颈,那样的话就该考虑自己修改 stream_copy_to_stream 源码放宽限制,以获得更高性能了。
    12
herexf   8 天前 via Android
好久没在 app 第一页看到这样的排列5走势图技术 贴,今天一天心情肯定会不错
    13
lazyyz   8 天前 via Android
厉害,佩服楼主这折腾劲!
    14
taresky   8 天前 via iPhone
厉害!
    15
JaguarJack   8 天前 via iPhone
一大早就学习了
    16
carlclone   8 天前 via Android
强,基础好扎实
    17
mokeyjay   8 天前
强无敌,点赞
    18
CallMeReznov   8 天前 via Android
这才是真正的干货啊
    19
zvcs   8 天前 via Android
谢谢楼主的分享
    20
Canon1014   8 天前
目瞪口呆
    21
zuokanyunqishi   8 天前 via Android
点赞
    22
fengtalk   8 天前
收藏了,佩服和赞赏楼主的这种探索精神。
    23
Edwards   8 天前
收藏
    24
zzxCNCZ   8 天前
赞楼主,厉害了
    25
R18   8 天前
厉害了!打破砂锅闻到底
    26
fox0001   8 天前 via Android
点赞! nextcloud 15 之前,性能低下,排列5走势图我 只是从树莓派搬到 x8350。一直以为是 PHP 背的锅,没想到楼主还能找出具体原因
    27
SupperMary   8 天前 via Android
很强👍
    28
eluotao   8 天前
排列5走势图技术 贴 要收藏...回头看看 NAS 有没有排列5走势图优化 的空间.
    29
whatsmyip   8 天前
很强
    30
yngby   8 天前
牛逼牛逼
    31
polymerdg   8 天前
牛逼
    32
hst001   8 天前 via Android
666
    33
SbloodyS   8 天前
牛逼
    34
sorshion   8 天前
基础很扎实,厉害
    35
liuxu   8 天前
这波操作可以的
    36
whwq2012   8 天前 via Android
⊙∀⊙!这就是开源的魅力啊,有需要就可以自己改。不过确定魔改这一部分的代码不会对其他地方造成影响吗?
    37
dapang1221   8 天前
厉害了
    38
bzi   8 天前
厉害啊
    39
tailf   8 天前
服了
    40
reeble   8 天前
大佬大佬
    41
sheeta   8 天前
佩服佩服
    42
zhujinliang   8 天前 via iPhone
使用 nginx 的 X-Accel-Redirect 可不可行呢
    43
ipengxh   8 天前
厉害了
    44
liuxyon   8 天前
厉害👍
    45
yytsjq   8 天前
@zhujinliang X-Accel-Redirect 相比 fpassthru 应该更好些吧
    46
dalieba   8 天前 via Android
希望 Sabre 项目早日接纳楼主的改进,新版本早日发布。
    47
klusfq   8 天前 via iPhone
膜拜楼主大佬
    48
zzxx3322   8 天前
楼主有遇到上传瓶颈吗?官方默认最多同时上传三个任务,关键速度跑不满,排列5走势图我 没有详细测试是不是网络或者硬件问题导致速度跑不满,但是排列5走势图我 感觉排列5走势图你 的问题和这个问题也应该是相同的锅,提一下,可以给点意见嘛?
    49
duola   8 天前
折腾精神,厉害!
    50
raysonx   8 天前 via Android
@zzxx3322 上传速度确实比排列5走势图下载 慢很多。Nextcloud 的上传机制比较复杂,等有时间研究一下开个帖分享。
    51
moonfly   8 天前
排列5走势图技术 贴必须要支持,
虽然自己的功力远远没有达到 LZ 的级别,
但能看到这样的帖子,真的是一种享受!
    52
Huelse   8 天前
真是一篇干货,感谢感谢!!
    53
Actrace   8 天前
还有一个方案,文件输出完全交给 Nginx 去做,PHP 只负责处理输出前逻辑。
这里需要用到 Nginx 的一个特性 X-Accel-Redirect,不过这样整套程序就和 Nginx 绑定到一起了。
    54
zjq123   8 天前 via Android
排列5走势图你 们排列5走势图下载 速度达到几百兆每秒?
    55
dnsaq   8 天前 via iPhone
目瞪口呆 排列5走势图我 都看懵了。
    56
tongz   8 天前
奈何本人没文化, 一句卧槽走天下
    57
laozhoubuluo   8 天前
啥也不说了,点赞!!👍👍👍
    58
ultimate010   8 天前
真心点赞,排列5走势图我 自己搭建的局域网 samba 和 nfs 等文件排列5走势图服务 ,速度也没法跑满千兆网卡,查了下参数排列5走势图优化 了下 samba aio,有点提升,但是仍然无法满速,没思路就凑合用了。
    59
KasuganoSoras   8 天前


确实是快了很多,在千兆排列5走势图服务 器上测试的
    60
KasuganoSoras   8 天前   ♥ 1
@ultimate010 #58 局域网 samba 排列5走势图我 测试千兆是可以跑满的,传文件速度稳定在 110MB/s 左右,如果跑不满可能是 samba 版本比较低或者其他问题
    61
killerv   8 天前
厉害了
    62
cfcboy   8 天前
感谢楼主的分享,做个记号。
    63
HuasLeung   8 天前
awesome
    64
fengci   8 天前
mk
    65
panlilu   7 天前
硬核 debug
    66
ultimate010   7 天前
@KasuganoSoras 谢谢,排列5走势图我 用 docker 跑的,最新的 dperson/samba,小机器 cpu 是 Intel(R) Atom(TM) CPU D525 @ 1.80GHz,机械硬盘,全速的时候也就 50mb 左右,以前调出过写入 80mb,读取也就 30-40mb,cpu 好像没有跑满,感觉自己的配置有点问题。
    67
KasuganoSoras   7 天前   ♥ 1
@ultimate010 #66 这应该就是 CPU 性能瓶颈问题了,排列5走势图我 手上也有一台 Atom D2550 的工控主机,装了 Samba 测试也是跑不满千兆,速度在 100-400Mbps 左右浮动,就上不去了
    68
kookxiang   7 天前
应该用 sendfile 吧
    69
ben1024   7 天前
厉害
    70
intsilence   7 天前
手动点赞!
    71
raysonx   7 天前
@KasuganoSoras 截图中是打了补丁后的速度吗?之前是多少? CPU load 有没有跑满?
    72
KasuganoSoras   7 天前
@raysonx #71 之前大概是 20 ~ 40M/s 左右,CPU 的话基本上不可能跑满的……至少是宽带先跑满
因为 CPU 是 32 核 64 线程,但是排列5走势图下载 的时候看到 CPU 占用率明显比之前高了,速度也快了很多
    73
dandycheung   7 天前 via Android
@zhs227 不是一回事。
    74
KasuganoSoras   7 天前
@raysonx #71 这个速度其实不是固定的,一直在跳来跳去,可能和排列5走势图我 排列5走势图本地 网络有关,排列5走势图我 看到有几秒钟速度上到了 97MB/s,然后又掉到 60 左右,不过已经算很不错了
    75
raysonx   7 天前
@KasuganoSoras 有时间的话可以试试在排列5走势图服务 器上排列5走势图本地 测速,排除网络影响。排列5走势图方法 是直接用 curl 命令排列5走势图下载 文件:

curl -o /dev/null --user 'username:password' -H hostname http://127.0.0.1/remote.php/webdav/文件名
    76
BooksE   7 天前
排列5走势图你 们都是点赞?只有排列5走势图我 是羡慕 lz 有这个能力
    77
wmwwmv   7 天前 via Android
这对排列5走势图我 很有用,感谢楼主
    78
ericgui   7 天前
@BooksE 看来学 C 还是挺有用的
    79
tankren   7 天前 via Android
收藏了 谢谢
    80
silencefent   7 天前
速度提高 3 倍,cpu 跑满载还是有点划不来吧,gen8 好歹也是 3.5G 起步的 4C8T 排列5走势图服务 器
    81
ganbuliao   7 天前
牛逼 排列5走势图我 觉得 还是比较适合拧小螺丝钉
    82
ganbuliao   7 天前
还是觉得自己比较适合拧小螺丝钉 (滑稽
    83
wttx   7 天前 via Android
Mark 一下,以后有用,,
    84
telami   7 天前
开源的魅力,心向往之
    85
lzj307077687   7 天前
敬佩!
    86
nyaruko   7 天前
万兆。。厉害了。。家里千兆网完全不担心这些。
    87
knightgao2   6 天前
厉害厉害了
    88
Jaeger   6 天前
反手就是一赞
    89
abccccabc   4 天前
这个贡献可大了。
排列5走势图关于   ·   FAQ   ·   API   ·   排列5走势图排列5走势图我 们 的愿景   ·   广告投放   ·   感谢   ·   实用小排列5走势图工具   ·   4079 人在线   最高记录 5043   ·  
创意工作者们的社区
World is powered by solitude
VERSION: 3.9.8.3 · 29ms · UTC 08:01 · PVG 16:01 · LAX 01:01 · JFK 04:01
♥ Do have faith in what you're doing.
沪排列5走势图ICP 备16043287号-1