summaryrefslogtreecommitdiff
path: root/system/doc/efficiency_guide/commoncaveats.xmlsrc
diff options
context:
space:
mode:
Diffstat (limited to 'system/doc/efficiency_guide/commoncaveats.xmlsrc')
-rw-r--r--system/doc/efficiency_guide/commoncaveats.xmlsrc238
1 files changed, 238 insertions, 0 deletions
diff --git a/system/doc/efficiency_guide/commoncaveats.xmlsrc b/system/doc/efficiency_guide/commoncaveats.xmlsrc
new file mode 100644
index 0000000000..fb004da203
--- /dev/null
+++ b/system/doc/efficiency_guide/commoncaveats.xmlsrc
@@ -0,0 +1,238 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2001</year><year>2020</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ 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
+ distributed under the License is distributed on an "AS IS" BASIS,
+ 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>Common Caveats</title>
+ <prepared>Bjorn Gustavsson</prepared>
+ <docno></docno>
+ <date>2001-08-08</date>
+ <rev></rev>
+ <file>commoncaveats.xmlsrc</file>
+ </header>
+
+ <p>This section lists a few modules and BIFs to watch out for, not only
+ from a performance point of view.</p>
+
+ <section>
+ <title>Timer Module</title>
+
+ <p>Creating timers using <seemfa
+ marker="erts:erlang#send_after/3">erlang:send_after/3</seemfa>
+ and
+ <seemfa marker="erts:erlang#start_timer/3">erlang:start_timer/3</seemfa>,
+ is much more efficient than using the timers provided by the
+ <seeerl marker="stdlib:timer">timer</seeerl> module in STDLIB.
+ The <c>timer</c> module uses a separate process to manage the timers.
+ That process can easily become overloaded if many processes
+ create and cancel timers frequently.</p>
+
+ <p>The functions in the <c>timer</c> module that do not manage timers
+ (such as <c>timer:tc/3</c> or <c>timer:sleep/1</c>), do not call the
+ timer-server process and are therefore harmless.</p>
+ </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
+ removed. The emulator terminates if the limit for the number
+ of atoms (1,048,576 by default) is reached.</p>
+
+ <p>Therefore, converting arbitrary input strings to atoms can be
+ dangerous in a system that runs continuously.
+ If only certain well-defined atoms are allowed as input,
+ <seemfa marker="erts:erlang#list_to_existing_atom/1">list_to_existing_atom/1</seemfa>
+ can be used
+ to guard against a denial-of-service attack. (All atoms that are allowed
+ must have been created earlier, for example, by simply using all of them
+ in a module and loading that module.)</p>
+
+ <p>Using <c>list_to_atom/1</c> to construct an atom that is passed to
+ <c>apply/3</c> as follows, is quite expensive and not recommended
+ in time-critical code:</p>
+ <code type="erl">
+apply(list_to_atom("some_prefix"++Var), foo, Args)</code>
+ </section>
+
+ <section>
+ <title>length/1</title>
+
+ <p>The time for calculating the length of a list is proportional to the
+ length of the list, as opposed to <c>tuple_size/1</c>, <c>byte_size/1</c>,
+ and <c>bit_size/1</c>, which all execute in constant time.</p>
+
+ <p>Normally, there is no need to worry about the speed of <c>length/1</c>,
+ because it is efficiently implemented in C. In time-critical code,
+ you might want to avoid it if the input list could potentially be very
+ long.</p>
+
+ <p>Some uses of <c>length/1</c> can be replaced by matching.
+ For example, the following code:</p>
+ <code type="erl">
+foo(L) when length(L) >= 3 ->
+ ...</code>
+
+ <p>can be rewritten to:</p>
+ <code type="erl">
+foo([_,_,_|_]=L) ->
+ ...</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
+ accepts an improper list.</p>
+ </section>
+
+ <section>
+ <title>setelement/3</title>
+
+ <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
+ <c>setelement/3</c> is replaced with a special destructive <c>setelement</c>
+ instruction. In the following code sequence, the first <c>setelement/3</c>
+ call copies the tuple and modifies the ninth element:</p>
+ <code type="erl">
+multiple_setelement(T0) ->
+ T1 = setelement(9, T0, bar),
+ T2 = setelement(7, T1, foobar),
+ setelement(5, T2, new_value).</code>
+
+ <p>The two following <c>setelement/3</c> calls modify
+ the tuple in place.</p>
+
+ <p>For the optimization to be applied, <em>all</em> the followings conditions
+ must be true:</p>
+
+ <list type="bulleted">
+ <item>The indices must be integer literals, not variables or expressions.</item>
+ <item>The indices must be given in descending order.</item>
+ <item>There must be no calls to another function in between the calls to
+ <c>setelement/3</c>.</item>
+ <item>The tuple returned from one <c>setelement/3</c> call must only be used
+ in the subsequent call to <c>setelement/3</c>.</item>
+ </list>
+
+ <p>If the code cannot be structured as in the <c>multiple_setelement/1</c>
+ example, the best way to modify multiple elements in a large tuple is to
+ convert the tuple to a list, modify the list, and convert it back to
+ a tuple.</p>
+ </section>
+
+ <section>
+ <title>size/1</title>
+
+ <p><c>size/1</c> returns the size for both tuples and binaries.</p>
+
+ <p>Using the BIFs <c>tuple_size/1</c> and <c>byte_size/1</c>
+ gives the compiler and the runtime system more opportunities for
+ optimization. Another advantage is that the BIFs give Dialyzer more
+ type information.</p>
+ </section>
+
+ <section>
+ <title>split_binary/2</title>
+ <p>It is usually more efficient to split a binary using matching
+ instead of calling the <c>split_binary/2</c> function.
+ Furthermore, mixing bit syntax matching and <c>split_binary/2</c>
+ can prevent some optimizations of bit syntax matching.</p>
+
+ <p><em>DO</em></p>
+ <code type="none"><![CDATA[
+ <<Bin1:Num/binary,Bin2/binary>> = Bin,]]></code>
+ <p><em>DO NOT</em></p>
+ <code type="none">
+ {Bin1,Bin2} = split_binary(Bin, Num)</code>
+ </section>
+</chapter>