ankush981
ankush981

Reputation: 5407

Writing an Erlang timeit function

I want to write an Erlang timeit function (like the Python module) as a learning exercise that tells me for how long a particular function ran. Problem is, I'm pretty noob in Erlang and can't figure out how this is to be done.

Attempt 1 (try to make an accumulator):

timeit_acc(T, start, F) -> timeit_acc({os:timestamp(), T}, finish, F);
timeit_acc({T1, T2}, finish, F) -> element(T1, 2) - element(T2, 2).
timeit(F) -> timeit_acc(os:timestamp(), start, F).

This of course doesn't work as I couldn't figure out where to actually call the function F. I'm also very sure that's not the only problem with this code. :P

Attempt 2 (trying to make a function execute several statements, like Python):

timeit(F)->
    {M, S, MS} = os.timestamp(),
    F,
    {M2, S2, MS2} = os.timestamp(),
    {M2 - M1, S2- S1, MS2- MS1}.

I was pretty hopeful of the second attempt, but ran into the following brick-wall:

79> c(timeit).
timeit.erl:11: syntax error before: '.'
timeit.erl:3: function timeit/1 undefined
error

Now that I think about it, I'm also wondering how can I handle the function F when it can have different number of arguments . . . Can somebody please tell me how this can be done? And of course, a little bit of explanation will be very nice.

==== Update ====

Following the suggestions received in comments, the new version of the function is:

timeit(F) ->
    {M, S, MS} = os:timestamp(),
    Val = F(),
    {M2, S2, MS2} = os:timestamp(),
    {{M2 - M, S2 - S, MS2 - MS}, Val}.

However, now I'm not sure how to use it. I tried to profile the erlang:time/0 function but failed:

109> c(timeit).
{ok,timeit}
110> timeit:timeit(time).
** exception error: bad function time

Upvotes: 3

Views: 775

Answers (2)

A. Sarid
A. Sarid

Reputation: 3996

Your second attempt is very close. Try using this,

timeit(F,Args) ->
    {MS, S, US} = os:timestamp(),
    erlang:apply(F,Args),
    {MS2, S2, US2} = os:timestamp(),
    {MS2-MS,S2-S,US2-US}.

Note: erlang:apply/2 allows you to run a function with a list of arguments. In the code above Args should be a list with all the arguments for function F. If you for example want to run a function with 3 arguments Args will be [Arg1,Arg2,Arg3]. (This includes zero arguments too). See examples below.

Examples:

3> testit:timeit(fun(X) -> X+X end, [5]).
{0,0,7}
4> testit:timeit(fun lists:append/2, [[1,2,3],[4,5,6]]).
{0,0,1}
5> testit:timeit(fun() -> io:format("Fun with no args~n",[]) end, []).
Fun with no args
{0,0,688}

Upvotes: 2

legoscia
legoscia

Reputation: 41528

In attempt 2, your function calls look like os.timestamp, with a dot between the module name and the function name like in Python, but in Erlang that should be a colon instead: os:timestamp.


As of Erlang 18, an alternative to os:timestamp is erlang:monotonic_time. If I understand the documentation correctly, the "Erlang monotonic time" is probably the kind of time stamp you want to use to measure time elapsed during a function call.

From the timer:tc source code:

tc(F) ->
    T1 = erlang:monotonic_time(),
    Val = F(),
    T2 = erlang:monotonic_time(),
    Time = erlang:convert_time_unit(T2 - T1, native, micro_seconds),
    {Time, Val}.

erlang:monotonic_time() by itself returns a timestamp as an integer in an unspecified "native" time unit. That's why the call to erlang:convert_time_unit is needed, to convert from native to micro_seconds. You could also call erlang:monotonic_time(micro_seconds) to do the conversion at the point where you get the timestamp.

Upvotes: 4

Related Questions