summaryrefslogtreecommitdiff
path: root/system
diff options
context:
space:
mode:
authorBjörn Gustavsson <bjorn@erlang.org>2021-08-11 10:45:57 +0200
committerBjörn Gustavsson <bjorn@erlang.org>2021-08-17 14:19:53 +0200
commit59ac9be8a5b3589d7e4a6b10c616395dbdfa1b2f (patch)
tree0ed97e0aabdfff5aa034da6e16898eef063134a9 /system
parentb5c939280fb7ef577b3e52656f15d6e1253ce40e (diff)
downloaderlang-59ac9be8a5b3589d7e4a6b10c616395dbdfa1b2f.tar.gz
Add a section about accidental copying and loss of sharing
Add a section about accidental copying and loss of sharing to the Common Caveats chapter. While at it, also update information about copying in the Processes chapter as follows: * Lift up several sub sections to the top level to make them visible in the left sidebar. * Rename the "Constant Pool" to "Literal Pool" (which is the correct technical term), and also update information about copying and persistent terms. Closes #3533.
Diffstat (limited to 'system')
-rw-r--r--system/doc/efficiency_guide/commoncaveats.xmlsrc (renamed from system/doc/efficiency_guide/commoncaveats.xml)83
-rw-r--r--system/doc/efficiency_guide/efficiency_guide.erl55
-rw-r--r--system/doc/efficiency_guide/processes.xmlsrc (renamed from system/doc/efficiency_guide/processes.xml)328
3 files changed, 303 insertions, 163 deletions
diff --git a/system/doc/efficiency_guide/commoncaveats.xml b/system/doc/efficiency_guide/commoncaveats.xmlsrc
index a0ed5f5b0d..fb004da203 100644
--- a/system/doc/efficiency_guide/commoncaveats.xml
+++ b/system/doc/efficiency_guide/commoncaveats.xmlsrc
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -26,7 +26,7 @@
<docno></docno>
<date>2001-08-08</date>
<rev></rev>
- <file>commoncaveats.xml</file>
+ <file>commoncaveats.xmlsrc</file>
</header>
<p>This section lists a few modules and BIFs to watch out for, not only
@@ -51,6 +51,75 @@
</section>
<section>
+ <title>Accidental Copying and Loss of Sharing</title>
+
+ <p>When spawning a new process using a fun, one can accidentally
+ copy more data to the process than intended. For example:</p>
+
+ <p><em>DO NOT</em></p>
+ <codeinclude file="efficiency_guide.erl" tag="%%acc1" type="erl"/>
+
+ <p>The code in the fun will extract one element from the record
+ and print it. The rest of the <c>state</c> record is not used.
+ However, when the <c>spawn/1</c> function is executed, the entire
+ record is copied to the newly created process.</p>
+
+ <p>The same kind of problem can happen with a map:</p>
+ <p><em>DO NOT</em></p>
+ <codeinclude file="efficiency_guide.erl" tag="%%acc2" type="erl"/>
+
+ <p>In the following example (part of a module implementing the
+ <seeerl marker="stdlib:gen_server">gen_server</seeerl> behavior)
+ the created fun is sent to another process:</p>
+
+ <p><em>DO NOT</em></p>
+ <codeinclude file="efficiency_guide.erl" tag="%%handle_call" type="erl"/>
+
+ <p>How bad that unnecessary copy is depends on the contents of
+ the record or the map.</p>
+
+ <p>For example, if the <c>state</c> record is initialized like
+ this:</p>
+ <codeinclude file="efficiency_guide.erl" tag="%%init1" type="erl"/>
+
+ <p>a list with 10000 elements (or about 20000 heap words) will be
+ copied to the newly created process.</p>
+
+ <p>An unncessary copy of 10000 element list can be bad enough, but it
+ can get even worse if the <c>state</c> record contains <em>shared subterms</em>.
+ Here is a simple example of a term with a shared subterm:</p>
+
+ <code type="erl"><![CDATA[
+{SubTerm, SubTerm}]]></code>
+
+ <p>When a term is copied to another process, sharing of subterms
+ will be lost and the copied term can be many times larger than the
+ original term. For example:</p>
+
+ <codeinclude file="efficiency_guide.erl" tag="%%init2" type="erl"/>
+
+ <p>In the process that calls <c>init2/0</c>, the size of the
+ <c>data</c> field in the <c>state</c> record will be 32 heap
+ words. When the record is copied to the newly created process,
+ sharing will be lost and the size of the copied <c>data</c> field
+ will be 131070 heap words. More details about <seeguide
+ marker="processes#loss-of-sharing">loss off sharing</seeguide> are
+ found in a later section.</p>
+
+ <p>To avoid the problem, outside of the fun extract only the
+ fields of the record that are actually used:</p>
+
+ <p><em>DO</em></p>
+ <codeinclude file="efficiency_guide.erl" tag="%%fixed_acc1" type="erl"/>
+
+ <p>Similarly, outside of the fun extract only the map elements
+ that are actually used:</p>
+
+ <p><em>DO</em></p>
+ <codeinclude file="efficiency_guide.erl" tag="%%fixed_acc2" type="erl"/>
+ </section>
+
+ <section>
<title>list_to_atom/1</title>
<p>Atoms are not garbage-collected. Once an atom is created, it is never
@@ -89,12 +158,12 @@ apply(list_to_atom("some_prefix"++Var), foo, Args)</code>
For example, the following code:</p>
<code type="erl">
foo(L) when length(L) >= 3 ->
- ...</code>
+ ...</code>
<p>can be rewritten to:</p>
<code type="erl">
foo([_,_,_|_]=L) ->
- ...</code>
+ ...</code>
<p>One slight difference is that <c>length(L)</c> fails if <c>L</c>
is an improper list, while the pattern in the second code fragment
@@ -107,7 +176,7 @@ foo([_,_,_|_]=L) ->
<p><seemfa marker="erts:erlang#setelement/3">setelement/3</seemfa>
copies the tuple it modifies. Therefore, updating a tuple in a loop
using <c>setelement/3</c> creates a new copy of the tuple every time.</p>
-
+
<p>There is one exception to the rule that the tuple is copied.
If the compiler clearly can see that destructively updating the tuple would
give the same result as if the tuple was copied, the call to
@@ -118,7 +187,7 @@ foo([_,_,_|_]=L) ->
multiple_setelement(T0) ->
T1 = setelement(9, T0, bar),
T2 = setelement(7, T1, foobar),
- setelement(5, T2, new_value).</code>
+ setelement(5, T2, new_value).</code>
<p>The two following <c>setelement/3</c> calls modify
the tuple in place.</p>
@@ -166,6 +235,4 @@ multiple_setelement(T0) ->
<code type="none">
{Bin1,Bin2} = split_binary(Bin, Num)</code>
</section>
-
</chapter>
-
diff --git a/system/doc/efficiency_guide/efficiency_guide.erl b/system/doc/efficiency_guide/efficiency_guide.erl
index 52e5610c4a..1e71024379 100644
--- a/system/doc/efficiency_guide/efficiency_guide.erl
+++ b/system/doc/efficiency_guide/efficiency_guide.erl
@@ -117,6 +117,7 @@ append([H|T], Tail) ->
append([], Tail) ->
Tail.
+%%kilo_byte
kilo_byte() ->
kilo_byte(10, [42]).
@@ -124,7 +125,8 @@ kilo_byte(0, Acc) ->
Acc;
kilo_byte(N, Acc) ->
kilo_byte(N-1, [Acc|Acc]).
-
+%%kilo_byte
+
recursive_sum([H|T]) ->
H+recursive_sum(T);
recursive_sum([]) -> 0.
@@ -226,3 +228,54 @@ cross_function_receive(Ref) ->
handle_reply(_) -> ok.
handle_error(_) -> ok.
handle_msg(_) -> ok.
+
+%%rec
+-record(state, {info,size,data}).
+%%rec
+
+%%init1
+init1() ->
+ #state{data=lists:seq(1, 10000)}.
+%%init1
+
+%%init2
+init2() ->
+ SharedSubTerms = lists:foldl(fun(_, A) -> [A|A] end, [0], lists:seq(1, 15)),
+ #state{data=Shared}.
+%%init2
+
+%%acc1
+accidental1(State) ->
+ spawn(fun() ->
+ io:format("~p\n", [State#state.info])
+ end).
+%%acc1
+
+%%acc2
+accidental2(State) ->
+ spawn(fun() ->
+ io:format("~p\n", [map_get(info, State)])
+ end).
+%%acc2
+
+%%fixed_acc1
+fixed_accidental1(State) ->
+ Info = State#state.info,
+ spawn(fun() ->
+ io:format("~p\n", [Info])
+ end).
+%%fixed_acc1
+
+%%fixed_acc2
+fixed_accidental2(State) ->
+ Info = map_get(info, State),
+ spawn(fun() ->
+ io:format("~p\n", [Info])
+ end).
+%%fixed_acc2
+
+%%handle_call
+handle_call(give_me_a_fun, _From, State) ->
+ Fun = fun() -> State#state.size =:= 42 end,
+ {reply, Fun, State}.
+%%handle_call
diff --git a/system/doc/efficiency_guide/processes.xml b/system/doc/efficiency_guide/processes.xmlsrc
index cef50c19ea..ad76e31946 100644
--- a/system/doc/efficiency_guide/processes.xml
+++ b/system/doc/efficiency_guide/processes.xmlsrc
@@ -11,7 +11,7 @@
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
-
+
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
@@ -19,7 +19,7 @@
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-
+
</legalnotice>
<title>Processes</title>
@@ -27,7 +27,7 @@
<docno></docno>
<date>2007-11-21</date>
<rev></rev>
- <file>processes.xml</file>
+ <file>processes.xmlsrc</file>
</header>
<section>
@@ -49,7 +49,7 @@ Eshell V5.6 (abort with ^G)
{memory,1232}
3> <input>Bytes div erlang:system_info(wordsize).</input>
309</pre>
-
+
<p>The size includes 233 words for the heap area (which includes the
stack). The garbage collector increases the heap as needed.</p>
@@ -58,7 +58,7 @@ Eshell V5.6 (abort with ^G)
<p><em>DO NOT</em></p>
<code type="erl">
-loop() ->
+loop() ->
receive
{sys, Msg} ->
handle_sys_msg(Msg),
@@ -77,7 +77,7 @@ loop() ->
<p><em>DO</em></p>
<code type="erl">
- loop() ->
+ loop() ->
receive
{sys, Msg} ->
handle_sys_msg(Msg),
@@ -129,92 +129,95 @@ loop() ->
</section>
<section>
- <title>Process Messages</title>
+ <title>Sending Messages</title>
- <p>All data in messages between Erlang processes is copied,
- except for
- <seeguide marker="binaryhandling#refc_binary">refc binaries</seeguide>
- on the same Erlang node.</p>
+ <p>All data in messages sent between Erlang processes is copied,
+ except for <seeguide marker="binaryhandling#refc_binary">refc
+ binaries</seeguide> and <seeguide
+ marker="#literal-pool">literals</seeguide> on the same Erlang
+ node.</p>
<p>When a message is sent to a process on another Erlang node,
- it is first encoded to the Erlang External Format before
- being sent through a TCP/IP socket. The receiving Erlang node decodes
- the message and distributes it to the correct process.</p>
+ it is first encoded to the Erlang External Format before
+ being sent through a TCP/IP socket. The receiving Erlang node decodes
+ the message and distributes it to the correct process.</p>
+ </section>
- <section>
- <marker id="receiving-messages"></marker>
- <title>Receiving messages</title>
+ <section>
+ <marker id="receiving-messages"></marker>
+ <title>Receiving messages</title>
- <p>The cost of receiving messages depends on how complicated the
- <c>receive</c> expression is. Simple expressions that match any
- message are very cheap:</p>
+ <p>The cost of receiving messages depends on how complicated the
+ <c>receive</c> expression is. A simple expression that matches any
+ message is very cheap because it retrieves the first message in the
+ message queue:</p>
- <p><em>DO</em></p>
- <code type="erl"><![CDATA[
- receive
- Message -> handle_msg(Message)
- end.]]></code>
+ <p><em>DO</em></p>
+ <code type="erl"><![CDATA[
+receive
+ Message -> handle_msg(Message)
+end.]]></code>
+
+ <p>However, this is not always convenient: we can receive a message that
+ we do not know how to handle at this point, so it is common to
+ only match the messages we expect:</p>
+
+ <code type="erl"><![CDATA[
+receive
+ {Tag, Message} -> handle_msg(Message)
+end.]]></code>
+
+ <p>While this is convenient it means that the entire message queue must
+ be searched until it finds a matching message. This is very expensive
+ for processes with long message queues, so we have added an
+ optimization for the common case of sending a request and waiting for a
+ response shortly after:</p>
- <p>This is not always convenient however: we can receive a message that
- we do not know how to handle at this point, so it's rather common to
- only match the messages we expect:</p>
+ <p><em>DO</em></p>
+ <code type="erl"><![CDATA[
+MRef = monitor(process, Process),
+Process ! {self(), MRef, Request},
+receive
+ {MRef, Reply} ->
+ erlang:demonitor(MRef, [flush]),
+ handle_reply(Reply);
+ {'DOWN', MRef, _, _, Reason} ->
+ handle_error(Reason)
+end.]]></code>
+
+ <p>Since the compiler knows that the reference created by <c>monitor/2</c>
+ cannot exist before the call (since it is a globally unique identifier),
+ and that the <c>receive</c> only matches messages that contain said
+ reference, it will tell the emulator to search only the messages that
+ arrived after the call to <c>monitor/2</c>.</p>
+
+ <p>The above is a simple example where one is but guaranteed that the
+ optimization will take, but what about more complicated code?</p>
- <code type="erl"><![CDATA[
- receive
- {Tag, Message} -> handle_msg(Message)
- end.]]></code>
+ <section>
+ <marker id="recv_opt_info"></marker>
+ <title>Option recv_opt_info</title>
- <p>While this is convenient it means that the entire message queue must
- be searched until it finds a matching message. This is very expensive
- for processes with long message queues, so we have added an
- optimization for the common case of sending a request and waiting for a
- response shortly after:</p>
+ <p>Use the <c>recv_opt_info</c> option to have the compiler print
+ information about receive optimizations. It can be given either to
+ the compiler or <c>erlc</c>:</p>
- <p><em>DO</em></p>
<code type="erl"><![CDATA[
- MRef = monitor(process, Process),
- Process ! {self(), MRef, Request},
- receive
- {MRef, Reply} ->
- erlang:demonitor(MRef, [flush]),
- handle_reply(Reply);
- {'DOWN', MRef, _, _, Reason} ->
- handle_error(Reason)
- end.]]></code>
-
- <p>Since the compiler knows that the reference created by <c>monitor/2</c>
- cannot exist before the call (since it is a globally unique identifier),
- and that the <c>receive</c> only matches messages that contain said
- reference, it will tell the emulator to search only the messages that
- arrived after the call to <c>monitor/2</c>.</p>
-
- <p>The above is a simple example where one is but guaranteed that the
- optimization will take, but what about more complicated code?</p>
-
- <section>
- <marker id="recv_opt_info"></marker>
- <title>Option recv_opt_info</title>
-
- <p>Use the <c>recv_opt_info</c> option to have the compiler print
- information about receive optimizations. It can be given either to
- the compiler or <c>erlc</c>:</p>
-
- <code type="erl"><![CDATA[
erlc +recv_opt_info Mod.erl]]></code>
- <p>or passed through an environment variable:</p>
+ <p>or passed through an environment variable:</p>
- <code type="erl"><![CDATA[
+ <code type="erl"><![CDATA[
export ERL_COMPILER_OPTIONS=recv_opt_info]]></code>
- <p>Notice that <c>recv_opt_info</c> is not meant to be a permanent
- option added to your <c>Makefile</c>s, because all messages that it
- generates cannot be eliminated. Therefore, passing the option through
- the environment is in most cases the most practical approach.</p>
+ <p>Notice that <c>recv_opt_info</c> is not meant to be a permanent
+ option added to your <c>Makefile</c>s, because all messages that it
+ generates cannot be eliminated. Therefore, passing the option through
+ the environment is in most cases the most practical approach.</p>
- <p>The warnings look as follows:</p>
+ <p>The warnings look as follows:</p>
- <code type="erl"><![CDATA[
+ <code type="erl"><![CDATA[
efficiency_guide.erl:194: Warning: INFO: receive matches any message, this is always fast
efficiency_guide.erl:200: Warning: NOT OPTIMIZED: all clauses do not match a suitable reference
efficiency_guide.erl:206: Warning: OPTIMIZED: reference used to mark a message queue position
@@ -222,38 +225,38 @@ efficiency_guide.erl:208: Warning: OPTIMIZED: all clauses match reference create
efficiency_guide.erl:219: Warning: INFO: passing reference created by make_ref/0 at efficiency_guide.erl:218
efficiency_guide.erl:222: Warning: OPTIMIZED: all clauses match reference in function parameter 1]]></code>
- <p>To make it clearer exactly what code the warnings refer to, the
- warnings in the following examples are inserted as comments
- after the clause they refer to, for example:</p>
+ <p>To make it clearer exactly what code the warnings refer to, the
+ warnings in the following examples are inserted as comments
+ after the clause they refer to, for example:</p>
- <code type="erl"><![CDATA[
+ <code type="erl"><![CDATA[
%% DO
simple_receive() ->
- %% efficiency_guide.erl:194: Warning: INFO: not a selective receive, this is always fast
- receive
- Message -> handle_msg(Message)
- end.
+%% efficiency_guide.erl:194: Warning: INFO: not a selective receive, this is always fast
+receive
+ Message -> handle_msg(Message)
+end.
%% DO NOT, unless Tag is known to be a suitable reference: see
%% cross_function_receive/0 further down.
selective_receive(Tag, Message) ->
- %% efficiency_guide.erl:200: Warning: NOT OPTIMIZED: all clauses do not match a suitable reference
- receive
- {Tag, Message} -> handle_msg(Message)
- end.
+%% efficiency_guide.erl:200: Warning: NOT OPTIMIZED: all clauses do not match a suitable reference
+receive
+ {Tag, Message} -> handle_msg(Message)
+end.
%% DO
optimized_receive(Process, Request) ->
- %% efficiency_guide.erl:206: Warning: OPTIMIZED: reference used to mark a message queue position
+%% efficiency_guide.erl:206: Warning: OPTIMIZED: reference used to mark a message queue position
MRef = monitor(process, Process),
Process ! {self(), MRef, Request},
%% efficiency_guide.erl:208: Warning: OPTIMIZED: matches reference created by monitor/2 at efficiency_guide.erl:206
receive
{MRef, Reply} ->
- erlang:demonitor(MRef, [flush]),
- handle_reply(Reply);
- {'DOWN', MRef, _, _, Reason} ->
- handle_error(Reason)
+ erlang:demonitor(MRef, [flush]),
+ handle_reply(Reply);
+ {'DOWN', MRef, _, _, Reason} ->
+ handle_error(Reason)
end.
%% DO
@@ -268,87 +271,104 @@ cross_function_receive(Ref) ->
receive
{Ref, Message} -> handle_msg(Message)
end.]]></code>
- </section>
</section>
+ </section>
- <section>
- <title>Constant Pool</title>
+ <section>
+ <marker id="literal-pool"/>
+ <title>Literal Pool</title>
- <p>Constant Erlang terms (also called <em>literals</em>) are
- kept in constant pools; each loaded module has its own pool.
- The following function does not build the tuple every time
- it is called (only to have it discarded the next time the garbage
- collector was run), but the tuple is located in the module's
- constant pool:</p>
+ <p>Constant Erlang terms (hereafter called <em>literals</em>) are
+ kept in <em>literal pools</em>; each loaded module has its own pool.
+ The following function does not build the tuple every time
+ it is called (only to have it discarded the next time the garbage
+ collector was run), but the tuple is located in the module's
+ literal pool:</p>
<p><em>DO</em></p>
- <code type="erl">
+ <code type="erl"><![CDATA[
days_in_month(M) ->
- element(M, {31,28,31,30,31,30,31,31,30,31,30,31}).</code>
-
- <p>But if a constant is sent to another process (or stored in
- an Ets table), it is <em>copied</em>.
- The reason is that the runtime system must be able
- to keep track of all references to constants to unload code
- containing constants properly. (When the code is unloaded,
- the constants are copied to the heap of the processes that refer
- to them.) The copying of constants might be eliminated in a future
- Erlang/OTP release.</p>
- </section>
+ element(M, {31,28,31,30,31,30,31,31,30,31,30,31}).]]></code>
- <section>
- <title>Loss of Sharing</title>
+ <p>If a literal, or a term that contains a literal, is inserted
+ into an Ets table, it is <em>copied</em>. The reason is that the
+ module containing the literal can be unloaded in the future.</p>
- <p>Shared subterms are <em>not</em> preserved in the following
- cases:</p>
- <list type="bulleted">
- <item>When a term is sent to another process</item>
- <item>When a term is passed as the initial process arguments in
- the <c>spawn</c> call</item>
- <item>When a term is stored in an Ets table</item>
- </list>
- <p>That is an optimization. Most applications do not send messages
- with shared subterms.</p>
+ <p>When a literal is sent to another process, it is <em>not</em>
+ copied. When a module holding a literal is unloaded, the literal
+ will be copied to the heap of all processes that hold references
+ to that literal.</p>
- <p>The following example shows how a shared subterm can be created:</p>
+ <p>There also exists a global literal pool that is managed by the
+ <seeerl marker="erts:persistent_term">persistent_term</seeerl>
+ module.</p>
- <code type="erl">
-kilo_byte() ->
- kilo_byte(10, [42]).
+ <p>By default, 1 GB of virtual address space is reserved for all
+ literal pools (in BEAM code and persistent terms). The amount of
+ virtual address space reserved for literals can be changed by
+ using the <seecref marker="erts:erts_alloc#MIscs"><c>+MIscs
+ option</c></seecref> when starting the emulator.</p>
-kilo_byte(0, Acc) ->
- Acc;
-kilo_byte(N, Acc) ->
- kilo_byte(N-1, [Acc|Acc]).</code>
+ <p>Here is an example how the reserved virtual address space for
+ literals can be raised to 2 GB (2048 MB):</p>
- <p><c>kilo_byte/1</c> creates a deep list.
- If <c>list_to_binary/1</c> is called, the deep list can be
- converted to a binary of 1024 bytes:</p>
+ <pre>
+ erl +MIscs 2048</pre>
+ </section>
- <pre>
+ <section>
+ <marker id="loss-of-sharing"></marker>
+ <title>Loss of Sharing</title>
+
+ <p>An Erlang term can have shared subterms. Here is a simple
+ example:</p>
+
+ <code type="erl"><![CDATA[
+{SubTerm, SubTerm}]]></code>
+
+ <p>Shared subterms are <em>not</em> preserved in the following
+ cases:</p>
+ <list type="bulleted">
+ <item>When a term is sent to another process</item>
+ <item>When a term is passed as the initial process arguments in
+ the <c>spawn</c> call</item>
+ <item>When a term is stored in an Ets table</item>
+ </list>
+ <p>That is an optimization. Most applications do not send messages
+ with shared subterms.</p>
+
+ <p>The following example shows how a shared subterm can be created:</p>
+
+ <codeinclude file="efficiency_guide.erl" tag="%%kilo_byte" type="erl"/>
+
+ <p><c>kilo_byte/1</c> creates a deep list.
+ If <c>list_to_binary/1</c> is called, the deep list can be
+ converted to a binary of 1024 bytes:</p>
+
+ <pre>
1> <input>byte_size(list_to_binary(efficiency_guide:kilo_byte())).</input>
1024</pre>
- <p>Using the <c>erts_debug:size/1</c> BIF, it can be seen that the
- deep list only requires 22 words of heap space:</p>
+ <p>Using the <c>erts_debug:size/1</c> BIF, it can be seen that the
+ deep list only requires 22 words of heap space:</p>
- <pre>
+ <pre>
2> <input>erts_debug:size(efficiency_guide:kilo_byte()).</input>
22</pre>
- <p>Using the <c>erts_debug:flat_size/1</c> BIF, the size of the
- deep list can be calculated if sharing is ignored. It becomes
- the size of the list when it has been sent to another process
- or stored in an Ets table:</p>
+ <p>Using the <c>erts_debug:flat_size/1</c> BIF, the size of the
+ deep list can be calculated if sharing is ignored. It becomes
+ the size of the list when it has been sent to another process
+ or stored in an Ets table:</p>
- <pre>
+ <pre>
3> <input>erts_debug:flat_size(efficiency_guide:kilo_byte()).</input>
4094</pre>
- <p>It can be verified that sharing will be lost if the data is
- inserted into an Ets table:</p>
+ <p>It can be verified that sharing will be lost if the data is
+ inserted into an Ets table:</p>
- <pre>
+ <pre>
4> <input>T = ets:new(tab, []).</input>
#Ref&lt;0.1662103692.2407923716.214181>
5> <input>ets:insert(T, {key,efficiency_guide:kilo_byte()}).</input>
@@ -358,13 +378,14 @@ true
7> <input>erts_debug:flat_size(element(2, hd(ets:lookup(T, key)))).</input>
4094</pre>
- <p>When the data has passed through an Ets table,
- <c>erts_debug:size/1</c> and <c>erts_debug:flat_size/1</c>
- return the same value. Sharing has been lost.</p>
+ <p>When the data has passed through an Ets table,
+ <c>erts_debug:size/1</c> and <c>erts_debug:flat_size/1</c>
+ return the same value. Sharing has been lost.</p>
- <p>In a future Erlang/OTP release, it might be implemented a
- way to (optionally) preserve sharing.</p>
- </section>
+ <p>It is possible to build an <em>experimental</em> variant of the
+ runtime system that will preserve sharing when copying terms by
+ giving the <c>--enable-sharing-preserving</c> option to the
+ <c>configure</c> script.</p>
</section>
<section>
@@ -385,4 +406,3 @@ true
is active, while the others wait in a <c>receive</c> statement.</p>
</section>
</chapter>
-