views:

267

answers:

5

I need to write a server that will receive instructions from other modules and take actions depending on the instructions received. Efficiency is my main concern. So do I use gen_server or do I write my own server. By "my own server" I mean something like:

-module(myserver).
-export([start/0, loop/0]).

start() ->
        spawn(myserver, loop, []).

loop() ->
   receive
        {From, Msg} -> %Do some action here... ;
        message2 -> %Do some action here...;
        message3 -> %Do some action here...;
        message4 -> %Do some action here...;
        .
        .
        .
        _-> ok
   end,
   loop().

So to use myserver, I will probably register the process under a registered name while starting it, and then each client will send messages to the server using this pid.

So should I use this method, or instead implement the server using the gen_server behaviour? Are there any advantages to using gen_server? But will using gen_server add any overhead, when compared to myserver?

+1  A: 

I would go with the gen_server simply because so much thought has gone into making it do the right thing under various circumstances. It takes care of details that are difficult to get right. I imagine gen_server might add some overhead but I've stopped giving performance advice. If you're really interested then implement both and measure the speed, that's the only surefire way to find out.

svenningsson
+7  A: 

I would go with gen_server as well. Once you've used this facility, you will learn to appreciate its value. The function callback can be a bit awkward (e.g. handle_cast for async calls) but in the end, you'll get used to it.

Furthermore, I would be advised not to engage in "premature optimization" without having done some testing. You probably don't want to sacrifice readibility/maintainability for marginal efficiency gains.

jldupont
+4  A: 

gen_server will have a negligible overhead compared to self-implemented servers, because it requires a few additional function calls per message (one of which is dynamic). I don't think you should consider this at this point of implementation. Did you changed your mind at any point, moving from gen_server to your own server should be straightforward.

What you get with gen_server compared to a simple loop is:

  • debugging (with sys)
  • SASL logging
  • hibernation support
  • code upgrade support
Zed
+1  A: 

You can also use gen_server2 by the guys behind RabbitMQ.

It's like gen_server except for the following adjustments (from the comments):

1) the module name is gen_server2

2) more efficient handling of selective receives in callbacks
gen_server2 processes drain their message queue into an internal
buffer before invoking any callback module functions. Messages are
dequeued from the buffer for processing. Thus the effective message
queue of a gen_server2 process is the concatenation of the internal
buffer and the real message queue.
As a result of the draining, any selective receive invoked inside a
callback is less likely to have to scan a large message queue.

3) gen_server2:cast is guaranteed to be order-preserving
The original code could reorder messages when communicating with a
process on a remote node that was not currently connected.
cstar
+1  A: 

I assume from your question that you are writing a more "permanent" server.

In general rolling your own server is more versatile and a little faster, if you get it right. But, and this is a big BUT:

  • You will have to do everything yourself, which increases the risk of errors!

  • If you want your server to be managed in the OTP way, which you probably do if you are building a robust system, then you will have to handle all that yourself as well. And get it right.

If I was doing a permanent server I would start out using gen_server and only fallback and roll my own if I run into serious difficulties in implementing what I need.

Short lived servers are another matter.

rvirding