V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
zjsxwc
V2EX  ›  程序员

php7 怎么比 Java 还快?

  •  2
     
  •   zjsxwc ·
    zjsxwc · 2019-09-23 11:22:47 +08:00 · 16960 次点击
    这是一个创建于 1892 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有点反认知,分别用 PHP 与 Java Spring Boot 写了个返回 1 个像素点图片的接口,结果 php 比 java 快。

    代码

    PHP 代码

    <?php
    //xpng.php
    
    header("Content-type: image/png");
    
    echo base64_decode("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWP4////fwAJ+wP9CNHoHgAAAABJRU5ErkJggg==");
    

    Java 代码

    //XController.java
    
    package com.abtest;
    
    import java.util.Base64;
    import org.springframework.http.HttpHeaders;
    import org.springframework.http.HttpStatus;
    import org.springframework.http.ResponseEntity;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    
    @RestController
    public class XController {
        @RequestMapping("/x.png")
        public ResponseEntity<byte[]> xpng() {
            String onepointpngBase64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWP4////fwAJ+wP9CNHoHgAAAABJRU5ErkJggg==";
            HttpHeaders responseHeaders = new HttpHeaders();
            responseHeaders.set("Content-type", "image/png");
            byte[] decodedValue = Base64.getDecoder().decode(onepointpngBase64);
            return new ResponseEntity<byte[]>(decodedValue, responseHeaders, HttpStatus.OK);
        }
    }
    

    开始测试

    Server 分别 SpringBoot Jar 包自带的 Apache Tomcat/9.0.24

    PHP 就是 PHP 7.0.33-0+deb9u1 (cli) 自带的低性能调试用 server (使用php -S localhost:7767 -t .运行)

    AB 结果反直觉,PHP 居然比 Java 快

    PHP 结果耗时 0.125 秒
    $ ab -n 1000 -c 1000 "http://localhost:7767/xpng.php"
    This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking localhost (be patient)
    Completed 100 requests
    Completed 200 requests
    Completed 300 requests
    Completed 400 requests
    Completed 500 requests
    Completed 600 requests
    Completed 700 requests
    Completed 800 requests
    Completed 900 requests
    Completed 1000 requests
    Finished 1000 requests
    
    
    Server Software:       
    Server Hostname:        localhost
    Server Port:            7767
    
    Document Path:          /xpng.php
    Document Length:        70 bytes
    
    Concurrency Level:      1000
    Time taken for tests:   0.125 seconds
    Complete requests:      1000
    Failed requests:        0
    Total transferred:      190000 bytes
    HTML transferred:       70000 bytes
    Requests per second:    8011.99 [#/sec] (mean)
    Time per request:       124.813 [ms] (mean)
    Time per request:       0.125 [ms] (mean, across all concurrent requests)
    Transfer rate:          1486.60 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    6   5.5      8      14
    Processing:     5   22  20.1     13      69
    Waiting:        4   22  20.2     12      69
    Total:          8   29  21.3     19      71
    
    Percentage of the requests served within a certain time (ms)
      50%     19
      66%     24
      75%     37
      80%     62
      90%     68
      95%     70
      98%     70
      99%     70
     100%     71 (longest request)
    
    Java 结果耗时 0.498 秒
    $ ab -n 1000 -c 1000 "http://localhost:8080/x.png"
    This is ApacheBench, Version 2.3 <$Revision: 1757674 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking localhost (be patient)
    Completed 100 requests
    Completed 200 requests
    Completed 300 requests
    Completed 400 requests
    Completed 500 requests
    Completed 600 requests
    Completed 700 requests
    Completed 800 requests
    Completed 900 requests
    Completed 1000 requests
    Finished 1000 requests
    
    
    Server Software:       
    Server Hostname:        localhost
    Server Port:            8080
    
    Document Path:          /x.png
    Document Length:        70 bytes
    
    Concurrency Level:      1000
    Time taken for tests:   0.498 seconds
    Complete requests:      1000
    Failed requests:        0
    Total transferred:      188000 bytes
    HTML transferred:       70000 bytes
    Requests per second:    2007.85 [#/sec] (mean)
    Time per request:       498.046 [ms] (mean)
    Time per request:       0.498 [ms] (mean, across all concurrent requests)
    Transfer rate:          368.63 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    1   3.2      0      11
    Processing:     5   73  63.3     48     237
    Waiting:        3   64  56.7     41     194
    Total:          5   74  66.0     48     247
    
    Percentage of the requests served within a certain time (ms)
      50%     48
      66%     63
      75%     76
      80%     88
      90%    216
      95%    225
      98%    229
      99%    234
     100%    247 (longest request)
    
    第 1 条附言  ·  2019-09-23 12:44:32 +08:00
    我收回说的这句话,java 只是在前几次跑的时候会比 php 慢,之后多次访问后会快很多,

    大概在同一个接口访问 5、6 遍后,耗时就低于 0.1 秒了,比 php 快了
    第 2 条附言  ·  2019-09-23 13:39:02 +08:00
    php7 使用 php-fpm 结果 1000 并发是 0.062 秒, 9000 并发我是在 0.46 秒之间波动(需要把 php-fpm 闲置进程时长调长,不然每个 10 秒都会把闲置进程回收再启用会产生不必要耗时)

    java tomcat 在多次访问后 1000 并发是 0.06 秒,9000 并发我是稳定在 0.5 秒左右

    整体感觉其实性能 php 与 java 已经区别不大。
    第 3 条附言  ·  2019-09-23 14:48:41 +08:00
    io 密集方面也就这样都差不多,语言的主要优势还是在计算密集需求里面

    写了几个程序算第 30 万个质数,

    c/c++耗时 2.8 秒 https://paste.ubuntu.com/p/Sm3rQYK3Kj/
    java 耗时 3.3 秒 https://paste.ubuntu.com/p/84Zyzr6w3V/
    golang 耗时 10.4 秒 https://paste.ubuntu.com/p/MSNWkJTvz6/
    php7.0 耗时 22.2 秒 https://paste.ubuntu.com/p/M7gj3XfxFM/
    第 4 条附言  ·  2019-09-23 15:02:36 +08:00
    JavaScript 耗时 3.5 秒 https://paste.ubuntu.com/p/rd8hYtxQc2/
    第 5 条附言  ·  2019-09-26 16:55:26 +08:00
    拉了最新的 php7.4 源码本机编译后跑同样的代码 h 耗时 20.1 秒,比 php7.0 快 2 秒,性能不行呀
    第 6 条附言  ·  2019-09-26 17:10:19 +08:00
    对 php7.4 开启 opcache 后 耗时 18 秒,还是性能不行
    第 7 条附言  ·  2019-10-14 10:51:40 +08:00
    php8.0 开启 jit 耗时 20 多秒



    https://i.loli.net/2019/10/14/R5bchsBoA3SGqQD.png
    第 8 条附言  ·  2019-10-15 09:09:01 +08:00
    python 2.7 耗时 65991 毫秒 https://paste.ubuntu.com/p/5jqjBKgBFB/
    第 9 条附言  ·  2019-12-09 06:52:26 +08:00
    强类型的 hhvm4 并没有比 php7 快多少,快了 1 秒的样子,放弃
    第 10 条附言  ·  2020-03-24 09:29:00 +08:00
    134 条回复    2021-11-26 08:27:26 +08:00
    1  2  
    guixiexiezou
        101
    guixiexiezou  
       2019-09-24 10:01:14 +08:00
    测试方法不太准确。你要真的去测试一门语言速度怎么就直接去写大型矩阵运算,基本就是内存分配就调用 cpu 计算。相对而言比较好比较,因为没啥额外条件。而且现在都 9102 年,几乎所有语言都是直接调用 c 模块(golang 这种除外),运行速度不会有太大的差距的。
    rjwphp
        102
    rjwphp  
       2019-09-24 10:05:35 +08:00
    真心羡慕你们啥都懂的人
    silencefent
        103
    silencefent  
       2019-09-24 10:07:26 +08:00
    js 是最好的语言
    sobigfish
        104
    sobigfish  
       2019-09-24 10:13:24 +08:00
    这是来黑 PHP 的吧,你直接输出对比一个 framework ?
    zjsxwc
        105
    zjsxwc  
    OP
       2019-09-24 10:26:39 +08:00 via Android
    @wo642436249 #92 原文:“照我的理解,java、go 之类的代码,是启动时候直接加载到内存中了,单凭这个就很快了,而 php 大多是跑在 fmp 中的,每次请求都重新拉起进程,这样很慢,如果把 php 加载内存中,比如结合 swoole,那破 java 也很容易”
    回复:

    php-fpm 配置调一下把回收 children 进程频率降低延长 worker 进程寿命就能提高性能
    zjsxwc
        106
    zjsxwc  
    OP
       2019-09-24 10:27:24 +08:00 via Android
    @sobigfish #103 原文:“这是来黑 PHP 的吧,你直接输出对比一个 framework ?”
    回复:

    后面回帖有不用框架用 servlet 的
    speedofstephen
        107
    speedofstephen  
       2019-09-24 10:36:57 +08:00
    notreami
        108
    notreami  
       2019-09-24 10:48:12 +08:00
    @zjsxwc 讨好程序员领域???
    热门前 30 的编程语言,绝大多数都标榜有讨好程序员领域,目前看来,证明方式就是,某个领域的霸主地位,比如 C 在嵌入式上、go 在服务运维上,java 在大数据上,python 在 AI 上、js 在前端上。而 PHP 原来是在网页模版上,但是近几年 js 明显想自立门户。

    类型转换(强、弱类型)这事嘛,大神们都讨论了几十年。牺牲类型是可以提升编码速度,但是缺乏代码质量和持续迭代保障(无注释 array,你猜里面有什么数据?)。如果非要证明强弱类型谁好,可以从 js 发展上看出,这两年,jser 里很多人都主动引入 typescript 了。
    fhsan
        109
    fhsan  
       2019-09-24 11:04:34 +08:00
    脱离业务谈速度,都是胡扯。
    notreami
        110
    notreami  
       2019-09-24 11:07:51 +08:00
    @zjsxwc @wo642436249 别瞎折腾、别讲理论,techempower 上提 pr,Shut up and show them the code
    encro
        111
    encro  
       2019-09-24 11:10:41 +08:00
    @nioncodotcom
    其实语言层面已经差不都了,
    现在基本所有语言都支持多进程,多线程,协程,
    所以主要是看协程实现的复杂度和开销了,这方面 go, rust 占有优势。
    polymerdg
        112
    polymerdg  
       2019-09-24 16:01:11 +08:00
    @encro PHP 的短板,说到点子上了, 特别是高并发 IO 密集型业务 PHP 会很难受
    cloudzhou
        113
    cloudzhou  
       2019-09-25 14:45:49 +08:00
    @zhuzeitou
    https://paste.ubuntu.com/p/6kynBdJgsv/
    这个版本,将耗时减少一半
    zhuzeitou
        114
    zhuzeitou  
       2019-09-25 22:50:11 +08:00
    @cloudzhou 这个直接优化算法了啊,因为其他语言的算法都是一致的,所以之前刻意没往这个方向改

    https://paste.ubuntu.com/p/xJJZwzSbM9/
    这个地方有 3 个改动:
    1.将 for 循环改为 goto 控制
    2.将返回值类型改为 int32
    3.将 if i%j == 0 的判断改为 if i%j != 0
    在这 3 个改动同时同时存在时,在我的设备上可以再减少 100ms 左右,如果只存在其中 1 个或者 2 个则没什么效果甚至更慢

    另外还尝试了 j 初始化为 3,再 if i%j != 0 时再+2,也没有什么效果
    zhuzeitou
        115
    zhuzeitou  
       2019-09-25 23:00:07 +08:00
    @cloudzhou 顺便,楼主贴的 c++的耗时,应该是没开编译优化的,-O2 的话耗时能减少一半左右吧
    zhuzeitou
        116
    zhuzeitou  
       2019-09-25 23:17:42 +08:00
    又做了一些尝试

    https://paste.ubuntu.com/p/M3zZ4y6SPh/
    这里只对 else if i%j != 0 这个分支加 goto 效果明显,其他两个分支加不加都差不多

    https://paste.ubuntu.com/p/zvCpbXtCHB/
    这里更换了 else if 和 else 的判断顺序,在 else 分支加 goto 效果明显,else if i%j == 0 这个分支加不加没什么区别,在 if j > i/j 这个分支加的话会明显变慢……
    cloudzhou
        117
    cloudzhou  
       2019-09-26 09:54:33 +08:00   ❤️ 1
    cloudzhou
        118
    cloudzhou  
       2019-09-26 18:52:14 +08:00
    @zhuzeitou ok,来了一个最终优化版 :-)

    https://paste.ubuntu.com/p/hgxr3CXkQf/ 再次减少一半
    zhuzeitou
        119
    zhuzeitou  
       2019-09-27 00:28:59 +08:00
    @cloudzhou 最后这两个版本都是算法优化了,最后这是空间换时间了啊……我也来玩一下~

    https://paste.ubuntu.com/p/wr7nMXhnSm/
    zhuzeitou
        120
    zhuzeitou  
       2019-09-27 09:50:44 +08:00
    @cloudzhou 在不同的机器上结果差别也挺大的,就拿我最后贴的那个代码,在昨天测试的机器上我的代码会比你的快一些,但现在手边的两台机器都是你的代码更快些
    cloudzhou
        121
    cloudzhou  
       2019-09-27 13:47:09 +08:00
    @zhuzeitou 仅仅从代码分析的话,我的代码更符合优化逻辑
    因为减少了 f * f > i or f > i / f 的乘除操作(通过增量计算开平方 sqrt )
    不过这个题目挺有意思,让我发现一个简单的问题,可以这样的优化
    larry123
        122
    larry123  
       2019-09-27 15:24:08 +08:00
    我记得 Java 默认是解释执行 class,当代码变成热点代码后,会进行 JIT 编译。
    zhuzeitou
        123
    zhuzeitou  
       2019-09-27 17:16:19 +08:00
    @cloudzhou 恩,之前没想到,也没仔细看你的代码,的确这样更好呢

    https://paste.ubuntu.com/p/FpY5kK9R3q/
    这里 NthPrime 是你上面版本的函数,NthPrime2 是我刚才又修改的函数
    然后在我这边的机器上,调整函数定义的顺序(定义的顺序,不是执行的顺序哦……),执行效果差异也很大……
    cloudzhou
        124
    cloudzhou  
       2019-09-27 17:46:54 +08:00   ❤️ 1
    @zhuzeitou haha. 这道题目准备到此为止了。
    我又发现一篇文章不错: https://dave.cheney.net/practical-go/presentations/gophercon-singapore-2019.html
    crella
        125
    crella  
       2019-11-10 11:54:30 +08:00
    自己改了下算法,同环境原 node 算法 4200ms,原 ruby 算法太久了测不出。改良 ruby 算法 8525ms (从 8900 优化成 8500 真是伤透了心),不会 javascript。

    https://paste.ubuntu.com/p/g7XWqJGRxp/
    zjsxwc
        126
    zjsxwc  
    OP
       2020-11-27 11:36:18 +08:00
    今天 php8.0.0 发布,我在同一台电脑里测试同样的代码,变成 19 秒了
    zjsxwc
        127
    zjsxwc  
    OP
       2020-11-27 14:27:27 +08:00
    由于 javascript nodejs 不能用 int64 所以没有意义,我就不写了


    在 int64 的情况下,求第 300000 个质数耗时

    python2.7 65.9 秒
    python3.8 62.4 秒
    ruby2.7 28.3 秒
    php7.2 22 秒
    php7.4 20 秒
    php8.0 19 秒

    golang 10 秒
    c++ 9.4 秒
    纯 c 9.2 秒
    java 8.8 秒
    zjsxwc
        129
    zjsxwc  
    OP
       2020-11-27 15:16:18 +08:00
    zjsxwc
        130
    zjsxwc  
    OP
       2020-11-28 09:02:06 +08:00
    zjsxwc
        131
    zjsxwc  
    OP
       2020-11-28 09:09:12 +08:00
    @zjsxwc #124 的 php8 19 秒是没开启 jit 成绩,开启 jit 后 php8 达到 14.9 秒,如图

    https://i.loli.net/2020/11/28/XhekKo1gyzvTHF6.png



    由于 javascript nodejs 不能用 int64 所以没有意义,我就不写了


    在 int64 的情况下,求第 300000 个质数耗时

    python2.7 65.9 秒
    python3.8 62.4 秒
    ruby2.7 28.3 秒
    php7.2 22 秒
    php7.4 20 秒
    php8.0 19 秒
    php8.0 开启 jit 14.9 秒

    golang 10 秒
    c++ 9.4 秒
    纯 c 9.2 秒
    java 8.8 秒
    zjsxwc
        132
    zjsxwc  
    OP
       2020-11-28 09:17:37 +08:00
    编译 php8.0 开启 jit 的指令

    下载 php8 源代码后解压进目录
    ./configure --prefix=/home/wangchao/Develop/php8.0.0 --enable-opcache
    make install
    cp ./php.ini-production /home/wangchao/Develop/php8.0.0/lib/php.ini
    把 php.ini 里 zend_extension=opcache 前的分号删掉

    开 jit 跑 php 测试脚本
    /home/wangchao/Develop/php8.0.0/bin/php -dopcache.enable_cli=1 -dopcache.jit_buffer_size=100M -dopcache.jit=1255 test.php
    zjsxwc
        133
    zjsxwc  
    OP
       2020-11-28 16:37:00 +08:00
    pypy 可以达到 10 秒 老牌 jit 牛逼
    zjsxwc
        134
    zjsxwc  
    OP
       2021-11-26 08:27:26 +08:00
    使用 kphp 把 php 编译成独立二进制可执行文件,在同一台机器上算质数,耗时 5 秒,比 golang 快

    root@caaa15c3621c:/tmp/dev# ./kphp_out/cli
    第 300000 个素数的值是:4256233 耗时 5351 毫秒 root@caaa15c3621c:/tmp/dev# ls PN.php pnmain.php
    PN.php pnmain.php

    源码:
    https://paste.ubuntu.com/p/FfZx5bjR3f/
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5448 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 09:14 · PVG 17:14 · LAX 01:14 · JFK 04:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.