该帖已经被评为良好帖
|
|
---|---|
作者 | 正文 |
发表时间:2007-08-01
mryufeng 写道 erts的1xw代码俺基本上已经读过一遍了 erlang的emulator实现总体来讲效能还是蛮高的 个人感觉erlang的优势在于dist和得益于fp语言特性的lockfree 和code hot replacement.
对于Http这种应用一收一发,大不了搞搞Cache,逻辑简单,C++会有优势.但是一旦逻辑复杂起来,比如很多电信应用里面,一个客户端连上来需要数个服务器同时协调提供服务.ACE那种,异步方式处理网络IO的C++框架,处理这种复杂逻辑就非常困难,不但难以调试和扩展,而且本身就会造成性能下降.在可伸缩性上,C/C++ 的实现更无法自然地扩展到集群上去. |
|
返回顶楼 | |
发表时间:2007-08-01
Trustno1 写道 ACE那种,异步方式处理网络IO的C++框架,处理这种复杂逻辑就非常困难,不但难以调试和扩展,而且本身就会造成性能下降.在可伸缩性上,C/C++ 的实现更无法自然地扩展到集群上去.
用C++最终也能实现出来,只不过最后可能重新实现了一个erlang核心,而且调用界面反而不如erlang美观。。我看C++的各种完成并行计算或分布式的库甚至是编译器扩展,从语法上来看都比较丑陋,代码风格上也截然不同。 C++好像没有一个框架同时支持并发/并行/分布,把几个框架凑到一块的难度也不小。 |
|
返回顶楼 | |
发表时间:2007-08-01
qiezi 写道 Trustno1 写道 ACE那种,异步方式处理网络IO的C++框架,处理这种复杂逻辑就非常困难,不但难以调试和扩展,而且本身就会造成性能下降.在可伸缩性上,C/C++ 的实现更无法自然地扩展到集群上去.
用C++最终也能实现出来,只不过最后可能重新实现了一个erlang核心,而且调用界面反而不如erlang美观。。我看C++的各种完成并行计算或分布式的库甚至是编译器扩展,从语法上来看都比较丑陋,代码风格上也截然不同。 C++好像没有一个框架同时支持并发/并行/分布,把几个框架凑到一块的难度也不小。 什么时候向编程语言里加入新概念取决于“创造性扩展原则”:当程序出于技术原因变得过度复杂,但增加的复杂度和要解决的问题无关时,就可以引入新的概念了。引入的新概念应该简化程序。 |
|
返回顶楼 | |
发表时间:2007-08-01
其实我要说的不是erlang的性能差或者不好 我个人对这个东西非常着迷 做了多年的网络程序 突然发现 erlang该有的东西都差不多有了。我要说的是 任何程序其实都有改进的余地 不要盲目相信。用profile工具收集证据 接着优化流程 优化代码,持续改进,才能把东西做成精品。
|
|
返回顶楼 | |
发表时间:2007-08-01
个人比较倾向于赞同 mryufeng 同学“质疑-询证”的独立思考与严谨求证态度。
我认为这里是两个问题: A,以 c 的视角来看 erlang 的网络底层代码,在性能上有进一步提高的空间。profile 结果表明了这一点。 B,erlang 在并发/分布的高度复杂问题上提供了目前仅见的统一而且优雅的一揽子解决方案。这似乎是大家的共识。 我想这两个问题之间,并不存在非此即彼的逻辑。 更进一步, mryufeng 有难得的 c 网络编程视角与丰富的实践经验,我建议你继续深入 hack erlang 的源码,从性能角度优化 erlang 这部分 c 代码(比如,将 yaws 的 ab 测试的数据从 c3k 提升到 c100k 的性能水平?)。优化的成果,可以做成 patch 提交到 erlang-patch maillist。 倘若能够如此,将会是为整个 erlang 社区做出的巨大贡献。 |
|
返回顶楼 | |
发表时间:2007-08-01
谢谢jackz的鼓励,俺一直在hack erlang这条路上work hard.
|
|
返回顶楼 | |
发表时间:2007-08-01
jackyz 写道 个人比较倾向于赞同 mryufeng 同学“质疑-询证”的独立思考与严谨求证态度。
我认为这里是两个问题: A,以 c 的视角来看 erlang 的网络底层代码,在性能上有进一步提高的空间。profile 结果表明了这一点。 这个帖子实际上有几个问题. bottle neck定位的问题.mryufeng 用profile 工具dump出来的information,的确有很大的价值.可惜他对bottle neck的定位并不准确.我手上没有什么profile工具,仅凭逻辑上的推断和ultraedit的search基本上可以确定,strace 出来的结果肯定不是Erlang以及OPT本身的问题. erlang 所有的socket api的封装都是在kernel下的inet module里面,getopts,setopts和recv,send这些函数是分开实现的.erlang底层的代码就没有必要把recv和send里面再加入getopts的代码.有能力设计runtime库的作者都不会犯这种低级错误. 在inet上面opt又封装了一层gentcp.gentcp的recv,send唯一一个多余的操作是去inet_db去lookup Socket.对于正常的anscyn的网络程序管理一个socket queue这点恐怕也是逃不掉的. 那么唯一的那些耗时操作是yaws代码: %% ret: continue | done 'GET'(CliSock, Req, Head) -> SC=get(sc), ok = yaws:setopts(CliSock, [{packet, raw}, binary], is_ssl(SC#sconf.ssl)), flush(CliSock, Head#headers.content_length), ARG = make_arg(CliSock, Head, Req, undefined), handle_request(CliSock, ARG, 0). 这个至多是说yaws实现的问题,和Erlang本身无关. 关于Cache,Yaws的文档Chapter3里面明确写明了是支持Cache的 引用 YAWS acts very much like any regular webserver while delivering static pages.By default YAWS will cache
static content in RAM.The caching behavior is controlled by a number of global configuration directives. Since the RAM caching occupies memory,it maybe interesting to tweak the default values for the caching directives or even to turn it off completely. The following configuration directives control the caching behavior * max_num_cached_files=Integer YAWS will cache small files such as commonly accessed GIF images in RAM.This directives ets a maximum number on the number of cached files.The default value is 400. * max_num_cached_bytes=Integer This directive controls the total amount of RAM which can maxi- mally be used for cached RAM files.The default value is 1000000,1 mega byte. * max_size_cached_file=Integer This directive sets a maximum size on the files that are RAM cached by YAWS . The default value i 8000,8 batters. It maybe considered to be confusing,but the numbers specified in the above mentioned cache directives are local to each server.Thus if we have specified max_num_cached_bytes=1000000 and have defined 3 servers,we mayactually use 3*1000000bytes. |
|
返回顶楼 | |
发表时间:2007-08-01
Trustno1 写道 这个至多是说yaws实现的问题,和Erlang本身无关. 我测试的结果,应该就是erlang底层实现问题,连接建立时都会有这种调用,对于长连接来说这个影响倒并不是很大: $ strace -p xxxxx -T ..... clock_gettime(CLOCK_MONOTONIC, {1287762, 170939432}) = 0 <0.000008> gettimeofday({1185949291, 63789}, NULL) = 0 <0.000007> accept(8, {sa_family=AF_INET, sin_port=htons(64671), sin_addr=inet_addr("127.0.0.1")}, [16]) = 11 <0.000013> fcntl64(11, F_GETFL) = 0x2 (flags O_RDWR) <0.000008> fcntl64(11, F_SETFL, O_RDWR|O_NONBLOCK) = 0 <0.000008> getsockopt(8, SOL_TCP, TCP_NODELAY, [1], [4]) = 0 <0.000009> getsockopt(8, SOL_SOCKET, SO_KEEPALIVE, [0], [4]) = 0 <0.000009> getsockopt(8, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0 <0.000007> getsockopt(8, SOL_IP, IP_TOS, [0], [4]) = 0 <0.000009> getsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0 <0.000008> getsockopt(11, SOL_IP, IP_TOS, [0], [4]) = 0 <0.000008> setsockopt(11, SOL_IP, IP_TOS, [0], 4) = 0 <0.000008> setsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], 4) = 0 <0.000011> getsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0 <0.000007> getsockopt(11, SOL_IP, IP_TOS, [0], [4]) = 0 <0.000008> setsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], 4) = 0 <0.000007> getsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0 <0.000008> getsockopt(11, SOL_IP, IP_TOS, [0], [4]) = 0 <0.000008> setsockopt(11, SOL_SOCKET, SO_KEEPALIVE, [0], 4) = 0 <0.000008> setsockopt(11, SOL_IP, IP_TOS, [0], 4) = 0 <0.000007> setsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], 4) = 0 <0.000008> getsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], [4]) = 0 <0.000007> getsockopt(11, SOL_IP, IP_TOS, [0], [4]) = 0 <0.000008> setsockopt(11, SOL_TCP, TCP_NODELAY, [1], 4) = 0 <0.000008> setsockopt(11, SOL_SOCKET, SO_PRIORITY, [0], 4) = 0 <0.000008> clock_gettime(CLOCK_MONOTONIC, {1287762, 172860432}) = 0 <0.000008> accept(8, 0xbffff1c0, [28]) = -1 EAGAIN (Resource temporarily unavailable) <0.000010> recv(11, 0xb7cfb970, 4, 0) = -1 EAGAIN (Resource temporarily unavailable) <0.000009> clock_gettime(CLOCK_MONOTONIC, {1287762, 173046432}) = 0 <0.000008> clock_gettime(CLOCK_MONOTONIC, {1287762, 173098432}) = 0 <0.000008> epoll_ctl(3, EPOLL_CTL_DEL, 8, {EPOLLIN, {u32=8, u64=8589934600}}) = 0 <0.000010> epoll_ctl(3, EPOLL_CTL_ADD, 8, {EPOLLIN, {u32=8, u64=8589934600}}) = 0 <0.000009> epoll_ctl(3, EPOLL_CTL_ADD, 11, {EPOLLIN, {u32=11, u64=8589934603}}) = 0 <0.000012> epoll_wait(3, {}, 256, 3000) = 0 <2.999371> 我的测试程序是一个进程accept,收到一个就建立一个新进程去recv,上面这个过程就是telnet上去什么也不做抓下来的。 |
|
返回顶楼 | |
发表时间:2007-08-01
是OTP库问题不是erlang本身的问题.
问题应该出在prim_inet里面. accept0(L, Time) when port(L), integer(Time) -> case async_accept(L, Time) of {ok, Ref} -> receive {inet_async, L, Ref, {ok,S}} -> accept_opts(L, S); {inet_async, L, Ref, Error} -> Error end; Error -> Error end. %% setup options from listen socket on the connected socket accept_opts(L, S) -> case getopts(L, [active, nodelay, keepalive, delay_send, priority, tos]) of {ok, Opts} -> case setopts(S, Opts) of ok -> {ok, S}; Error -> close(S), Error end; Error -> close(S), Error end. 不过你应该不用改动代码,直接使用async_accept/2即可跳过这段. |
|
返回顶楼 | |
发表时间:2007-08-01
不单单是 setsockopt 或者 getsockopt有问题 这个倒好 因为是一次性的开销 毕竟一个连接才做一次 你看下clock_gettime epoll_ctl也是无厘头的调用,这也问题不是很大,最大的问题是 read write反复调用 这个才是大头 不过这个也可以用delay_send option来避免。 所有这个erlang是有很多细节可以微调的,只有把这些问题都处理掉了,性能才会提升。
|
|
返回顶楼 | |