~ubuntu-branches/debian/squeeze/erlang/squeeze

« back to all changes in this revision

Viewing changes to system/doc/efficiency_guide/functions.xml

  • Committer: Bazaar Package Importer
  • Author(s): Sergei Golovan
  • Date: 2010-03-09 17:34:57 UTC
  • mfrom: (10.1.2 sid)
  • Revision ID: james.westby@ubuntu.com-20100309173457-4yd6hlcb2osfhx31
Tags: 1:13.b.4-dfsg-3
Manpages in section 1 are needed even if only arch-dependent packages are
built. So, re-enabled them.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?xml version="1.0" encoding="latin1" ?>
 
2
<!DOCTYPE chapter SYSTEM "chapter.dtd">
 
3
 
 
4
<chapter>
 
5
  <header>
 
6
    <copyright>
 
7
      <year>2001</year><year>2009</year>
 
8
      <holder>Ericsson AB. All Rights Reserved.</holder>
 
9
    </copyright>
 
10
    <legalnotice>
 
11
      The contents of this file are subject to the Erlang Public License,
 
12
      Version 1.1, (the "License"); you may not use this file except in
 
13
      compliance with the License. You should have received a copy of the
 
14
      Erlang Public License along with this software. If not, it can be
 
15
      retrieved online at http://www.erlang.org/.
 
16
    
 
17
      Software distributed under the License is distributed on an "AS IS"
 
18
      basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 
19
      the License for the specific language governing rights and limitations
 
20
      under the License.
 
21
    
 
22
    </legalnotice>
 
23
 
 
24
    <title>Functions</title>
 
25
    <prepared>Bjorn Gustavsson</prepared>
 
26
    <docno></docno>
 
27
    <date>2007-11-22</date>
 
28
    <rev></rev>
 
29
    <file>functions.xml</file>
 
30
  </header>
 
31
 
 
32
  <section>
 
33
    <title>Pattern matching</title>
 
34
    <p>Pattern matching in function head and in <c>case</c> and <c>receive</c>
 
35
     clauses are optimized by the compiler. With a few exceptions, there is nothing
 
36
     to gain by rearranging clauses.</p>
 
37
 
 
38
    <p>One exception is pattern matching of binaries. The compiler
 
39
    will not rearrange clauses that match binaries. Placing the
 
40
    clause that matches against the empty binary <em>last</em> will usually
 
41
    be slightly faster than placing it <em>first</em>.</p>
 
42
 
 
43
    <p>Here is a rather contrived example to show another exception:</p>
 
44
 
 
45
    <p><em>DO NOT</em></p>
 
46
    <code type="erl">
 
47
atom_map1(one) -> 1;
 
48
atom_map1(two) -> 2;
 
49
atom_map1(three) -> 3;
 
50
atom_map1(Int) when is_integer(Int) -> Int;
 
51
atom_map1(four) -> 4;
 
52
atom_map1(five) -> 5;
 
53
atom_map1(six) -> 6.</code>
 
54
 
 
55
     <p>The problem is the clause with the variable <c>Int</c>.
 
56
     Since a variable can match anything, including the atoms
 
57
     <c>four</c>, <c>five</c>, and <c>six</c> that the following clauses
 
58
     also will match, the compiler must generate sub-optimal code that will
 
59
     execute as follows:</p>
 
60
 
 
61
     <p>First the input value is compared to <c>one</c>, <c>two</c>, and
 
62
     <c>three</c> (using a single instruction that does a binary search;
 
63
     thus, quite efficient even if there are many values) to select which
 
64
     one of the first three clauses to execute (if any).</p>
 
65
 
 
66
     <p>If none of the first three clauses matched, the fourth clause
 
67
     will match since a variable always matches. If the guard test
 
68
     <c>is_integer(Int)</c> succeeds, the fourth clause will be
 
69
     executed.</p>
 
70
 
 
71
     <p>If the guard test failed, the input value is compared to
 
72
     <c>four</c>, <c>five</c>, and <c>six</c>, and the appropriate clause
 
73
     is selected. (There will be a <c>function_clause</c> exception if
 
74
     none of the values matched.)</p>
 
75
 
 
76
     <p>Rewriting to either</p>
 
77
 
 
78
     <p><em>DO</em></p>
 
79
     <code type="erl"><![CDATA[
 
80
atom_map2(one) -> 1;
 
81
atom_map2(two) -> 2;
 
82
atom_map2(three) -> 3;
 
83
atom_map2(four) -> 4;
 
84
atom_map2(five) -> 5;
 
85
atom_map2(six) -> 6;
 
86
atom_map2(Int) when is_integer(Int) -> Int.]]></code>
 
87
 
 
88
     <p>or</p> 
 
89
 
 
90
     <p><em>DO</em></p>
 
91
     <code type="erl"><![CDATA[
 
92
atom_map3(Int) when is_integer(Int) -> Int;
 
93
atom_map3(one) -> 1;
 
94
atom_map3(two) -> 2;
 
95
atom_map3(three) -> 3;
 
96
atom_map3(four) -> 4;
 
97
atom_map3(five) -> 5;
 
98
atom_map3(six) -> 6.]]></code>
 
99
 
 
100
     <p>will give slightly more efficient matching code.</p>
 
101
 
 
102
     <p>Here is a less contrived example:</p>
 
103
 
 
104
     <p><em>DO NOT</em></p>
 
105
     <code type="erl"><![CDATA[
 
106
map_pairs1(_Map, [], Ys) ->
 
107
    Ys;
 
108
map_pairs1(_Map, Xs, [] ) ->
 
109
    Xs;
 
110
map_pairs1(Map, [X|Xs], [Y|Ys]) ->
 
111
    [Map(X, Y)|map_pairs1(Map, Xs, Ys)].]]></code>
 
112
 
 
113
     <p>The first argument is <em>not</em> a problem. It is variable, but it
 
114
     is a variable in all clauses. The problem is the variable in the second
 
115
     argument, <c>Xs</c>, in the middle clause. Because the variable can
 
116
     match anything, the compiler is not allowed to rearrange the clauses,
 
117
     but must generate code that matches them in the order written.</p>
 
118
 
 
119
     <p>If the function is rewritten like this</p>
 
120
 
 
121
     <p><em>DO</em></p>
 
122
     <code type="erl"><![CDATA[
 
123
map_pairs2(_Map, [], Ys) ->
 
124
    Ys;
 
125
map_pairs2(_Map, [_|_]=Xs, [] ) ->
 
126
    Xs;
 
127
map_pairs2(Map, [X|Xs], [Y|Ys]) ->
 
128
    [Map(X, Y)|map_pairs2(Map, Xs, Ys)].]]></code>
 
129
 
 
130
    <p>the compiler is free rearrange the clauses. It will generate code
 
131
    similar to this</p>
 
132
 
 
133
    <p><em>DO NOT (already done by the compiler)</em></p>
 
134
    <code type="erl"><![CDATA[
 
135
explicit_map_pairs(Map, Xs0, Ys0) ->
 
136
    case Xs0 of
 
137
        [X|Xs] ->
 
138
            case Ys0 of
 
139
                [Y|Ys] ->
 
140
                    [Map(X, Y)|explicit_map_pairs(Map, Xs, Ys)];
 
141
                [] ->
 
142
                    Xs0
 
143
            end;
 
144
        [] ->
 
145
            Ys0
 
146
    end.]]></code>
 
147
      
 
148
    <p>which should be slightly faster for presumably the most common case
 
149
    that the input lists are not empty or very short.
 
150
    (Another advantage is that Dialyzer is able to deduce a better type
 
151
    for the variable <c>Xs</c>.)</p>
 
152
  </section>
 
153
 
 
154
  <section>
 
155
    <title>Function Calls </title>
 
156
 
 
157
    <p>Here is an intentionally rough guide to the relative costs of
 
158
    different kinds of calls. It is based on benchmark figures run on
 
159
    Solaris/Sparc:</p>
 
160
 
 
161
    <list type="bulleted">
 
162
    <item>Calls to local or external functions (<c>foo()</c>, <c>m:foo()</c>)
 
163
    are the fastest kind of calls.</item>
 
164
    <item>Calling or applying a fun (<c>Fun()</c>, <c>apply(Fun, [])</c>)
 
165
    is about <em>three times</em> as expensive as calling a local function.</item>
 
166
    <item>Applying an exported function (<c>Mod:Name()</c>,
 
167
    <c>apply(Mod, Name, [])</c>) is about twice as expensive as calling a fun,
 
168
    or about <em>six times</em> as expensive as calling a local function.</item>
 
169
    </list>
 
170
 
 
171
    <section>
 
172
       <title>Notes and implementation details</title>
 
173
 
 
174
       <p>Calling and applying a fun does not involve any hash-table lookup.
 
175
       A fun contains an (indirect) pointer to the function that implements
 
176
       the fun.</p>
 
177
 
 
178
       <warning><p><em>Tuples are not fun(s)</em>.
 
179
       A "tuple fun", <c>{Module,Function}</c>, is not a fun.
 
180
       The cost for calling a "tuple fun" is similar to that
 
181
       of <c>apply/3</c> or worse. Using "tuple funs" is <em>strongly discouraged</em>,
 
182
       as they may not be supported in a future release.</p></warning>
 
183
 
 
184
       <p><c>apply/3</c> must look up the code for the function to execute
 
185
       in a hash table. Therefore, it will always be slower than a
 
186
       direct call or a fun call.</p>
 
187
 
 
188
       <p>It no longer matters (from a performance point of view)
 
189
       whether you write</p>
 
190
 
 
191
       <code type="erl">
 
192
Module:Function(Arg1, Arg2)</code>
 
193
 
 
194
       <p>or</p>
 
195
 
 
196
       <code type="erl">
 
197
apply(Module, Function, [Arg1,Arg2])</code>
 
198
 
 
199
       <p>(The compiler internally rewrites the latter code into the former.)</p>
 
200
 
 
201
       <p>The following code</p>
 
202
 
 
203
       <code type="erl">
 
204
apply(Module, Function, Arguments)</code>
 
205
 
 
206
       <p>is slightly slower because the shape of the list of arguments
 
207
       is not known at compile time.</p>
 
208
    </section>
 
209
  </section>
 
210
 
 
211
  <section>
 
212
    <title>Memory usage in recursion</title>
 
213
    <p>When writing recursive functions it is preferable to make them
 
214
      tail-recursive so that they can execute in constant memory space.</p>
 
215
    <p><em>DO</em></p>
 
216
    <code type="none">
 
217
list_length(List) ->
 
218
    list_length(List, 0).
 
219
 
 
220
list_length([], AccLen) -> 
 
221
    AccLen; % Base case
 
222
 
 
223
list_length([_|Tail], AccLen) ->
 
224
    list_length(Tail, AccLen + 1). % Tail-recursive</code>
 
225
    <p><em>DO NOT</em></p>
 
226
    <code type="none">
 
227
list_length([]) ->
 
228
    0. % Base case
 
229
list_length([_ | Tail]) ->
 
230
    list_length(Tail) + 1. % Not tail-recursive</code>
 
231
  </section>
 
232
 
 
233
</chapter>
 
234