之前,在 [Erlang 0126] 我们读过的Erlang论文 提到过下面这篇论文:

On Preserving Term Sharing in the Erlang Virtual Machine
地址: http://user.it.uu.se/~kostis/Papers/erlang12_sharing.pdf 
摘要:In this paper we describe our experiences and argue through examples why flattening terms during copying is not a good idea for
a language like Erlang. More importantly, we propose a sharing preserving copying mechanism for Erlang/OTP and describe a pub-
licly available complete implementation of this mechanism.

Term Sharing  数据项共享这个东西不新鲜,"Efficiency Guide User's Guide"的4.2章节"Constructing binaries"[ 链接 ] 和 8.2 章节的"Loss of sharing" [链接]就提到过(再次吐槽一下Erlang文档组织,一个主题往往分散在多个文档里面,需要耐心).不仅仅是Binary数据类型,Term Sharing 是一个通用的问题.在Guide中提到了强制拷贝的场景:发送到别的进程或者插入到ETS. 下面是文档摘抄的:
   Loss of sharing
  
  Shared sub-terms are not preserved when a term is sent to another process, passed as the initial process arguments in the spawn call, or stored in an ETS table. That is an optimization. Most applications do not send messages with shared sub-terms.
 
   数据拷贝过程,Erlang会进行两次遍历.第一次遍历,会计算flat size(erts/emulator/beam/copy.c中的size_object 方法)然后为此分配对应的内存,第二次遍历完成实际的拷贝 (function copy_structin erts/emulator/beam/copy.c).

首先,我们写一个简单的代码演示一下后面要频繁用到的erts_debug:size/1和erts_debug:flat_size/1
1
2
3
4
5
6
7
8
s3(L)->
    L2=[L,L,L,L],
    {{erts_debug:size(L),erts_debug:flat_size(L)},
      {erts_debug:size(L2),erts_debug:flat_size(L2)}}
.
 
9> d:s3([1,2,3,4,5,6]).
{{12,12},{20,56}}
  下面在shell这段代码,分别演示了spawn,消息发送,以及插入ETS.
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
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
52
53
54
55
56
57
58
59
60
61
Eshell V6.0  (abort with ^G)
1> L=[1,2,3,4,5,6,7,8,9,10].
[1,2,3,4,5,6,7,8,9,10]
2>  L2=[L,L,L,L,L,L].
[[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10]]
3>  erts_debug:size(L2).
32
4>  erts_debug:flat_size(L2).
132
5>  spawn(fun () ->receive Data ->  io:format("~p",[erts_debug:size(Data)]) end end).
<0.39.0>
6> v(5) ! L2.
132[[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10]]
7>  erts_debug:size(L2).
32
8> ets:new(test,[named_table]).
test
9> ets:insert(test,{1,L2}).
true
10>  ets:lookup(test ,1).
[{1,
  [[1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10]]}]
11> [{1,Data}]=v(10).
[{1,
  [[1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10],
   [1,2,3,4,5,6,7,8,9,10]]}]
12> Data.
[[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10],
[1,2,3,4,5,6,7,8,9,10]]
13>  erts_debug:size(Data).
132
14> spawn(d,test,[L2]).
132<0.54.0>
 
 test(Data)->
   io:format("~p",[erts_debug:size(Data)]).
 
   
  除了上面的情况,还有一些潜在的情况也会导致数据展开,比如上面提到的论文里面设计的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
show_printing_may_be_bad() ->
  F = fun (N) ->
  T = now(),
  L = mklist(N),
  S = erts_debug:size(L),
  io:format("mklist(~w), size ~w, ", [N, S]),
  io:format("is ~P, ", [L, 2]), %%% BAD !!!
  D = timer:now_diff(now(), T),
  io:format("in ~.3f sec.~n", [D/1000000])
   end,
   lists:foreach(F, [10, 20, 22, 24, 26, 28, 30]).
 
mklist(0) -> 0;
mklist(M) -> X = mklist(M-1), [X, X].
  io:format("is ~P, ", [L, 2]), %%% BAD !!!这行代码删掉前后分别执行代码,在过机器上得到的结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Eshell V6.0  (abort with ^G)
1>  d:show_printing_may_be_bad().
mklist(10), size 40, in 0.001 sec.
mklist(20), size 80, in 0.000 sec.
mklist(22), size 88, in 0.000 sec.
mklist(24), size 96, in 0.000 sec.
mklist(26), size 104, in 0.000 sec.
mklist(28), size 112, in 0.000 sec.
mklist(30), size 120, in 0.000 sec.
ok
 
 
Eshell V6.0  (abort with ^G)
1>  d:show_printing_may_be_bad().
mklist(10), size 40, is [[...]|...], in 0.001 sec.
mklist(20), size 80, is [[...]|...], in 0.110 sec.
mklist(22), size 88, is [[...]|...], in 0.421 sec.
mklist(24), size 96, is [[...]|...], in 43.105 sec.
mklist(26), size 104,
Crash dump was written to: erl_crash.dump
eheap_alloc: Cannot allocate 3280272216 bytes of memory (of type "heap").
rlwrap: warning: erl killed by SIGABRT.
rlwrap has not crashed, but for transparency,
it will now kill itself (without dumping core)with the same signal
   很明显看到有这行代码的版本不仅执行时间长,而且需要大量内存.

为什么会出现这种情况呢?就是上面提到的"Loss of sharing",为什么会触发数据展开(或者说数据平铺化)呢?之前我们曾经聊过关于io:format的事情( [Erlang 0041] 详解io:format [链接]),在Erlang/OTP中I/O是通过向I/O Server发起I/O请求实现的.io:format调用实际上是向I/O 发送了一个io request消息,剩下的就由IO Server处理了.虽然上面的L被简略的输出为" [[...]|...]"但是在消息的传递过程中已经触发了数据平铺然后拷贝;

这种问题实际上是非常隐蔽的,所以通过宏选项在release生成的时候清理掉所有io:format输出是非常有必要的。

本文转自博客园坚强2002的博客,原文链接: http://www.cnblogs.com/me-sa/p/term_sharing_in_erlang_otp_one.html
如需转载请自行联系原博主。

[Erlang 0127] Term sharing in Erlang/OTP 上篇相关推荐

  1. Erlang 入门——从普通tcp到OTP框架通信

    根据Erlang的语言特点,Erlang创建进程就如同Java创建对象那样简单.而Erlang的OTP框架,可以理解为是Java的Spring框架. 刚入门Erlang的tcp通信,书上的写法是根据s ...

  2. linux查看erlang安装目录,Linux 安装Erlang

    Erlang目前已经是Fedora和Debian/Ubuntu软件仓库中的一部分. Erlang目前最新的版本是OTP 17.0.Erlang是一种编程语言,用于构建大规模.高可伸缩性.高可用性的软实 ...

  3. Erlang基础 -- 介绍 -- 历史及Erlang并发

    前言 最近在总结一些Erlang编程语言的基础知识,拟系统的介绍Erlang编程语言,从基础到进阶,然后再做Erlang编程语言有意思的库的分析. 其实,还是希望越来越多的人关注Erlang,使用Er ...

  4. 学习:erlang的term反序列化,string转换为term

    一. string_to_term(String) ->     case erl_scan:string(String++".") of         {ok, Toke ...

  5. Erlang程序设计笔记---(第三节 Erlang的基本概念)

    Erlang的基本概念 一. 简单的整数运算 Erlang遵循算术表达式的一般规则 Erlang可以用任意长度的整数执行整数运算.在Erlang里,整数运算是精确的,因此无需担 心运算溢出或无法用特定 ...

  6. 【Erlang新手成长日记】Erlang开源项目推荐

    学习一门新语言,需要多看,多想,多写. 多看,就要阅读优秀的源代码. 以下是自己找到4款优秀的Erlang开源项目: Cowboy HTTP服务器 简介: Cowboy是一款小而快速的模块化HTTP服 ...

  7. erlang底层c定时器设计-Erlang源码学习二

    Erlang底层的定时器实现位于源码的erts/emulator/beam/time.c文件,用时间轮的方式动态添加和删除定时器,结构体名为typedef struct ErtsTimerWheel_ ...

  8. Erlang/OTP设计原则(文档翻译)

    http://erlang.org/doc/design_principles/des_princ.html 图和代码皆源自以上链接中Erlang官方文档,翻译时的版本为20.1. 这个设计原则,其实 ...

  9. CentOS 6 5安装Erlang/OTP 17 0

    CentOS 6.5安装Erlang/OTP 17.0 作者:chszs,转载需注明.博客主页:http://blog.csdn.net/chszs Erlang目前已经是Fedora和Debian/ ...

  10. 【erlang】字符串转成term

    字符串转成erlang的term -module(test). -compile(export_all).string_to_term(String) ->case erl_scan:strin ...

最新文章

  1. HTML5 script 标签的 crossorigin 和integrity属性的作用
  2. Struct 和 Class 性能有差异吗?自己测试
  3. Oracle 11g Release 2 (11.2) for Microsoft Windows (32-Bit)安装与卸除
  4. VTK:图片之ImageShiftScale
  5. onesignal php,PHP FPM源代码反刍品味之五:信号signal处理
  6. Intel处理器CPUID指令学习
  7. mysql主从同步搭建过程_mysql 主从复制搭建详细步骤
  8. Python 第六节课
  9. myeclipse复制的文件代码乱码
  10. [线性代数] 1.2 全排列和对换
  11. Guava-Joiner工具类
  12. 总算了解了什么叫云计算
  13. flink 滚动窗口、滑动窗口、会话窗口、全局窗口
  14. 红外图像盲元检测matlab,红外焦平面阵列盲元类型与判别.pdf
  15. 使用讯飞语音识别的空指针错误
  16. 史上最全的中高级JAVA工程师-面试题汇总
  17. JavaScript数据结构和算法笔记一(前八章)
  18. 织梦响应式精密机械模具类网站织梦模板(自适应手机端)
  19. python虚拟环境管理器
  20. 信息系统项目管理:软件开发生命周期模型的选择比较

热门文章

  1. Want VS Needs,产品经理基于场景的需求挖掘
  2. RAC架构之业务分割
  3. 设计没有标准,只有目标
  4. 无法添加外键约束的原因(cannot add foreign key constraint)
  5. 计算机 大文件查找,win7笔记本电脑如何快速查找大文件
  6. spring 处理带有特殊字符的请求_Spring爸爸又给Spring MVC生了个弟弟叫Spring WebFlux...
  7. 计算机作业老师会批改吗,小学和初中生家长每天都按照老师的要求批改作业吗?...
  8. 蓝桥杯2017年第八届C/C++省赛C组第一题-贪吃蛇长度
  9. 7-24 求集合数据的均方差 (15 分)
  10. Tomcat8.0之后GET请求不需要再设置中文乱码问题