见识Erlang网络IO能力

原创
2011/11/03 18:06
阅读数 5.6K

顿悟,之前浪费了大把光阴。。痛惜!

决定落户到oschina了,支持开源。先贴上CSDN已发表过的。

测试目的:比较c,c++,node.js,erlang网络TCP读写性能

测试环境:linux

测试条件:Server为简单的TCP echo服务器,Server和Client运行在同一机器上,10000长连接,100活动连接

测试结果:

c++ boost::asio
connect=10000,active connect=100,req=148791,time=60,req/sec=2479.85,msec/req=40.343
erlang  kernel-poll false
connect=10000,active connect=100,req=979803,time=60,req/sec=16330,msec/req=6.12356
node.js
connect=10000,active connect=100,req=1378370,time=60,req/sec=22972.8,msec/req=4.35543
c libevent
connect=10000,active connect=100,req=3719106,time=60,req/sec=61985.1,msec/req=1.61258
erlang kernel-poll true
connect=10000,active connect=100,req=6377574,time=60,req/sec=106293,msec/req=0.939882

源代码见下:

asio_echo_server.cpp

#include <cstdlib>
#include <iostream>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/thread/thread.hpp>

using boost::asio::ip::tcp;

int total_conn = 0;

class session
{
public:
  session(boost::asio::io_service& io_service)
    : socket_(io_service)
  {
  }

  tcp::socket& socket()
  {
    return socket_;
  }

  void start()
  {
    socket_.async_read_some(boost::asio::buffer(data_, max_length),
        boost::bind(&session::handle_read, this,
          boost::asio::placeholders::error,
          boost::asio::placeholders::bytes_transferred));
  }

private:
  void handle_read(const boost::system::error_code& error,
      size_t bytes_transferred)
  {
    if (!error)
    {
      boost::asio::async_write(socket_,
          boost::asio::buffer(data_, bytes_transferred),
          boost::bind(&session::handle_write, this,
            boost::asio::placeholders::error));
    }
    else
    {
      delete this;
    }
  }

  void handle_write(const boost::system::error_code& error)
  {
    if (!error)
    {
      socket_.async_read_some(boost::asio::buffer(data_, max_length),
          boost::bind(&session::handle_read, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
    }
    else
    {
      delete this;
    }
  }

  tcp::socket socket_;
  enum { max_length = 1024 };
  char data_[max_length];
};

class server
{
public:
  server(boost::asio::io_service& io_service, short port)
    : io_service_(io_service),
      acceptor_(io_service, tcp::endpoint(tcp::v4(), port))
  {
    start_accept();
  }

private:
  void start_accept()
  {
    session* new_session = new session(io_service_);
    acceptor_.async_accept(new_session->socket(),
        boost::bind(&server::handle_accept, this, new_session,
          boost::asio::placeholders::error));
  }

  void handle_accept(session* new_session,
      const boost::system::error_code& error)
  {
    start_accept();

    if (!error)
    {
      std::cout << "total connect =" << ++total_conn <<std::endl;
      new_session->start();
    }
    else
    {
      delete new_session;
    }
  }

  boost::asio::io_service& io_service_;
  tcp::acceptor acceptor_;
};

int main(int argc, char* argv[])
{
  try
  {
    if (argc < 2)
    {
      std::cerr << "Usage: async_tcp_echo_server <port>\n";
      return 1;
    }

    boost::asio::io_service io_service;

    using namespace std; // For atoi.
    server s(io_service, atoi(argv[1]));

    int thread_num = 6;
    if (argc > 2)
	  thread_num = atoi(argv[2]);

    boost::thread_group th_group; 
    for (int i=0; i<thread_num; ++i)
    {
	  th_group.add_thread(new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service)));
    }

    th_group.join_all();
  }
  catch (std::exception& e)
  {
    std::cerr << "Exception: " << e.what() << "\n";
  }

  return 0;
}

libevent-echo-server.c

#include <stdlib.h>
#include <unistd.h>
#include <netinet/in.h>  
#include <sys/socket.h>  
#include <sys/types.h>  
#include <sys/socket.h>
#include <event.h>  
#include <stdio.h>  
#include <time.h> 
#include <string.h>
#include <fcntl.h>

int buf_len = 8192;
int msg_len = 4096; 
int total = 0;

int setnonblock(int fd)
{       
	int flags;       
	flags = fcntl(fd, F_GETFL);       
	if (flags < 0)               
		return flags;       
	flags |= O_NONBLOCK;       
	if (fcntl(fd, F_SETFL, flags) < 0)               
		return -1;       
 
	return 0;
}

void connection_echo(int fd, short event, void *arg)
{
	struct event *ev = (struct event *)arg;
	event_add(ev, NULL);

	char buf[buf_len];
	int read_len = read(fd, buf, msg_len);
	write(fd, buf, read_len);
}

void connection_accept(int fd, short event, void *arg)   
{ 
    /* for debugging */ 
    fprintf(stderr, "%s(): fd = %d, event = %d,	total = %d.\n", __func__, fd, event, ++total);  

    /* Accept a new connection. */ 
    struct sockaddr_in s_in;  
    socklen_t len = sizeof(s_in);  
    int ns = accept(fd, (struct sockaddr *) &s_in, &len);  
    if (ns < 0) {  
        perror("accept");  
        return;  
    }  

	setnonblock(ns);

    /* Install echo server. */ 
    struct event *ev = (struct event *)malloc(sizeof(struct event));  
    event_set(ev, ns, EV_READ, connection_echo, ev);  
    event_add(ev, NULL);  
} 

int main(void)  
{  
    /* Request socket. */ 
    int s = socket(PF_INET, SOCK_STREAM, 0);  
    if (s < 0) {  
        perror("socket");  
        exit(1);  
    }  

    /* bind() */ 
    struct sockaddr_in s_in;  
    memset(&s_in, 0, sizeof(s_in));  
    s_in.sin_family = AF_INET;  
    s_in.sin_port = htons(9000);  
    s_in.sin_addr.s_addr = INADDR_ANY;  
    if (bind(s, (struct sockaddr *) &s_in, sizeof(s_in)) < 0) {  
        perror("bind");  
        exit(1);  
    }  

    /* listen() */ 
    if (listen(s, 1000) < 0) {  
        perror("listen");  
        exit(1);  
    }  

    /* Initial libevent. */ 
    event_init();  

    /* Create event. */ 
    struct event ev;  
    event_set(&ev, s, EV_READ | EV_PERSIST, connection_accept, &ev);  

    /* Add event. */ 
    event_add(&ev, NULL);  

    event_dispatch();  

    return 0;  
}

erlang-echo-server.erl

-module(echo_server).
-export([start/0]).

start() ->
        {ok, Listen} = gen_tcp:listen(9000, [binary,
                                                %{packet, 4},
                                                {reuseaddr, true},
                                                {backlog, 2000},
                                                {active, true}]),
        spawn(fun() -> par_connect(Listen, 0) end).

par_connect(Listen, Count) ->
        {ok, Socket} = gen_tcp:accept(Listen),
        New = Count + 1,
        io:format("Accept succ ~p~n", [New]),
        spawn(fun() -> par_connect(Listen, New) end),
        loop(Socket).


loop(Socket) ->
    receive
        {tcp, Socket, Bin} ->
            gen_tcp:send(Socket, Bin),
            loop(Socket);
        {tcp_closed, Socket} ->
            io:format("Server socket closed~n")
    end.

node-echo-server.js

var net = require('net');
var cnt = 0;
var server = net.createServer(function (socket) {
  socket.pipe(socket);
});

server.listen(9000, "127.0.0.1");

console.log('Server running at 127.0.0.1:9000');

展开阅读全文
打赏
0
7 收藏
分享
加载中
erlang使用的是erlang虚拟机里自己实现的用户态线程,当然高效了,其他几种的线程在linux上本质就是进程,创建进程的效率当然低了。
这种测试有失偏颇,应该加上I/O吞吐率的测试。
语言不是解决问题的关键,合理利用OS的机制以及各种库才是关键。
2013/10/08 17:04
回复
举报
你是来黑C++的吧
2013/10/01 00:32
回复
举报
请问测试结果中的数据是用什么方法测出来的?每个实现有自己的数据收集方法,还是用了通用的测试工具?
谢谢!
2013/07/23 13:08
回复
举报
yak

引用来自“hekd”的评论

asio那个例子是半双工的,其他都是全双工的,能这样比吗,不要误导大家?

有没有全双式的例子,我再测试一下
2013/03/17 23:21
回复
举报
asio那个例子是半双工的,其他都是全双工的,能这样比吗,不要误导大家?
2013/03/15 17:50
回复
举报
请问测试结果中的数据是用什么方法测出来的?每个实现有自己的数据收集方法,还是用了通用的测试工具?
谢谢!
2013/02/10 14:45
回复
举报
逍遥客博主

引用来自“337240552”的评论

哪位兄弟告诉下 用什么工具发起大量到连接呢 lr?

自己写程序
2012/10/18 16:10
回复
举报
哪位兄弟告诉下 用什么工具发起大量到连接呢 lr?
2012/10/12 20:25
回复
举报
逍遥客博主

引用来自“wangyongcun”的评论

有人跟进测试吗 asio和erlang能差60倍? 不大可能

我也为此纳闷。但最终我选择的libevent。另:thrift的c++实现,对 nonblocing server的封装也是用的libevent,测试下来性能也不错。
2012/08/06 15:56
回复
举报
有人跟进测试吗 asio和erlang能差60倍? 不大可能
2012/08/06 15:49
回复
举报
更多评论
打赏
14 评论
7 收藏
0
分享
返回顶部
顶部