One process listen to server on async socket and on each message {tcp, Socket, Bin} takes it's buffer and:
Data = list_to_binary([Buffer, Bin]),
{next_state, 'READY', State#state{buffer = Data}}.
On some events it flushes buffer:
'READY'({flush}, #state{buffer = Buffer} = State) ->
{reply, {Buffer}, 'READY', State#state{buffer = <<>>}}.
Is it expensive? Maybe better just to make a list and make list_to_binary(lists:reverse()) once on flush?
Your first method appears to be much slower than your second method (by a factor of about 3000 on my platform):
-module(test).
-export([test/0, performance_test/4]).
-define(ITERATIONS, 100000).
-define(NEW_DATA, <<1, 2, 3, 4, 5, 6, 7, 8, 9, 10>>).
accumulate_1(AccumulatedData, NewData) ->
list_to_binary([AccumulatedData, NewData]).
extract_1(AccumulatedData) ->
AccumulatedData.
accumulate_2(AccumulatedData, NewData) ->
[NewData | AccumulatedData].
extract_2(AccumulatedData) ->
list_to_binary(lists:reverse(AccumulatedData)).
performance_test(AccumulateFun, ExtractFun) ->
{Time, _Result} = timer:tc(test, performance_test, [AccumulateFun, ExtractFun, [], ?ITERATIONS]),
io:format("Test run: ~p microseconds~n", [Time]).
performance_test(_AccumulateFun, ExtractFun, AccumulatedData, _MoreIterations = 0) ->
ExtractFun(AccumulatedData);
performance_test(AccumulateFun, ExtractFun, AccumulatedData, MoreIterations) ->
NewAccumulatedData = AccumulateFun(AccumulatedData, ?NEW_DATA),
performance_test(AccumulateFun, ExtractFun, NewAccumulatedData, MoreIterations - 1).
test() ->
performance_test(fun accumulate_1/2, fun extract_1/1),
performance_test(fun accumulate_2/2, fun extract_2/1),
ok.
Output:
7> test:test().
Test run: 57204314 microseconds
Test run: 18996 microseconds
In current releases, the handling of binaries by the emulator has been significant improved,
so now you could also take the simpler path and generate the binary chunk by chunk:
buffer = #state{buffer = <<Buffer/binary, Bin/binary>>}.
I didn't test it against the other approach, but shouldn't be bad.
Performance between different implementations will also likely depends on how many times you are performing this on the same buffer and how big each chunk is.
Related
-module(solarSystem).
-export([process_csv/1, is_numeric/1, parseALine/2, parse/1, expandT/1, expandT/2,
parseNames/1]).
parseALine(false, T) ->
T;
parseALine(true, T) ->
T.
parse([Name, Colour, Distance, Angle, AngleVelocity, Radius, "1" | T]) ->
T;%Where T is a list of names of other objects in the solar system
parse([Name, Colour, Distance, Angle, AngleVelocity, Radius | T]) ->
T.
parseNames([H | T]) ->
H.
expandT(T) ->
T.
expandT([], Sep) ->
[];
expandT([H | T], Sep) ->
T.
% https://rosettacode.org/wiki/Determine_if_a_string_is_numeric#Erlang
is_numeric(L) ->
S = trim(L, ""),
Float = (catch erlang:list_to_float(S)),
Int = (catch erlang:list_to_integer(S)),
is_number(Float) orelse is_number(Int).
trim(A) ->
A.
trim([], A) ->
A;
trim([32 | T], A) ->
trim(T, A);
trim([H | T], A) ->
trim(T, A ++ [H]).
process_csv(L) ->
X = parse(L),
expandT(X).
The problem is that it will calls process_csv/1 function in my module in a main, L will be a file like this:
[["name "," col"," dist"," a"," angv"," r "," ..."],["apollo11 ","white"," 0.1"," 0"," 77760"," 0.15"]]
Or like this:
["planets ","earth","venus "]
Or like this:
["a","b"]
I need to display it as follows:
apollo11 =["white", 0.1, 0, 77760, 0.15,[]];
Planets =[earth,venus]
a,b
[[59],[97],[44],[98]]
My problem is that no matter how I make changes, it can only show a part, and there are no symbols. The list cannot be divided, so I can't find a way.
In addition, because Erlang is a niche programming language, I can't even find examples online.
So, can anyone help me? Thank you, very much.
In addition, I am restricted from using recursion.
I think the first problem is that it is hard to link what you are trying to achieve with what your code says thus far. Therefore, this feedback maybe is not exactly what you are looking for, but might give some ideas. Let's structure the problem into the common elements: (1) input, (2) process, and (3) output.
Input
You mentioned that L will be a file, but I assume it is a line in a file, where each line can be one of the 3 (three) samples. In this regard, the samples also do not have consistent pattern.For this, we can build a function to convert each line of the file into Erlang term and pass the result to the next step.
Process
The question also do not mention the specific logic in parsing/processing the input. You also seem to care about the data type so we will convert and display the result accordingly. Erlang as a functional language will naturally be handling list, so on most cases we will need to use functions on lists module
Output
You didn't specifically mention where you want to display the result (an output file, screen/erlang shell, etc), so let's assume you just want to display it in the standard output/erlang shell.
Sample file content test1.txt (please note the dot at the end of each line)
[["name "," col"," dist"," a"," angv"," r "],["apollo11 ","white","0.1"," 0"," 77760"," 0.15"]].
["planets ","earth","venus "].
["a","b"].
Howto run: solarSystem:process_file("/Users/macbook/Documents/test1.txt").
Sample Result:
(dev01#Macbooks-MacBook-Pro-3)3> solarSystem:process_file("/Users/macbook/Documents/test1.txt").
apollo11 = ["white",0.1,0,77760,0.15]
planets = ["earth","venus"]
a = ["b"]
Done processing 3 line(s)
ok
Module code:
-module(solarSystem).
-export([process_file/1]).
-export([process_line/2]).
-export([format_item/1]).
%%This is the main function, input is file full path
%%Howto call: solarSystem:process_file("file_full_path").
process_file(Filename) ->
%%Use file:consult to convert the file content into erlang terms
%%File content is a dot (".") separated line
{StatusOpen, Result} = file:consult(Filename),
case StatusOpen of
ok ->
%%Result is a list and therefore each element must be handled using lists function
Ctr = lists:foldl(fun process_line/2, 0, Result),
io:format("Done processing ~p line(s) ~n", [Ctr]);
_ -> %%This is for the case where file not available
io:format("Error converting file ~p due to '~p' ~n", [Filename, Result])
end.
process_line(Term, CtrIn) ->
%%Assume there are few possibilities of element. There are so many ways to process the data as long as the input pattern is clear.
%%We basically need to identify all possibilities and handle them accordingly.
%%Of course there are smarter (dynamic) ways to handle them, but below may give you some ideas.
case Term of
%%1. This is to handle this pattern -> [["name "," col"," dist"," a"," angv"," r "],["apollo11 ","white"," 0.1"," 0"," 77760"," 0.15"]]
[[_, _, _, _, _, _], [Name | OtherParams]] ->
%%At this point, Name = "apollo11", OtherParamsList = ["white"," 0.1"," 0"," 77760"," 0.15"]
OtherParamsFmt = lists:map(fun format_item/1, OtherParams),
%%Display the result to standard output
io:format("~s = ~p ~n", [string:trim(Name), OtherParamsFmt]);
%%2. This is to handle this pattern -> ["planets ","earth","venus "]
[Name | OtherParams] ->
%%At this point, Name = "planets ", OtherParamsList = ["earth","venus "]
OtherParamsFmt = lists:map(fun format_item/1, OtherParams),
%%Display the result to standard output
io:format("~s = ~p ~n", [string:trim(Name), OtherParamsFmt]);
%%3. Other cases
_ ->
%%Display the warning to standard output
io:format("Unknown pattern ~p ~n", [Term])
end,
CtrIn + 1.
%%This is to format the string accordingly
format_item(Str) ->
StrTrim = string:trim(Str), %%first, trim it
format_as_needed(StrTrim).
format_as_needed(Str) ->
Float = (catch erlang:list_to_float(Str)),
case Float of
{'EXIT', _} -> %%It is not a float -> check if it is an integer
Int = (catch erlang:list_to_integer(Str)),
case Int of
{'EXIT', _} -> %%It is not an integer -> return as is (string)
Str;
_ -> %%It is an int
Int
end;
_ -> %%It is a float
Float
end.
When executing an implementation of the Tarry distributed algorithm, a problem occurs that I don't know how to address: a crash containing the error {undef,[{rand,uniform,[2],[]}. My module is below:
-module(assign2_ex).
-compile(export_all).
%% Tarry's Algorithm with depth-first version
start() ->
Out = get_lines([]),
Nodes = createNodes(tl(Out)),
Initial = lists:keyfind(hd(Out), 1, Nodes),
InitialPid = element(2, Initial),
InitialPid ! {{"main", self()}, []},
receive
{_, List} ->
Names = lists:map(fun(X) -> element(1, X) end, List),
String = lists:join(" ", lists:reverse(Names)),
io:format("~s~n", [String])
end.
get_lines(Lines) ->
case io:get_line("") of
%% End of file, reverse the input for correct order
eof -> lists:reverse(Lines);
Line ->
%% Split each line on spaces and new lines
Nodes = string:tokens(Line, " \n"),
%% Check next line and add nodes to the result
get_lines([Nodes | Lines])
end.
%% Create Nodes
createNodes(List) ->
NodeNames = [[lists:nth(1, Node)] || Node <- List],
Neighbours = [tl(SubList) || SubList <- List],
Pids = [spawn(assign2_ex, midFunction, [Name]) || Name <-NodeNames],
NodeIDs = lists:zip(NodeNames, Pids),
NeighbourIDs = [getNeighbours(N, NodeIDs) || N <- lists:zip(NodeIDs, Neighbours)],
[Pid ! NeighbourPids || {{_, Pid}, NeighbourPids} <- NeighbourIDs],
NodeIDs.
getNeighbours({{Name, PID}, NeighboursForOne}, NodeIDs) ->
FuncMap = fun(Node) -> lists:keyfind([Node], 1, NodeIDs) end,
{{Name, PID}, lists:map(FuncMap, NeighboursForOne)}.
midFunction(Node) ->
receive
Neighbours -> tarry_depth(Node, Neighbours, [])
end.
%% Tarry's Algorithm with depth-first version
%% Doesn't visit the nodes which have been visited
tarry_depth(Name, Neighbours, OldParent) ->
receive
{Sender, Visited} ->
Parent = case OldParent of [] -> [Sender]; _ -> OldParent end,
Unvisited = lists:subtract(Neighbours, Visited),
Next = case Unvisited of
[] -> hd(Parent);
_ -> lists:nth(rand:uniform(length(Unvisited)), Unvisited)
end,
Self = {Name, self()},
element(2, Next) ! {Self, [Self | Visited]},
tarry_depth(Name, Neighbours, Parent)
end.
An undef error means that the program tried to call an undefined function. There are three reasons that this can happen for:
There is no module with that name (in this case rand), or it cannot be found and loaded for some reason
The module doesn't define a function with that name and arity. In this case, the function in question is uniform with one argument. (Note that in Erlang, functions with the same name but different numbers of arguments are considered separate functions.)
There is such a function, but it isn't exported.
You can check the first by typing l(rand). in an Erlang shell, and the second and third by running rand:module_info(exports)..
In this case, I suspect that the problem is that you're using an old version of Erlang/OTP. As noted in the documentation, the rand module was introduced in release 18.0.
Will be good if you provide the version of Erlang/OTP you are using for future questions as Erlang has changed a lot over the years. As far as i know there is no rand:uniform with arity 2 at least in recent Erlang versions and that is what you are getting the undef error, for that case you could use crypto:rand_uniform/2 like crypto:rand_uniform(Low, High). Hope this helps :)
I'm learning Erlang, and I'm trying to pass a message from one process to another with some input from the stdio input.
This is the code I have (I know I could use normal functions but that is not the topic).
-module(play).
-compile(export_all).
calculateArea() ->
receive
{rectangle, W, H,SenderPID} -> SenderPID ! {area,W * H};
{circle, R,SenderPID} -> SenderPID ! {area,3.14 * R * R};
_ -> io:format("We can only calculate area of rectangles or circles.")
end,
calculateArea().
doStuff() ->
CalculateArea = spawn(play,calculateArea,[]),
{ok,Width} = io:fread("Enter width","~d"),
{ok,Height} = io:fread("Enter height","~d"),
CalculateArea ! {rectangle,Width,Height,self()},
receive
{area,Size} -> io:write(Size)
end,
io:fwrite("done").
When I run play:doStuff(). I get an {badarith,[{play,calculateArea,0,[{file,"play.erl"},{line,10}]}]} error.
I don't understand why, according to the docs, "~d" will give me a decimal value, and it sure looks like that if I print it.
Whats the catch here?
io:fread returns
Result = {ok, Terms :: [term()]}
| {error, {fread, FreadError :: io_lib:fread_error()}}
| server_no_data()
So Width and Height will be lists containing a single number each. Fix by using {ok, [Width/Height]} = ....
I am currently playing around with minimal web servers, like Cowboy. I want to pass a number in the URL, load lines of a file, sort these lines and print the element in the middle to test IO and sorting.
So the code loads the path like /123, makes a padded "00123" out of the number, loads the file "input00123.txt" and sorts its content and then returns something like "input00123.txt 0.50000".
At the sime time I have a test tool which makes 50 simultaneous requests, where only 2 get answered, the rest times out.
My handler looks like the following:
-module(toppage_handler).
-export([init/3]).
-export([handle/2]).
-export([terminate/3]).
init(_Transport, Req, []) ->
{ok, Req, undefined}.
readlines(FileName) ->
{ok, Device} = file:open(FileName, [read]),
get_all_lines(Device, []).
get_all_lines(Device, Accum) ->
case io:get_line(Device, "") of
eof -> file:close(Device), Accum;
Line -> get_all_lines(Device, Accum ++ [Line])
end.
handle(Req, State) ->
{PathBin, _} = cowboy_req:path(Req),
case PathBin of
<<"/">> -> Output = <<"Hello, world!">>;
_ -> PathNum = string:substr(binary_to_list(PathBin),2),
Num = string:right(PathNum, 5, $0),
Filename = string:concat("input",string:concat(Num, ".txt")),
Filepath = string:concat("../data/",Filename),
SortedLines = lists:sort(readlines(Filepath)),
MiddleIndex = erlang:trunc(length(SortedLines)/2),
MiddleElement = lists:nth(MiddleIndex, SortedLines),
Output = iolist_to_binary(io_lib:format("~s\t~s",[Filename,MiddleElement]))
end,
{ok, ReqRes} = cowboy_req:reply(200, [], Output, Req),
{ok, ReqRes, State}.
terminate(_Reason, _Req, _State) ->
ok.
I am running this on Windows to compare it with .NET. Is there anything to make this more performant, like running the sorting/IO in threads or how can I improve it? Running with cygwin didn't change the result a lot, I got about 5-6 requests answered.
Thanks in advance!
The most glaring issue: get_all_lines is O(N^2) because list concatenation (++) is O(N). Erlang list type is a singly linked list. The typical approach here is to use "cons" operator, appending to the head of the list, and reverse accumulator at the end:
get_all_lines(Device, Accum) ->
case io:get_line(Device, "") of
eof -> file:close(Device), lists:reverse(Accum);
Line -> get_all_lines(Device, [Line | Accum])
end.
Pass binary flag to file:open to use binaries instead of strings (which are just lists of characters in Erlang), they are much more memory and CPU-friendly.
EDIT
I have two modules and both cause bad args errors when fetching from the dictionary(gen_server state)
Here is code from one module
init([ChunkSize, RunningCounter]) ->
D0 = dict:new(),
D1 = dict:store(chunkSize, ChunkSize, D0),
D2 = dict:store(torrentUploadSpeed, 0, D1),
D3 = dict:store(torrentDownloadSpeed, 0, D2),
TorrentDownloadQueue = queue:new(),
TorrentUploadQueue = queue:new(),
D4 = dict:store(torrentDownloadQueue, TorrentDownloadQueue, D3),
D5 = dict:store(torrentUploadQueue, TorrentUploadQueue, D4),
D6 = dict:store(runningCounter, RunningCounter, D5),
{ok, D6}.
I then set_peer_state which sets up a peer dictionary(1 unique for each peer) The dictionary holds the download and upload (queue and speed) and I add this to the main gen_server state(dictionary) So I have the main torrent data in the main dictionary with a dictionary for each peer stored by the peer id.
set_peer_state(Id) ->
gen_server:cast(?SERVER, {setPeerState, Id}).
handle_cast({setPeerState, Id}, State) ->
io:format("In the Set Peer State ~p~n", [dict:fetch(runningCounter, State)]),
Id0 = dict:new(),
PeerDownloadQueue = queue:new(),
PeerUploadQueue = queue:new(),
Id1 = dict:store(peerDownloadQueue, PeerDownloadQueue, Id0),
Id2 = dict:store(peerUploadQueue, PeerUploadQueue, Id1),
Id3 = dict:store(peerDownloadSpeed, 0, Id2),
Id4 = dict:store(peerUploadSpeed, 0, Id3),
D = dict:store(Id, Id4, State),
{noreply, D};
This seems to work so far. But when I try updating the torrent state it crashes when fetching from the dictionary.
handle_cast({updateTorrentDownloadState, Time}, State) ->
% fetch the counter for the speed calculation and queue length
RunningCounter = dict:fetch(runningCounter, State),
% Fetch the Torrents download queue
TorrentDownloadQueue = dict:fetch(torrentDownloadQueue, State),
io:format("The fetched queue is ~p~n", [dict:fetch(torrentDownloadQueue, State)]),
% Add the item to the queue (main torrent upload queue)
TorrentDownloadQueue2 = queue:in(Time, TorrentDownloadQueue),
% Get the lenght of the downloadQueue
TorrentDownloadQueueLength = queue:len(TorrentDownloadQueue2),
% If the queue is larger than the running counter remove item
if
TorrentDownloadQueueLength >= RunningCounter ->
% Remove item from the queue
TorrentDownloadQueue3 = queue:drop(TorrentDownloadQueue2),
update_torrent_download(TorrentDownloadQueue3, State);
TorrentDownloadQueueLength < RunningCounter ->
update_torrent_download(TorrentDownloadQueue2, State)
end;
and here are the 2 internal functions
update_torrent_download(TorrentDownloadQueue, State) ->
% Store the queue to the new torrent dict
State2 = dict:store(torrentDownLoadQueue, TorrentDownloadQueue, State),
Speed = calculate_speed(TorrentDownloadQueue, State2),
State3 = dict:store(torrentDownloadSpeed, Speed, State2),
{noreply, State3}.
calculate_speed(Queue, State) ->
List = queue:to_list(Queue),
Sum = lists:sum(List),
Count = queue:len(Queue),
ChunkSize = dict:fetch(chunkSize, State),
Speed = (Count * ChunkSize) div Sum,
{ok, Speed}.
Could it be that passing incorrect data to the setters crash the server?
Or does the state get lost along the way?
This way of doing it seems messy with all the new dicts to store in the old dict, is there a better way to handle this data structure(main torrent and data for each peer)?
I know I could make the dictionaries from lists, but it was messing with my mind at the point I was testing this module.
Thanks
Your problem is that State is not a dict.
1> dict:fetch(runningCounter, not_a_dict).
** exception error: {badrecord,dict}
in function dict:get_slot/2
in call from dict:fetch/2
As YOUR ARGUMENT IS VALID suggested, you're state, at that point of your code, is not a dict.
Answering to your comments, now.
The state of your gen_server is set up in the init function, where you return: {ok, State}.
Every time your gen_server receive a message, an handle_call or an handle_cast are called (depending if the call is synchronous or asynchronous). Inside these functions, the State that you set up during the init phase can be updated and transformed into ANYTHING. You can't rely on the assumption that the "type" of your initial state is the same during the whole execution of your server.
In other words, if you do something like:
init(_Args) -> {ok, dict:new()}.
handle_call(_Request, _From, State) ->
{ok, completely_new_state}.
you've just "converted" your state from a dict into an atom and that (the atom) is what you will get in subsequent calls.
For this kind of errors, the Erlang tracing tool dbg is quite helpful, allowing you to see how functions are called and which results are returned. Have a look to this short tutorial to learn how to use it:
http://aloiroberto.wordpress.com/2009/02/23/tracing-erlang-functions/
UPDATE:
What you should do:
init(_Args) -> {ok, dict:new()}.
handle_call(_Request, _From, State) ->
NewState = edit_dict(State),
{ok, NewState}.
Where the edit_dict function is a function which takes a dict and returns an updated dict.