Article:
Erlang vs. Ruby: So sánh tốc độ truyền thông điệp giữa các process
1097
ngocdaothanh.myopenid.com 172Updated over 3 years ago |
RPC có một số hạn chế. Truyền thông điệp là cách tốt hơn, đã được Erlang chứng minh hiệu quả. Bài viết này thử nghiệm và so sánh một số cách thực hiện của của Erlang và Ruby.

Môi trường thử nghiệm
Ta viết chương trình echo đơn giản. Process client gửi khoảng 100 byte (vì cần serialize và deserialize, nên không thể xác định chính xác) đến process server. Server khi nhận được thì gửi trả lại ngay cho client.
Về nguyên tắc, 2 process này có thể nằm trên 2 máy khác nhau. Để đơn giản, ta chỉ chạy trên 1 máy:
- MacBook Pro 2.4 GHz Intel Core 2 Duo
- Memory 2 GB 667 MHz DDR2 SDRAM
- Mac OS X Leopard 10.5.5
- Erlang 5.6.3
- Ruby 1.8.7 và 1.9.0
Erlang - !
Trước tiên ta dùng cách đơn giản nhất là cứ thế mà lôi process ID ra mà gọi.
Server:
-module(server).
-export([start_link/0]).
start_link() ->
global:register_name(server, self()),
loop().
loop() ->
receive
{Data, Pid} ->
Pid ! Data,
loop();
_Any ->
loop()
end.
Client:
-module(client).
-export([test/1]).
test(N) ->
T1 = now(),
Data = build_data(100, <<>>),
call(Data, N, T1, N).
build_data(0, Acc) ->
Acc;
build_data(Len, Acc) ->
Byte = random:uniform(256) - 1,
build_data(Len - 1, <<Byte, Acc/binary>>).
call(_Data, N, T1, 0) ->
{_, Sec2, Micro2} = now(),
{_, Sec1, Micro1} = T1,
Dt = (Sec2 + Micro2/1000000) - (Sec1 + Micro1/1000000),
io:format("N = ~p, Dt = ~p (~p)~n", [N, Dt, N/Dt]);
call(Data, N, T1, Current) ->
global:send(server, {Data, self()}),
receive
_Any ->
ok
end,
call(Data, N, T1, Current - 1).
Cách chạy:
- Chạy server node: erl -sname server@localhost -setcookie Test
- Chạy client node: erl -sname client@localhost -setcookie Test
- Từ client node thông đến server node: net_adm:ping(server@localhost)
- Ping thử 10^5 lần: client:test(100000).
Kết quả cho thấy tốc độ đạt khoảng 8500 lần/s.
Erlang - gen_server
Khi viết chương trình một cách nghiêm túc, không thể không dùng OTP.
Server:
-module(server).
-behaviour(gen_server).
-export([start_link/0]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
-record(state, {}).
start_link() ->
gen_server:start_link({global, server}, ?MODULE, [], []).
init([]) ->
{ok, #state{}}.
handle_call(Request, _From, State) ->
Reply = Request,
{reply, Reply, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVsn, State, _Extra) ->
{ok, State}.
Client:
Gần giống như trên, chỉ cần sửa đoạn
call(Data, N, T1, Current) ->
global:send(server, {Data, self()}),
receive
_Any ->
ok
end,
call(Data, N, T1, Current - 1).
thành
call(Data, N, T1, Current) ->
gen_server:call({global, server}, Data),
call(Data, N, T1, Current - 1).
Cách chạy như cũ, kết quả cho thấy tốc độ đạt khoảng 5000-6000 lần/s. Khi đổi gen_server:call thành gen_server:cast, tốc độ tăng lên khoảng 10 lần (hiển nhiên
).
Ruby - revent
Ruby có sẵn DRuby, nhưng nó là RPC. Ta dùng revent, thư viện dựa trên thư viện cực nổi tiếng EventMachine. Thư viện này đã có sẵn ví dụ echo, chỉ việc chạy, khoẻ
.
Kết quả cho thấy tốc độ đạt khoảng 7000 và 11500 lần/s lần với lần lượt Ruby 1.8.7 và 1.9.0.
Kết luận
Đồ thị ở đầu bài tạo nhờ trang Create A Graph.
Ruby 1.9 quả đáng mặt anh hùng
. Nếu chỉ xét tốc độ thì Ruby nhỉnh hơn. Nhưng nếu xét thêm nhiều yếu tố khác như độ dễ viết, scalability thì Erlang nhỉnh hơn.
Ghi chú
Thử nghiệm sơ trên môi trường:
- Intel(R) Xeon(TM) CPU 2.80GHz
- Memory 1 GB
- Ubuntu 8.04
- Erlang 5.5.5 HiPE
- Ruby 1.8.6 và 1.9.0
cho kết quả:
- Erlang - !: 11000
- Erlang - gen_server: 7500
- Ruby (cả 2!): 3000 (Ruby 1.9 sau vài lần chạy thì bị error, nghĩa là chưa ổn định)
Ngoài ra, EventMachine có đối thủ nặng kí là rev. Thử chạy rev với Ruby 1.9 trên Leopard ở trên cho kết quả 15000.
172
over 3 years ago
Updated over 3 years ago