原文地址
http://noss.github.com/2009/04/02/constant-pool-erlang-hack.html
2009-04-02
Erlang R12B-0 added a per-module memory area for constants, the literal values in a module are stored there. Before, they were allocated on the heap every time they were referenced. This meant that some kinds of optimization that avoided literal lookup-tables became irrelevant in one go (without even recompiling the source). A great example of the kind of improvements that OTP focus on: removing speed-bumps to having beautiful code.
From the release notes of R12B-0
OTP-6850: Literal lists, tuples, and binaries are no longer constructed at run-time as they used to be, but are stored in a per-module constant pool. Literals that are used more than once are stored only once.
This is not a change to the language, only in the details of its implementation. Therefore, the implications of this change is described in the Efficiency Guide.
The erlang efficiency guide on constant pools pretty much say the same thing. But Björn Gustavsson adds this very interesting remark about unloading a module and the constant pool.
If one has very assymetric access patterns to some value, Maybe millions of times more reads than updates, and this is a measured problem, one can reach for hacks such as generating a module containing the values as literals and thus have a global configuration value that will not grow your heap unecessary.
But…
As always, remember when to optimize.
-------------------------------------------------------------------------
在对beam_load.c:read_literal_table(LoaderState* stp)函数打了补丁 显示出这个模块的literal数目和类型我们可以看到这样的结果:
module base64: num_literals[4]
0: tag[1], size[5], term["="]
1: tag[1], size[6], term["=="]
2: tag[3], size[131], term[{65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,48,49,50,51,52,53,54,55,56,57,43,47}]
3: tag[3], size[1086], term[{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}]
这个是标准库base64.erl的literal.
从统计可以看出 atom本身已经不是literal, 这里大部分的literal是1,3的类型也是据说是tuple和list, 0是代表binary.
你在程序里面形如:
[1,2,3,4].
{1,2,3,4}.
<<1,2,3,4>>.
的编译器常量就会被放在模块的literal表格里面 在beam加载的时候 这些常量就创建完毕在内存里,同时调整涉及这些常量的模块的op code,让它们直接使用这些常量,而不是每次新建一个。
这样的优化对于base64或者c程序出身的人很有帮助,应为我们最擅长查表格,常数的查询时间。那么现在我们也可以在erlang下这么用了,看下base64.erl代码
%% arrays for character translation
%% value -> base64 (zero-based)
encode_tuple() ->
{$A, $B, $C, $D, $E, $F, $G, $H, $I, $J, $K, $L, $M, $N,
$O, $P, $Q, $R, $S, $T, $U, $V, $W, $X, $Y, $Z,
$a, $b, $c, $d, $e, $f, $g, $h, $i, $j, $k, $l, $m, $n,
$o, $p, $q, $r, $s, $t, $u, $v, $w, $x, $y, $z,
$0, $1, $2, $3, $4, $5, $6, $7, $8, $9, $+, $/}.
%% base64 -> value (one-based)
decode_tuple() ->
{-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63,
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,
-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}.
%% accessors
b64e(X, T) ->
element(X+1, T).
b64d(X, T) ->
b64d_ok(element(X, T)).
-spec b64d_ok(non_neg_integer()) -> non_neg_integer().
b64d_ok(N) when N >= 0 ->
N.
舒服吧! 很多时候如果你的erl程序设计到常量的问题 有效率问题的时候 你可以动态生成一个erl文件然后用编译器编译再动态加载,最大程度的提高效率。 你的这些常量不参与GC, 无需创建,无需拷贝,这就是它的意义!