Erlang list的++操作和append函数的底层实现
来源:互联网 发布:mac os 10.13下载地址 编辑:程序博客网 时间:2024/05/31 15:19
当提到Erlang中list的++操作符时,我们常会想到它的性能问题。
有些人知道++操作比较耗时,就改用函数append来代替。
到底++操作和append函数之间有什么区别?
我们来查看一下它们在Erlang源码及C源码中的实现。
在$ERL_TOP/lib/stdlib/src/lists.erl可以找到如下代码:
在$ERL_TOP/erts/emulator/beam/erl_bif_lists.c中可以看到:
结合上面两个文件中的源码,我们可以得出这样的结论:
++操作,以及函数lists:append/1和lists:append/2,
在Erlang底层都是调用了C函数append(Process* p, Eterm A, Eterm B)来实现。
要想揭秘++操作是如何耗性能的,我们只有彻底弄清楚它们的底层实现。
一起来看看$ERL_TOP/erts/emulator/beam/erl_bif_lists.c文件中append函数的C实现:
以上分析中,我们知道了++操作的具体实现,但我还是有疑问。
这是因为Erlang的原则是变量一旦赋值就不可再变,如果直接对A进行了修改操作,将会有什么结果?
有些人知道++操作比较耗时,就改用函数append来代替。
到底++操作和append函数之间有什么区别?
我们来查看一下它们在Erlang源码及C源码中的实现。
在$ERL_TOP/lib/stdlib/src/lists.erl可以找到如下代码:
%% append(X, Y) appends lists X and Y-spec append(List1, List2) -> List3 when List1 :: [T], List2 :: [T], List3 :: [T], T :: term(). append(L1, L2) -> L1 ++ L2. %% 使用了++操作%% append(L) appends the list of lists L-spec append(ListOfLists) -> List1 when ListOfLists :: [List], List :: [T], List1 :: [T], T :: term().append([E]) -> E;append([H|T]) -> H ++ append(T); %% 使用了++操作append([]) -> [].
在$ERL_TOP/erts/emulator/beam/erl_bif_lists.c中可以看到:
/* * erlang:'++'/2 */Etermebif_plusplus_2(BIF_ALIST_2){ return append(BIF_P, BIF_ARG_1, BIF_ARG_2);}BIF_RETTYPE append_2(BIF_ALIST_2){ return append(BIF_P, BIF_ARG_1, BIF_ARG_2);}
结合上面两个文件中的源码,我们可以得出这样的结论:
++操作,以及函数lists:append/1和lists:append/2,
在Erlang底层都是调用了C函数append(Process* p, Eterm A, Eterm B)来实现。
为什么Erlang的++操作耗性能?
要想揭秘++操作是如何耗性能的,我们只有彻底弄清楚它们的底层实现。
一起来看看$ERL_TOP/erts/emulator/beam/erl_bif_lists.c文件中append函数的C实现:
#define CONS(hp, car, cdr) \ (CAR(hp)=(car), CDR(hp)=(cdr), make_list(hp))#define CAR(x) ((x)[0])#define CDR(x) ((x)[1])static BIF_RETTYPE append(Process* p, Eterm A, Eterm B){ Eterm list; Eterm copy; Eterm last; size_t need; Eterm* hp; int i; if ((i = list_length(A)) < 0) { BIF_ERROR(p, BADARG); } if (i == 0) { // A的长底为0,直接返回B BIF_RET(B); } else if (is_nil(B)) { // B是一个空列表,直接返回A BIF_RET(A); } need = 2*i; // 为A的拷贝分配内存空间 hp = HAlloc(p, need); list = A; // 首先拷贝A的第一个元素,并初始化copy和last变量 // 为什么要两个变量指向同一值? // 因为copy指向副本头部,以下不再对copy的变量值做任何修改,最后用来做函数的返回值 // last保持指向A副本的最后一个元素,最后要保存B的头指针值 copy = last = CONS(hp, CAR(list_val(list)), make_list(hp+2)); // 更新list变量值,让它指向A的第2个元素 list = CDR(list_val(list)); hp += 2; // 注意,上面已经拷贝了A的第一个元素,所以要先i-- i--; // 从A的第2个元素开始,逐个拷贝 while(i--) { // 取出list指向的元素,8字节, // CAR(listp)为指向当前元素值的指针值 // CDR(listp)为指向下一个元素的指针值 Eterm* listp = list_val(list); // 注意make_list(hp+2),直至拷贝完成,这个列表的next指针都是指向下一个内存空间, // last总是指向最新的元素,因为是从A的头部开始拷贝的, // 所以last最终也就是指向A副本的最后一个元素 last = CONS(hp, CAR(listp), make_list(hp+2)); // 让list移动到下一个元素 list = CDR(listp); hp += 2; } // 上面的拷贝之后,副本尾部没有NIL值,在这里正好把这个位置的值更新为B的值, // 至此,两个list的合并完成 CDR(list_val(last)) = B; // 返回副本头部的指针值 BIF_RET(copy);}
以上分析中,我们知道了++操作的具体实现,但我还是有疑问。
为什么Erlang中++操作时要拷贝左边的list?
为什么要对A进行拷贝?
如果通过遍历找到A尾部的NIL值,并把它更新为B的指针值不就OK了?这是因为Erlang的原则是变量一旦赋值就不可再变,如果直接对A进行了修改操作,将会有什么结果?
我认为这也是一个在写NIF时需要注意的问题。
Erlang ++操作符使用小结
- 尽量把长度较小的list放在左边。
- 如果左边的list长度很小,并不会造成很大的性能影响,这种情况下可以放心使用++操作符。
- lists:append/2函数最终也是调用++操作来完成,还不如直接使用++明了。
- 如果需要合并的list个数是未知的,可以使用函数lists:append/1来完成。
Erlang数据类型的内部实现
Erlang中list和tuple的构建及转换的内部实现
- Erlang list的++操作和append函数的底层实现
- Erlang list的++操作和append函数的底层实现
- python list的append和extend操作
- 底层实现的字符串操作函数
- list中 append和extend的区别
- python list 的 extend 和 append
- python list的append和extend区别
- List接口常用实现类的特点和底层实现
- python之list.append()和list.extend(list)的区别
- python之list.append()和list.extend(list)的区别
- List-ArrayList、LinkedList、Vector的底层实现和区别
- python list.append 和 list.extend() 的功能及异同
- [python] list.append()和list.extend()的区别
- erlang 实现list的二分查找
- Erlang的list-at-a-time操作 与 Perl的函数型编程
- Python函数 extend()和append()的区别
- python list 中append与extend函数的区别赏析
- erlang中list和ets的查找
- Design Pattern: Bridge 模式
- 网页设计中颜色代表的意义
- 高温下的人
- HDU——2044—— 一只小蜜蜂...
- Android的线程使用来更新UI----Thread、Handler、Looper、TimerTask等
- Erlang list的++操作和append函数的底层实现
- 042详解 51-100题
- Android之Broadcast, BroadcastReceiver(广播)
- DataGridView直接导出EXCEL
- 通过ClassLoader说明容器热部署实现机制
- python面向对象编程(1)——基本概念,术语,self,构造器
- C中的volatile用法
- Android ListView入门知识--各种Adapter配合使用
- 042详解 1-50题