0

I am writing a program that solves producers-consumers problem using Erlang multiprocessing with one process responsible for handling buffer to which I produce/consume and many producers and many consumers processes. To simplify I assume producer/consumer does not know that his operation has failed (that it is impossible to produce or consume because of buffer constraints), but the server is prepared to do this.

My code is:

Server code

server(Buffer, Capacity, CountPid) ->
  receive
%%    PRODUCER
    {Pid, produce, InputList} ->
      NumberProduce = lists:flatlength(InputList),
      case canProduce(Buffer, NumberProduce, Capacity) of
        true ->
          NewBuffer = append(InputList, Buffer),
          CountPid ! lists:flatlength(InputList),
          Pid ! ok,
          server(NewBuffer,Capacity, CountPid);
        false ->
          Pid ! tryagain,
          server(Buffer, Capacity, CountPid)
      end;

%%    CONSUMER
    {Pid, consume, Number} ->
      case canConsume(Buffer, Number) of
        true ->
          Data = lists:sublist(Buffer, Number),
          NewBuffer = lists:subtract(Buffer, Data),
          Pid ! {ok, Data},
          server(NewBuffer, Capacity,CountPid);
        false ->
          Pid ! tryagain,
          server(Buffer, Capacity, CountPid)

      end
  end.

Producer and consumer

producer(ServerPid) ->
  X = rand:uniform(9),
  ToProduce = [rand:uniform(500) || _ <- lists:seq(1, X)],
  ServerPid ! {self(),produce,ToProduce},

  producer(ServerPid).

consumer(ServerPid) ->
  X = rand:uniform(9),
  ServerPid ! {self(),consume,X},

  consumer(ServerPid).

Starting and auxiliary functions (I enclose as I don't know where exactly my problem is)

spawnProducers(Number, ServerPid) ->
  case Number of
    0 -> io:format("Spawned producers");
    N ->
      spawn(zad2,producer,[ServerPid]),
      spawnProducers(N - 1,ServerPid)
  end.

spawnConsumers(Number, ServerPid) ->
  case Number of
    0 -> io:format("Spawned producers");
    N ->
      spawn(zad2,consumer,[ServerPid]),
      spawnProducers(N - 1,ServerPid)
  end.

start(ProdsNumber, ConsNumber) ->
  CountPid = spawn(zad2, count, [0,0]),

  ServerPid = spawn(zad2,server,[[],20, CountPid]),

  spawnProducers(ProdsNumber, ServerPid),
  spawnConsumers(ConsNumber, ServerPid).

canProduce(Buffer, Number, Capacity) ->
  lists:flatlength(Buffer) + Number =< Capacity.

canConsume(Buffer, Number) ->
  lists:flatlength(Buffer) >= Number.


append([H|T], Tail) ->
  [H|append(T, Tail)];
append([], Tail) ->
  Tail.

I am trying to count number of elements using such process, server sends message to it whenever elements are produced.

count(N, ThousandsCounter) ->
  receive
    X ->
      if
        N >= 1000 ->
          io:format("Yeah! We have produced ~p elements!~n", [ThousandsCounter]),
          count(0, ThousandsCounter + 1000);
        true -> count(N + X, ThousandsCounter)
      end
  end.

I expect this program to work properly, which means: it produces elements, increase of produced elements depends on time like f(t) = kt, k-constant and the more processes I have the faster production is.

ACTUAL QUESTION
I launch program:

  1. erl
  2. c(zad2)
  3. zad2:start(5,5)

How the program behaves:

  1. The longer production lasts the less elements in the unit of time are being produced (e.g. in first second 10000, in next 5000, in 10th second 1000 etc.
  2. The more processes I have, the slower production is, in start(10,10) I need to wait about a second for first thousand, whereas for start(2,2) 20000 appears almost immediately
  3. start(100,100) made me restart my computer (I work on Ubuntu) as the whole CPU was used and there was no memory available for me to open terminal and terminate erlang machine

Why does my program not behave like I expect? Am I doing something wrong with Erlang programming or is this the matter of OS or anything else?

Dawid
  • 153
  • 3
  • 18

1 Answers1

0

The producer/1 and consumer/1 functions as written above don't ever wait for anything - they just loop and loop, bombarding the server with messages. The server's message queue is filling up very quickly, and the Erlang VM will try to grow as much as it can, stealing all your memory, and the looping processes will steal all available CPU time on all cores.

RichardC
  • 10,412
  • 1
  • 23
  • 24
  • Is this shortage of memory also the reason why my program slows down with increasing number of elements produced? – Dawid Jan 07 '18 at 10:11
  • I have replaced `consumer(ServerPid)` with `receive _ -> consumer(ServerPid) end.`, the same for producer. Problems 1 and 3 disappeared. There is still one more thing I don't understand: the more producers I have the slower production is (I checked, it is like elementsProduced(numOfProds) = 1/numOfProds). Is this because EVM is being occupied by queuing requests and does not have time for actual producing? – Dawid Jan 07 '18 at 11:17
  • Yes, I think the problem is still that your many producers/consumers are outpacing the single server process. You need to do some more work with the synchronization you clearly had in mind with the ok/tryagain messages. By the way, use ++ instead of defining your own append, and try to use something more efficient than lists:subtract(). – RichardC Jan 07 '18 at 20:50