zoukankan      html  css  js  c++  java
  • Erlang中atom的实现

    Erlang的原子(atom)在匹配中有着重要作用,它兼顾了可读性和运行效率。 通过atom,可以实现很多灵活高效的应用。

    atom可以看作是给字符串生成了一个ID,内部使用的是ID值,必要时可以取出它的内容(字符串),例如用于打印输出。

    atom 的 Eterm 除去 6 位的标签之外剩下的部分,就是 atom 在 Erlang 虚拟机中的索引,也就是一个整数值。在 Erlang 中,有关 atom 比较的操作只需要比较两个索引值即可,就是整数操作,因此非常高效。atom 本身是一个字符串,那么 atom 的索引是怎样对应上具体的字符串的呢?也就是需要实现字符串和索引值之间的互相映射,字符串和索引值都必须唯一,这显然需要使用散列表。Erlang 虚拟机内实现了一套通用的索引和散列表机制,atom 表就是这个机制的一个客户。下图是这套机制中关键数据结构之间的关系。

    图中左侧是散列表部分,右侧是索引部分。先看左侧。这个散列表采用的是标准的数据结构教科书上的实现。查找的时候:通过散列函数计算被散列对象的散列值,然后对散列表的长度取模,得到图中左侧指针数组的索引,接下来运气好的话能直接得到查找的对象(封装在 HashBucket 中),运气不好的话可能查不到,或者发生碰撞进行线性搜索(例如图中通过散列值得到索引 2 的时候就发生了碰撞,需要线性搜索 HashBucket 中匹配的 hvalue)。插入的时候:同样是先计算散列值得到索引,然后看对应的指针是否已经有对象了,如果没有,则直接加入,如果有的话,则插入队列头部。散列表在扩容的时候,会选择下一个合适的大小(erts/emulator/beam/hash.c 文件中的 h_size_table 数组列出了散列表大小增长的序列,数组里面都是素数,但是基本上符合倍增的关系),把老表复制到新表,然后删除老表。当然,增长是有限制的,散列表大小不能超出 h_size_table 数组中指定的最大值。

    图中右侧是索引部分。索引表实际上是指向被索引对象的指针的数组,被索引对象的索引值就是对应指针在数组中的自然顺序。由于事先无法确定具体的索引数目,所以索引表的大小是动态增长的,增长单位为一个索引块的大小,每个索引块中有固定数目的指针(例如 1024 个)。散列表中每插入一个新的对象的时候,设置索引表中最小的那个可用索引。如果新的索引超出了索引块的边界,那么分配一个新的索引块,并且更新索引块表中的指针。同样,索引表的增长也是有限度的,索引块表的指针用完了就不能再增长了。索引块表的长度是在创建索引表的时候设置的,所以理论上可以很大,不超出内存限制即可,但是实际中还要考虑散列表的大小,这两者是相互制约的。

    描述了散列和索引的数据结构和实现之后,我们回到散列索引的客户——atom。由于散列和索引是通用的,所以散列表指向的对象是 HashBucket 数据结构,而索引表指向的是 IndexSlot 数据结构。为了将散列和索引结合起来,这两个数据结构是重叠的,HashBucket 在 IndexSlot 头部。具体客户在使用的时候,要把 IndexSlot 放在自己的头部,这样就把具体的对象和散列索引结合起来了(就好像原始的面向对象的实现)。 从图中可以看出,我们的 atom 数据结构除了上述结构之外,还包含了具体的字符串指针、长度以及编码信息。将这些信息串起来之后,我们就可以高效地在常量时间内查询 atom 是否已经存在,已经存在的 atom 的索引值是什么,某个索引值对应的 atom 是什么以及插入新的 atom。

    另一个问题,向散列表插入元素的时候,散列表要负责分配对象的内存,而散列表是通用的,那么散列表怎么知道分配例如 atom 呢?解决方法是散列表中元素的分配、比较、释放以及散列值计算的操作都通过回调函数的方式提供给散列表。这里 atom 使用的散列函数是经典的 hashpjw 散列算法,这个算法是字符串散列常用的算法。

    在一个 Erlang 节点内,atom 表是全局共享的,因此多个线程对 atom 表的访问是通过读写锁保护的。对 atom 表的操作绝大部分都是读操作,只有真正插入新的 atom 时的操作才是写操作,插入新 atom 的情况一般不频繁,而且也很少有多个线程争抢着插入新 atom 的情况,大部分情况都是试图插入 atom 但是发现其实已经存在了,因此 atom 表使用的读写锁是针对读操作优化的读写锁。使用针对读操作优化的读写锁时读锁的开销非常小,即使是在大量线程争用的情况下。

    Erlang 中的 atom 表是不进行垃圾回收的,毕竟在程序员不滥用 atom 的情况下,atom 数目可以控制在合理范围内。而且跟踪每一个 atom 的引用状况会产生很大的开销。所以不要滥用 atom,把 atom 表塞满是把 Erlang 虚拟机 crash 掉的一种方法。目前默认的 atom 数目限制是 1048576(1024×1024),通过虚拟机的 +t 参数可以设置。

    atom的实现文件位于erts/emulator/beam/atom.h atom.c  index.h index.c hash.h hash.c

    本文内容转自:http://www.cnblogs.com/zhengsyao/p/3424539.html

  • 相关阅读:
    不常用的cmd命令
    js获取宽度
    Marshaling Data with Platform Invoke 概览
    Calling a DLL Function 之三 How to: Implement Callback Functions
    Marshaling Data with Platform Invoke 之四 Marshaling Arrays of Types
    Marshaling Data with Platform Invoke 之一 Platform Invoke Data Types
    Marshaling Data with Platform Invoke 之三 Marshaling Classes, Structures, and Unions(用时查阅)
    Calling a DLL Function 之二 Callback Functions
    WCF 引论
    Marshaling Data with Platform Invoke 之二 Marshaling Strings (用时查阅)
  • 原文地址:https://www.cnblogs.com/cobbliu/p/3509500.html
Copyright © 2011-2022 走看看