论坛首页 综合技术论坛

质疑 apache和yaws的性能比较(必看)

浏览 59881 次
该帖已经被评为良好帖
作者 正文
   发表时间:2007-07-31  
http://www.sics.se/~joe/apachevsyaws.html 上比较yaws的性能 显示apache 4000就挂了 但是yaws 8w还在挺着。
见附件的图

但是你仔细看下他的测试方式

What do we measure and how?

We use a 16 node cluster running at SICS. We plot throughput vs. parallel load.

  • Machine 1 has a server (Apache or Yaws).
  • Machine 2 requests 20 KByte pages from machine 1. It does this in tight a loop requesting a new page as soon as it has received a page from the server. From this we derive a throughput figure, which is plotted in the horizontal scale on the graph. A typical value (800) means the throughput is 800 KBytes/sec.
  • Machines 3 to 16 generate load.

    Each machine starts a large number of parallel sessions.

    Each session makes a very slow request to fetch a one byte file from machine 1. This is done by sending very slow HTTP GET requests (we break up the GET requests and send them character at a time, with about ten seconds between each character)

这个比较是非常不公平的

apache的链接处理机制是 开线程或者进程来处理请求 按它的测试方法 你非常慢速的8w请求 导致apache开大量的线程来处理。而能开多少线程取决于操作系统的能力 这还是其次 大量的线程处理活跃的链接导致大量的thread content switch。 apache 挂了不奇怪。 而erlang的线程相大于c语言的一个数据结构 erl_process你开多少取决于你的内存 大量的但是慢速的链接刚好适合poll事件dispatch, 以epoll的能力(俺测试过epoll30w)能够轻松处理。 这个测试与其说测试web服务器的性能 不如说 测试服务器的进程生成能力。

俺的测试是这样的:.
./yaws --conf yaws.conf --erlarg "+K true +P 1024000"     #epoll 最多1024000个进程 内核都已经调优过

yaws.conf 的内容:

auth_log = false
max_num_cached_files = 8000
max_num_cached_bytes = 6000000

<server test_yaws=""></server>

大家都用 ab -c 1000 -n 1000000 http://192.168.0.98:8000/bomb.gif 来测
果然发现yaws的性能也是非常一般 大概也就是3K左右.

各位看下 strace 的结果就知道了:

accept(10, {sa_family=AF_INET, sin_port=htons(5644), sin_addr=inet_addr("192.168.0.97")}, [16]) = 11
fcntl64(11, F_GETFL)                    = 0x2 (flags O_RDWR)
fcntl64(11, F_SETFL, O_RDWR|O_NONBLOCK) = 0
getsockopt(10, SOL_TCP, TCP_NODELAY, [0], [4]) = 0
getsockopt(10, SOL_SOCKET, SO_KEEPALIVE, [0], [4]) = 0
getsockopt(10, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0
getsockopt(10, SOL_IP, IP_TOS, [0], [4]) = 0
getsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0
getsockopt(11, SOL_IP, IP_TOS, [0], [4]) = 0
setsockopt(11, SOL_IP, IP_TOS, [0], 4)  = 0
setsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], 4) = 0
getsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0
getsockopt(11, SOL_IP, IP_TOS, [0], [4]) = 0
setsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], 4) = 0
getsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0
getsockopt(11, SOL_IP, IP_TOS, [0], [4]) = 0
setsockopt(11, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0
setsockopt(11, SOL_IP, IP_TOS, [0], 4)  = 0
setsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], 4) = 0
getsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0
getsockopt(11, SOL_IP, IP_TOS, [0], [4]) = 0
setsockopt(11, SOL_TCP, TCP_NODELAY, [0], 4) = 0
setsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], 4) = 0
recv(11, "GET /bomb.gif HTTP/1.0\r\nUser-Age"..., 8192, 0) = 100
getpeername(11, {sa_family=AF_INET, sin_port=htons(5644), sin_addr=inet_addr("192.168.0.97")}, [16]) = 0
clock_gettime(CLOCK_MONOTONIC, {110242, 326908594}) = 0
stat64("/var/www/html/bomb.gif", {st_mode=S_IFREG|0644, st_size=4096, ...}) = 0
access("/var/www/html/bomb.gif", R_OK)  = 0
access("/var/www/html/bomb.gif", W_OK)  = 0
clock_gettime(CLOCK_MONOTONIC, {110242, 327135982}) = 0
time(NULL)                              = 1185894828
clock_gettime(CLOCK_MONOTONIC, {110242, 327222643}) = 0
stat64("/etc/localtime", {st_mode=S_IFREG|0644, st_size=405, ...}) = 0
writev(11, [{NULL, 0}, {"HTTP/1.1 200 OK\r\nConnection: clo"..., 231}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...
, 4096}], 3) = 4327
close(11

这里面充斥着大量的无用的昂贵的系统调用 (至少有20个*10us = 200us 的系统调用是无效的)
对文件的access 2 次  连文件的cache都没有  每次 打开文件  读文件 然后写到socket去 。

这个case是小文件(4k)的情况。 看下大文件(40k)的情况

open("/var/www/html/bomb.gif", O_RDONLY|O_LARGEFILE) = 19
read(19, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 10240) = 10240
writev(16, [{NULL, 0}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 10240}], 2) = 10240
read(19, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 10240) = 10240
writev(16, [{NULL, 0}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 10240}], 2) = 10240
read(19, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 10240) = 10240
writev(16, [{NULL, 0}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 10240}], 2) = 10240
read(19, "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 10240) = 10240
writev(16, [{NULL, 0}, {"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"..., 10240}], 2) = 7240
read(19, "", 10240)                     = 0
close(19)                               = 0
clock_gettime(CLOCK_MONOTONIC, {110574, 856508319}) = 0
epoll_ctl(3, EPOLL_CTL_DEL, 11, {0, {u32=11, u64=581990243524149259}}) = 0
epoll_ctl(3, EPOLL_CTL_DEL, 12, {0, {u32=12, u64=581990243524149260}}) = 0
epoll_ctl(3, EPOLL_CTL_ADD, 16, {EPOLLOUT, {u32=16, u64=581990243524149264}}) = 0
epoll_wait(3, {}, 256, 0)               = 0
clock_gettime(CLOCK_MONOTONIC, {110574, 856677411}) = 0
clock_gettime(CLOCK_MONOTONIC, {110574, 856729274}) = 0

大量的epoll_ctl 调用 clock_gettime的调用 足够让系统的速度变的非常慢。


比对下lighttpd的性能。 lighttpd用到了cache,用到了aio,还是完全用c语言小心编写, 他处理小文件大概是并发1w.  而yaws这个的处理方式打个3折我看差不多。

所以请各位大佬介绍erlang的性能时候不要 再用这个apache vs yaws的例子了 误导太多人了.

 

  • 大小: 55.2 KB
   发表时间:2007-07-31  
这么说 Erlang 是自己实现了线程机制, 自己调度的哦. 容量比操作系统提供的线程机制要大, 结果是并发数量高了不少, 不过单线程的效率注定要低些.

但是 Erlang 的线程调度应该和 CPU 硬件联系不紧密吧, 给一个比较高层次的语言实现一个软件线程机制确实相对要容易, 而且目前 CPU核心 还不是很多的情况下可能也能占不少便宜. 不过以后CPU核心数到上千的时候, 我觉得可能就比不过操作系统线程了.

但是目前 内存容量/CPU核心数 这个比例还是比较大, 严重并发的场合Erlang这种机制应该还是有用武之地吧.
0 请登录后投票
   发表时间:2007-07-31  
它说的是并发能力而不是性能,它的测试条件也很明确,16台机器,第1台跑WEB服务器,第2台用来做测试,其它服务器用来建立大量连接,但只是“makes a very slow request to fetch a one byte file from machine 1”,这就是故意造成高并发,但不是测试性能。在大量并发情况下,测试第2台读取页面的响应情况,这个图就是说明这个的,图上可以表明,erlang的软实时做得还是不错的,大量进程没有严重影响到单个进程,这个数量自己用线程+进程都是难以达到的。

erlang的优势就是高并发而非高性能,性能方面谁敢跟C比,erlang也是C写的内核。
0 请登录后投票
   发表时间:2007-07-31  
erlang根本没有进程机制 就是简单的erl_process 结构 里面有message queue 在epoll返回的时候 把socket来的数据变形 然后放到process的queue里面去  再来个循环出来process里面的message而已。 和普通的event dispatch程序(如用libevent)的没有区别。
0 请登录后投票
   发表时间:2007-07-31  
这样的并发俺的event dispatch的程序 单进程 单线程也能轻松达到10w哦
0 请登录后投票
   发表时间:2007-08-01  
erts的1xw代码俺基本上已经读过一遍了 erlang的emulator实现总体来讲效能还是蛮高的 个人感觉erlang的优势在于dist和得益于fp语言特性的lockfree 和code hot replacement.
0 请登录后投票
   发表时间:2007-08-01  
mryufeng 写道
这样的并发俺的event dispatch的程序 单进程 单线程也能轻松达到10w哦


即使是并发,erlang也不能和C相比,毕竟是C实现的。你自己实现这样一个当然是可以,不过如何把文件、数据库、网络调用都纳入到一个统一的框架中?erlang相当于完成了一个异步调用框架,所有符合它的规范的都可以融为一体,最终形成一个一致的界面。比如一个数据库调用模块,用C NODE方式,为了性能不受网络影响太大,需要使用一个线程池来完成调用。以后该数据库的接口升级了,支持异步调用了,就可以单个线程完成,对于这个C程序来说,修改相对比较小,因为它不面向具体业务。对于erlang来说,调用方式也没有改变,这正是最理想的方式嘛,想了N久没能力搞出来的东西。完全用erlang做项目我目前感觉不是特别必要,看中的是它的分布式和热代码替换。

对了,你上面strace抓的也太恐怖了,这么多重复的调用?很多调用看参数根本都是可以省略的,比如:
setsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], 4) = 0
getsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0

有种想扁人的感觉。。
0 请登录后投票
   发表时间:2007-08-01  
erlang的driver机制还是单线程的event dispatch机制 只有在具体的driver使用async的时候才用线程池(也就是+A n)来辅助处理。async还是后来引进来的 所以erlang的emulator基本上就是一个事件反转的调用框架, 和 ACE和Reactor概念上是一样的。其实这个东西ACE作的也不错 但是ACE的抽象原语偏低,使用起来容易出错。
0 请登录后投票
   发表时间:2007-08-01  
呼呼,有个文章说yaws做得还不够好

性能方面,俄罗斯的nginx服务器我自己测试下来是现在最快的,静态文件性能有apache2的两倍。有趣的是nginx也支持热配置更新和本身代码热更新,做法和erlang差不多:给master进程发信号说要升级拉,还在工作的worker继续,但别接新任务了等做完再更新,现在发呆着的worker马上更新然后等着去接新任务。

BTW, 楼主要小心别沾染标题党的“必看”恶习呵呵
0 请登录后投票
   发表时间:2007-08-01  
mryufeng 写道
erlang根本没有进程机制 就是简单的erl_process 结构

说到底,操作系统所谓的Process和Thread 也只不过是一个Context结构,用指针保存每个Stack Frame,然后按时间片切换Context.有任何区别吗?
用最简单的stack_t ,sigaltstack和longjmp 就能做一个最简单的micro thread.

0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics