Loading presentation...

Present Remotely

Send the link below via email or IM

Copy

Present to your audience

Start remote presentation

  • Invited audience members will follow you as you navigate and present
  • People invited to a presentation do not need a Prezi account
  • This link expires 10 minutes after you close the presentation
  • A maximum of 30 users can follow your presentation
  • Learn more about this feature in our knowledge base article

Do you really want to delete this prezi?

Neither you, nor the coeditors you shared it with will be able to recover it again.

DeleteCancel

Make your likes visible on Facebook?

Connect your Facebook account to Prezi and let your likes appear on your timeline.
You can change this under Settings & Account at any time.

No, thanks

Erlang_engine_tuning_part_2_beam

EUC 2013 presentation of beam
by

Erik Stenman

on 3 March 2014

Comments (0)

Please log in to add your comment.

Report abuse

Transcript of Erlang_engine_tuning_part_2_beam

Mindset: A Garden
The right people
Stable
Maintainable
Secure
Communication

Rapid development
Performance
Great developers
Easy to make fault-tolerant systems.
Erlang was designed from the ground up with the purpose of making it easy to develop fault-tolerant systems.
Erlang was developed by Ericsson with the telecom market in mind.
Erlang supports processes, distributed systems, advanced exception handling, and signals.
Erlang comes with OTP-libraries (Open Telecom Platform), e.g. supervisors and generic servers.
Low maintenace easy upgrade
Hot code loading.
Distribution.
Interactive shell.
Simple module system.
No shared state.
Virtual machine.
Rapid development
Ability to leverage multi-core
Network programming is easy
God way to get great programmers.
– Automatic memory management.
– Symbolic constants (atoms).
– An interactive shell.
– Dynamic typing.
– Simple but powerful data types.
– Higher order functions and list comprehensions.
– Built in (distributed) database.
The concept of processes is an integral part of Erlang.
No shared memory -- easier to program.
The Erlang Virtual machine (BEAM) has support for symmetric multiprocessing.
“Each year your sequential programs will go slower.
Each year your concurrent programs will go faster.”
Distributed Erlang solves many network programming needs.
Setting up a simple socket protocol is a breeze.
The binary- (and now bit-) syntax makes parsing binary protocols easy.
There are simple but powerful libraries for HTTP, XML, XML-RPC and SOAP.
Nice paradox:
The lack of Erlang programmers makes it easier for us to find great programmers.

There are many great C and Java programmers, I’m sure, but they are hidden by hordes of mediocre programmers.

Programmers who know a functional programming language are often passionate about programming.

Passionate programmers makes Great Programmers™
-- Joe Armstrong
Erlang the language
Functional
Single-assignment
Dynamically typed
Concurrent
Distributed
Message passing
Soft real-time
Fault tolerant
No sharing
Automatic Memory Management (GC)
Open Source
1982-1986 Experiments with existing languages
1986-1987: First experiments with own language
1988-1989: Internal use
1990-1998: Erlang sold as a product by Ericsson
1998: Open Source
2009: On github
Development still done mainly by Ericsson
Erlang is gaining popularity
Erlang History
F1
=
fun
() -> 42
end
.
42 =
F1
().

F2
=
fun
(
X
) ->
X
+ 1
end
.
11 =
F2
(10).

F3
=
fun
(
X
,
Y
) ->
{
X
,
Y
,
Z
}

end
.
F4
=
fun
({foo,
X
},
A
) ->

A
+
X
*
Y
;
({bar,
X
},
A
) ->

A
-
X
*
Y
;
(_,
A
) ->

A

end
.

F5
=
fun
f/3

F6
=
fun
mod:f/3
%% File: hello.erl
-module(hello).
-export([run/0]).
run() -> io:format(
"Hello, World!\n"
).
Developed by Ericsson, Sweden
How to write robust software?
Two machines
- concurrency/distribution/error handling
(after Danish mathematician A. K. Erlang.)
(foo@frodo)1>
X
=42.
42
(foo@frodo)2>
X
.
42
(foo@frodo)3>
X
=43.
** exception error: no match of right hand side value 43
(foo@frodo)4>
Supervisor trees
Hof on DB
QLC
What is Erlang?
(foo@frodo)4>

F
=
fun
(
X
,
Y
) ->
X
+
Y
end.
#Fun<erl_eval.12.113037538>
(foo@frodo)5>

F
(1,2).
3
(foo@frodo)6>

F
(1.0, 2).
3.0
(foo@frodo)7>

F
(
"1"
, 2).
** exception error: bad argument in an arithmetic expression
in operator +/2
called as

"1"

+ 2
Demo 1
Demo2
%%% DEMO 1
%%% Show code
cd("C:/Users/happi").
S=demo:start_server().
W = demo:start_worker(S).
W2 = demo:start_worker(S).
S ! {broadcast, hi}.
W ! terminate.
exit(W2, kill).
S ! {broadcast, hi}.
exit(S, kill).
S ! {broadcast, hi}.
%%%-------------------------------------------------------------------
%%% File : demo.erl
%%% Author : Tobias Lindahl <tobias@klarna.se>
%%% Description : Small server/worker demo
%%%
%%% Created : 21 Apr 2010 by Tobias Lindahl <tobias@klarna.se>
%%%-------------------------------------------------------------------
-module(demo).
-export([start_server/0, start_worker/1]).

%%%-------------------------------------------------------------------
%%% Interface
start_server() -> spawn(fun server/0).
start_worker(Server) -> spawn(fun() -> worker(Server)end).

%%%-------------------------------------------------------------------
%%% Server
server() ->
process_flag(trap_exit, true),
out("I am the server\n", []),
server_loop([]).

server_loop(Pids) ->
receive
{register, NewPid} ->
link(NewPid),
out("The pid ~p has registered\n", [NewPid]),
server_loop(ordsets:add_element(NewPid, Pids));
{broadcast, What} ->
out("Broadcasting: ~p\n", [What]),
send_to_all(Pids, What),
server_loop(Pids);
all_pids ->
out("Currently registered pids: ~p\n", [Pids]),
server_loop(Pids);
terminate ->
send_to_all(Pids, terminate),
out("Server terminating\n", []);
{'EXIT', Who, Why} ->
out("Process ~p died with slogan ~p\n", [Who, Why]),
server_loop(ordsets:del_element(Who, Pids));
What ->
out("Server received: ~p\n", [What]),
server_loop(Pids)
end.

%%%-------------------------------------------------------------------
%%% Worker
%%%

worker(Server) ->
out("I am a worker\n"),
Server ! {register, self()},
worker_loop(Server).

worker_loop(Server) ->
receive
terminate ->
out("Worker terminating\n");
What ->
out("Worker received: ~p\n", [What]),
worker_loop(Server)
end.

%%%-------------------------------------------------------------------
%%% Util
%%%

out(What) ->
out(What, []).

out(What, Fmt) ->
timer:sleep(100),
io:format("~p: " ++ What, [self()| Fmt]).

send_to_all([Pid|Left], What) ->
Pid ! What,
send_to_all(Left, What);
send_to_all([], _What) ->
ok.
%% DEMO 2
%% Start two cmd:s
%% On first
"c:\Program Files (x86)\erl5.8.1\bin\erl" -sname foo
nodes().
%% On second
"c:\Program Files (x86)\erl5.8.1\bin\erl" -sname bar
net:ping('foo@frodo').
nodes().

S = rpc:call('foo@frodo', demo, start_server, []).
%% on (foo@frodo)1>
i().

%% on (bar@frodo)
W = demo:start_worker(S).
S ! {broadcast, hi}.
Hot code loading
Genserver
Atoms/symbolic programming
Hof map/fold
Mnesia
lm() -> [c:l(M) || M <- mm()].
mm() -> modified_modules().
modified_modules() ->
[M || {M, _} <- code:all_loaded(), module_modified(M) == true].

module_modified(Module) ->
case code:is_loaded(Module) of
{file, preloaded} -> false;
{file, Path} -> CompileOpts = proplists:get_value(compile, Module:module_info()),
CompileTime = proplists:get_value(time, CompileOpts),
Src = proplists:get_value(source, CompileOpts),
module_modified(Path, CompileTime, Src);
_ -> false
end.
module_modified(Path, PrevCompileTime, PrevSrc) ->
case find_module_file(Path) of
false -> removed;
ModPath -> case beam_lib:chunks(ModPath, ["CInf"]) of
{ok, {_, [{_, CB}]}} ->
CompileOpts = binary_to_term(CB),
CompileTime = proplists:get_value(time, CompileOpts),
Src = proplists:get_value(source, CompileOpts),
not ((CompileTime == PrevCompileTime) and (Src == PrevSrc));
_ -> false
end
end.
Demo
cd("C:/Users/happi").
toolbar:start().
%% Klick on Kernel
%% OPen supervisor tree
sup:start().
%% Kill process
%% Kill supervisor
-module(sup).
-behaviour(supervisor).
-export([init/1]).
-export([start/0, start/1]).
-export([loop/1]).
%% Supervisor
init(_) ->
{ok, {{one_for_one, 1,60},
[{sup, {sup, start, [0]},
permanent, brutal_kill, worker, [sup]}]}}.

start() ->
%% {ok, P} = supervisor:start_link(sup, [0]),
supervisor:start_child(kernel_safe_sup,
{mysup, {supervisor,start_link, [sup,[0]]},
permanent, 2000, supervisor, [sup]}
).
%% Worker
start(Count) ->
io:fwrite("Starting...~n"),
Pid=spawn_link(fun() -> loop(Count) end),
{ok, Pid}.

loop(Count) ->
io:format("counting: ~p.~n", [Count]),
timer:sleep(1000),
loop(Count + 1).
Fault tolerant
%%%-------------------------------------------------------------------
%%% File : demo.erl
%%% Author : Tobias Lindahl <tobias@klarna.se>
%%% Description : Small server/worker demo
%%%
%%% Created : 21 Apr 2010 by Tobias Lindahl <tobias@klarna.se>
%%%-------------------------------------------------------------------
-module(demo).
-export([start_server/0, start_worker/1]).

%%%-------------------------------------------------------------------
%%% Interface
start_server() -> spawn(fun server/0).
start_worker(Server) -> spawn(fun() -> worker(Server)end).

%%%-------------------------------------------------------------------
%%% Server
server() ->
process_flag(trap_exit, true),
out("I am the server\n", []),
server_loop([]).

server_loop(Pids) ->
receive
{register, NewPid} ->
link(NewPid),
out("The pid ~p has registered\n", [NewPid]),
server_loop(ordsets:add_element(NewPid, Pids));
{broadcast, What} ->
out("Broadcasting: ~p\n", [What]),
send_to_all(Pids, What),
server_loop(Pids);
all_pids ->
out("Currently registered pids: ~p\n", [Pids]),
server_loop(Pids);
terminate ->
send_to_all(Pids, terminate),
out("Server terminating\n", []);
{'EXIT', Who, Why} ->
out("Process ~p died with slogan ~p\n", [Who, Why]),
server_loop(ordsets:del_element(Who, Pids));
What ->
out("Server received: ~p\n", [What]),
server_loop(Pids)
end.

%%%-------------------------------------------------------------------
%%% Worker
%%%

worker(Server) ->
out("I am a worker\n"),
Server ! {register, self()},
worker_loop(Server).

worker_loop(Server) ->
receive
terminate ->
out("Worker terminating\n");
What ->
out("Worker received: ~p\n", [What]),
worker_loop(Server)
end.

%%%-------------------------------------------------------------------
%%% Util
%%%

out(What) ->
out(What, []).

out(What, Fmt) ->
timer:sleep(100),
io:format("~p: " ++ What, [self()| Fmt]).

send_to_all([Pid|Left], What) ->
Pid ! What,
send_to_all(Left, What);
send_to_all([], _What) ->
ok.
%% DEMO 2
%% Start two cmd:s
%% On first
"c:\Program Files (x86)\erl5.8.1\bin\erl" -sname foo
nodes().
%% On second
"c:\Program Files (x86)\erl5.8.1\bin\erl" -sname bar
net:ping('foo@frodo').
nodes().

S = rpc:call('foo@frodo', demo, start_server, []).
%% on (foo@frodo)1>
i().

%% on (bar@frodo)
W = demo:start_worker(S).
S ! {broadcast, hi}.
Demo3
Bright
Passionate
Get things done
QUESTIONS?
The Scheduler
Processes
The Garbage Collector
The BEAM interpreter
HiPE
ERTS
CODE

GC
Scheduler
BEAM
Sockets
etc...

C-stack
BEAM
CODE

Queues
Processes
Binaries
P1
P2
P3
P99
...
...
PCB
Process
Control
Block
MQ
Message
Queue
Stack
Heap
Native
Stack
See: [OTP]/erts/emulator/beam/erl_process.h
htop
stop
heap
hend
cp
fcalls
reds
id
flags
next
prev
...
I/O
See: [OTP]/erts/
emulator/
beam/
hipe/
etc/

aaaaaaaaaaaaaaaaaaaaaaaaaatttt00 HEADER (see below)
pppppppppppppppppppppppppppppp01 CONS
pppppppppppppppppppppppppppppp10 BOXED (pointer to header)
iiiiiiiiiiiiiiiiiiiiiiiiiiii0011 PID
iiiiiiiiiiiiiiiiiiiiiiiiiiii0111 PORT
iiiiiiiiiiiiiiiiiiiiiiiiii001011 ATOM
iiiiiiiiiiiiiiiiiiiiiiiiii011011 CATCH
iiiiiiiiiiiiiiiiiiiiiiiiii111011 NIL (i always zero...)
iiiiiiiiiiiiiiiiiiiiiiiiiiii1111 SMALL_INT
aaaaaaaaaaaaaaaaaaaaaaaaaa000000 ARITYVAL
vvvvvvvvvvvvvvvvvvvvvvvvvv000100 BINARY_AGGREGATE |
vvvvvvvvvvvvvvvvvvvvvvvvvv001x00 BIGNUM with sign bit |
vvvvvvvvvvvvvvvvvvvvvvvvvv010000 REF |
vvvvvvvvvvvvvvvvvvvvvvvvvv010100 FUN | THINGS
vvvvvvvvvvvvvvvvvvvvvvvvvv011000 FLONUM |
vvvvvvvvvvvvvvvvvvvvvvvvvv011100 EXPOR |
vvvvvvvvvvvvvvvvvvvvvvvvvv100000 REFC_BINARY | |
vvvvvvvvvvvvvvvvvvvvvvvvvv100100 HEAP_BINARY | BINARIES |
vvvvvvvvvvvvvvvvvvvvvvvvvv101000 SUB_BINARY | |
vvvvvvvvvvvvvvvvvvvvvvvvvv101100 Not used
vvvvvvvvvvvvvvvvvvvvvvvvvv110000 EXTERNAL_PID | |
vvvvvvvvvvvvvvvvvvvvvvvvvv110100 EXTERNAL_PORT | EXTERNAL THINGS |
vvvvvvvvvvvvvvvvvvvvvvvvvv111000 EXTERNAL_REF | |
vvvvvvvvvvvvvvvvvvvvvvvvvv111100 Not used
The Tag Scheme
An example
The string "Hello", i.e. the list [104, 101, 108, 108, 111]
ADR BINARY VALUE + DESCRIPTION
hend -> +-------- -------- -------- --------+
| ... |
| ... |
|00000000 00000000 00000000 10000001| 128 + list tag ---------------+
stop -> | | |
|
htop -> | | |
132 |00000000 00000000 00000000 01111001| 120 + list tag -------------- | -+
128 |00000000 00000000 00000110 10001111| (H) 104 bsl 4 + small int tag <+ |
124 |00000000 00000000 00000000 01110001| 112 + list tag ----------------- | -+
120 |00000000 00000000 00000110 01011111| (e) 101 bsl 4 + small int tag <---+ |
116 |00000000 00000000 00000000 01110001| 112 + list tag -------------------- | -+
112 |00000000 00000000 00000110 11001111| (l) 108 bsl 4 + small int tag <------+ |
108 |00000000 00000000 00000000 01110001| 96 + list tag ----------------------- | -+
104 |00000000 00000000 00000110 11001111| (l) 108 bsl 4 + small int tag <---------+ |
100 |11111111 11111111 11111111 11111011| NIL |
96 |00000000 00000000 00000110 11111111| (o) 111 bsl 4 + small int tag <------------+
| ... |
heap -> +-----------------------------------+
Tuple
Sharing
If the list is used in several places all that is repeated is the tagged pointer to the list:
00000000000000000000000001000001
L = [104, 101, 108, 108, 111],
T = {L, L}.
ADR BINARY VALUE DESCRIPTION
144 1010000 00000000000000000000000001000001 128+CONS
140 1001100 00000000000000000000000001000001 128+CONS
136 1010100 00000000000000000000000010000000 2+ARITYVAL
This is nice...

... until you do

a send
or IO (any deep copy)

... then the sharing
is expanded.
share(0, Y) -> {Y,Y};
share(N, Y) -> [share(N-1, [N|Y]) || _ <- Y].

timer:tc(fun() -> test:share(10,[a,b,c]), ok end).
{1131,ok}

test:share(10,[a,b,c]), ok.
ok

byte_size(list_to_binary(test:share(10,[a,b,c]))), ok.
HUGE size (13695500364)
Abort trap: 6
BEAM is a register machine.
It has two sets of registers:
x and y
x registers are caller save
and arguments.
y registers are callee save
and actually the stack.
See: [OTP]/erts/emulator/beam/beam_emu.c
You can look at beam code by giving the 'S' flag to the compiler:

c(test, ['S']).
Each process get a number of reductions.
Each function call reduces the reduction count by 1.
When the reduction count reaches 0 the process is scheduled out.
The process is then put at the end of the ready queue.
The scheduler picks the next process from the ready queue.
If a process blocks in a receive, it is put in the wait queue.
When a process receives a new message it is moved to the ready queue.
BIFs uses an arbitrary amount of reductions.
A return does not use any reductions.
p2() ->
L = "Hello",
T = {L, L},
P3 = mk_proc(),
P3 ! T.
Heap
P2
P3
Copying Generational Garbage Collector
Why copying collector?
Erlang has no updates -
there can be no cycles: use reference count.
Erlang terms are small.
The HiPE group did some measures:
75% cons cells
24% !cons but smaller than 8 words
1% >= 8 words
Advantages with 1 heap/process:
+ Free reclamation when a process dies
+ Small root set
+ Improved cache locallity
+ Cheap stack/heap test
Disadvantages with 1 heap/process:
- Message passing is expensive
- Uses more space (fragmentation)
Hibernation:
Do a GC to a temp area.
Check size
Allocate a minimal mem area
move live data

Less fragmentation & better locality with copying collector
A Stack
A Heap
A Mailbox
A Process Control Block
A PID
Conceptually: 4 memory areas and a pointer:
Erlang Engine Tuning:
Erik Stenman
Lessons learned:
BEAM is a
Garbage Collecting-
Reduction Counting-
Non-preemptive-
Directly Threaded-
Register-
Virtual- -Machine
Use the option ['S', binary] to get beam code.
PCB
Process
Control
Block
MQ
Stack
Heap
htop
stop
heap
hend
cp
fcalls
reds
id
flags
next
prev
...
Generational GC
"Most objects die young."
From
To
Nursery
Stack
Old generation
Binaries are reference counted
If they are larger than 64 bytes
Each process has a list of off-heap binaries.
After a GC the reference count is adjusted.
If you create a sub-binary the process still has a reference to the binary.
Passing a binary to a process that sends it on without looking at it, creates a new reference.
Process
Control
Block
htop
stop
heap
hend
cp
fcalls
reds
id
flags
next
prev
...
PCB
MQ
ERTS as source code:
ERTS as components:
ERTS as memory areas:
-
module
(beamfile).
-
export
(
[read/1]).
read(
Filename
)

->
{ok,
File
} =
file:read_file(
Filename
),

<<
"FOR1"
,
Size
:32/
integer,
"BEAM"
,
Chunks
/bin
ary>> =
File
,

{Size,
rea
d_chunks(
Chunks
, [])
}.

read_chunks(<<
N,A,M,E
,
Size
:
32
/int
eger,
Tail
/bi
nary
>>,
Acc
) ->

%% Align each chunk on even 4 bytes

ChunkLength

= align_
by_four(
Size
),

<<
Chunk
:
ChunkLength
/binary,
Rest
/binary>> =
Tail
,
re
ad_c
hunks(
Rest
, [{[
N,A,M,E
],
Size
,
Chunk
}|
Acc
]);
read_
chu
nks(<<>>,
Acc
) ->
lists:reverse(
Acc
).

align_by_four(
N
) -> (4 * ((
N
+3) div 4)).
get_all_atom_chunks() ->
[get_atom_chunk(
Name
)
||
Name
<- all_beam_files()].

get_atom_chunk(
FileName
) ->
{_,
Chunks
} = beamfile:read(
FileName
),
{value, {_,_,
AtomChunk
}} = lists:keysearch(
"Atom"
,1,
Chunks
),

AtomChunk
.
[AC1, AC2, AC3, ...]
As long as we hang on to the sub-binaries, the GC can't reclaim the parent binaries.

...
read_chunks(<<
N,A,M,E
,
Size
:
32
/int
eger,
Tail
/bi
nary
>>,
Acc
) ->

%% Align each chunk on even 4 bytes

ChunkLength

= align_
by_four(
Size
),

<<
Chunk
:
ChunkLength
/binary,
Rest
/binary>> =
Tail
,
re
ad_c
hunks(
Rest
, [{[
N,A,M,E
],
Size
,
Chunk
}|
Acc
]);
...
Fix this with binary:copy/1

...
read_chunks(<<
N,A,M,E
,
Size
:
32
/int
eger,
Tail
/bi
nary
>>,
Acc
) ->

%% Align each chunk on even 4 bytes

ChunkLength

= align_
by_four(
Size
),

<<
Chunk
:
ChunkLength
/binary,
Rest
/binary>> =
Tail
,
re
ad_c
hunks(
Rest
, [{[
N,A,M,E
],
Size
,
binary:copy(
Chunk
)}|
Acc
]);
...
[AC1, AC2, AC3, ...]
Now there are no references to the parent binaries and they can be reclaimed.
Two subtle problems:
Problem two
Your nice server architecture:
Socket listener
Dispatcher/load balancer
Work pool A
Work pool B
receive
{From, BigBinary} ->
{RequestType, Workload} = parse(BigBinary)
LB ! {RequestType, From, binary:copy(Workload)}
loop(A,B) ->
receive
Msg = {RequestType, _From, _Load} ->
case RequestType of
a -> A ! Msg;
b -> B ! Msg;
end,
loop(A,B),
end.
Dispatcher/load balancer
loop(A,B) ->
receive
Msg = {RequestType, _From, _Load} ->
case RequestType of
a -> A ! Msg;
b -> B ! Msg;
end,
loop(A,B);
after 1000 ->
garbage_collect(),
loop(A,B)
end.
Solution
Add an explicit GC
Know Your Engine
Part 2: The Beam

What is ERTS?
ERTS is the Erlang Runtime System.
Programming since 1980
Erlang since 1994
First native code compiler for Erlang
HiPE
Project Manager for Scala 1.0
2005-2010 CTO @ Klarna
Chief Scientist @ Klarna
Writing a book about ERTS
Happi
Sharing
-define(GREETING, "hello world").
world.hrl
-module(world).
-export([hello/0]).

-include("world.hrl").

hello() -> ?GREETING.
world.erl
-file("world.erl",1).
-module(world).
-export([hello/0]).
-file("world.hrl", 1).
-file("world.erl", 4).
hello() ->
"hello world".
1> c(world, ['P']).
** Warning: No object file created - nothing loaded **
ok
world.P
{module, world}.
%% version = 0
{exports, [{hello,0}, {module_info,0}, {module_info,1}]}.
{attributes, []}.
{labels, 7}.


{function, hello, 0, 2}.
{label,1}.
{func_info, {atom,world}, {atom,hello}, 0}.
{label,2}.
{move,{literal,"hello world"},{x,0}}.
return.

{function, module_info, 0, 4}.
{label,3}.
{func_info, {atom,world}, {atom,module_info},0}.
{label,4}.
{move,{atom,world},{x,0}}.
{call_ext_only, 1,
{extfunc, erlang, get_module_info, 1}}.

{function, module_info, 1, 6}
{label,5}. {func_info,{atom,world},{atom,module_info},1}. {label,6}.
{move,{x,0},{x,1}}.
{move,{atom,world},{x,0}}.
{call_ext_only, 2,
{extfunc, erlang, get_module_info, 2}}.
BEAM
Garbage Collecting-
Reduction Counting-
Non-preemptive-
Directly Threaded-
Register-
Virtual-
-Machine
BEAM is Virtually Unreal
The Beam is a virtual machine: it is implemented
in software instead of in hardware.
There is no official specification of the Beam,
it is currently only defined by
the implementation in Erlang/OTP.
A Stack Machine - it is not
BEAM is a register machine
Advantage of a stack machine
Easier to compile to
Easier to implement
See my blog: http://stenmans.org/happi_blog/?p=194
for an example of a stack machine.
Advantage of a register machine
More efficient (?)
Two types of registers: X and Y-registers.
X0 is the accumulator and mapped to a physical register, also called R0.
Y registers are actually stack slots.
There are a number of special purpose registers:
htop, stop, I, fcalls and floating point registers.
Dispatch: Directly Threaded Code
The dispatcher finds the next instruction to execute.
{move,{x,0},{x,1}}.
{move,{y,0},{x,0}}.
{move,{x,1},{y,0}}.
External beam format:
Loaded code*:
*This is a lie... beam actually rewrites the external format to different internal instructions....
0x1000: 0x3000
0x1004: 0x0
0x1008: 0x1
0x100c: 0x3200
0x1010: 0x1
0x1014: 0x1
0x1018: 0x3100
0x101c: 0x1
0x1020: 0x1
{
{
{
OpCase(move_xx): {
0x3000: x(Arg(1)) = x(Arg(0)):
I += 3;
Goto(*I);
}
OpCase(move_yx): {
0x3200: x(Arg(1)) = y(Arg(0));
I += 3;
Goto(*I);
}
OpCase(move_xy): {
0x3100: y(Arg(1)) = x(Arg(0));
I += 3;
Goto(*I);
}
I: 0x1000
beam_emu.c **:
** This is another lie, there are no such instructions in beam_emu, but you can't handle the truth.
#define Arg(N) (Eterm *) I[(N)+1]
#define Goto(Rel) goto *((void *)Rel)
Scheduling:
Non-preemptive, Reduction counting
Each function call is counted as a reduction
Beam does a test at function entry: if (reds < 0) yield
A reduction should be a small work item
Loops are actually recursions, burning reductions
A process can also yield in a receive.
Memory Management:
Garbage Collection
On the Beam level the code is responsible for:
checking for stack and heap overrun.
allocating enough space

"test_heap" will check that there is free heap space.
If needed the instruction will call the GC.
The GC might call lower levels of the memory subsystem to allocate or free memory as needed.
1> c(world, 'S').
1> compile:file(world, ['S', binary]).
Beam Instructions
An Added Example
-module(add).-export([add/2]).add(A,B) -> id(A) + id(B).id(I) -> I.
{allocate,1,2}. {move,{x,1},{y,0}}. {call,1,{f,4}}. {move,{x,0},{x,1}}. {move,{y,0},{x,0}}. {move,{x,1},{y,0}}. {call,1,{f,4}}. {gc_bif,'+',{f,0},1,[{y,0},{x,0}],{x,0}}. {deallocate,1}. return.
Full transcript