[erlang-questions] Erlang arithmetics
Richard O'Keefe
ok@REDACTED
Mon Nov 1 04:08:12 CET 2010
On 30/10/2010, at 9:03 PM, Dmitry Demeshchuk wrote:
> Greetings.
>
> I'm writing an article comparing Erlang and Node.js and I stumbled
> upon the performance question.
Jest: it looks more like you tripped over it.
>
> My initial goal was to compare some basic arithmetics speed, like the
> total distance between randomly distributed points.
It seems strange to see floating point called *basic*.
I see that you did not include random number generation in your
measurement. Wise of you. To give you
some figures I happen to have for a recent simulation in C:
"Time with checking and -g : 16.5 seconds.
Time sans checking and -O3 : 9.4 seconds using drand48().
8.9 seconds using my_drand48().
7.5 seconds using Mersenne Twister."
drand48() is the classic System V 48-bit linear congruential generator,
done using 16-bit chunks. my_drand48() is the same algorithm done using
64-bit arithmetic and with no locking. The Mersenne Twister is the
integer one, not the more recent version.
So within *ONE* language with *ONE* compiler on *ONE* platform there was
a factor of 1.75 from compiler options and a factor of 1.25 from random
number generator.
The Erlang random number generator is AS183 (about the speed of drand48()
or somewhat slower), and it's written in Erlang. In OSSP js, Version 1.6
Math.random() is implemented in C. (It looks pretty much like my_drand48().)
However, the fact that Erlang and JavaScript don't use the same generator
means you're not measuring the same calculations.
> So, I have written
> the following code for Erlang:
>
> =====================================================
>
> -module(arith_speed).
> -export([
> test/1
> ]).
>
> test(N) ->
> L = lists:seq(1, N),
> [{X0, Y0} | Points] = [{random:uniform(1000),
> random:uniform(1000)} || _ <- L],
> Now = now(),
> lists:foldl(fun move_to/2, {0, {X0, Y0}}, Points),
> timer:now_diff(now(), Now).
>
> move_to({X, Y}, {Sum, {X0, Y0}}) ->
> {Sum + math:sqrt((X - X0) * (X - X0) + (Y - Y0) * (Y - Y0)), {X, Y}}.
This would not be the fastest way to do it.
test(N) ->
Data = [{random:uniform(1000),random:uniform(1000)}
|| _ <- lists:seq(1, N)],
Before = now(),
_ = path_length(Data),
timer:now_diff(now(), Before).
path_length([{X0,Y0}|Path]) ->
path_length(Path, X0, Y0, 0.0).
path_length([{X,Y}|Path], X0, Y0, Length) ->
Dx = float(X - X0),
Dy = float(Y - Y0),
path_length(Path, X, Y, Length + math:sqrt(Dx*Dx + Dy*Dy));
path_length([], _, _, Length) ->
Length.
would probably be faster. In fact, native-compiled on an elderly
500MHz UltraSPARC II, this version is 3/7 the time of the original code.
(Using Dx = float(X - X0) does actually improve the results a fair bit.)
Note that the original Erlang code and the original Javascript code do
rather different things.
(1) The Erlang code uses a higher-order function,
the JavaScript code does not, even though higher order functions
are available in JavaScript. (Good for JavaScript, bad for Erlang.)
(2) The Erlang code represents a point by an 2-element array with
elements extracted by pattern matching; the JavaScript version uses
objects with fields at least notionally found by searching a hash
table. (Good for Erlang, bad for JavaScript.)
Note also that there are *huge* performance differences between JavaScript
implementations.
I suspect that a much more relevant benchmark for many potential users
of Node.js would be something like "how many clients can a hello world
server like the one on the nodejs.org home page serve at once?"
(Hey, if it _isn't_ relevant, what's it doing on the home page?)
More information about the erlang-questions
mailing list