Erlang ets -- something about cache continue

上一次说到了实现一个简单cache 的基本思路和想法, http://www.cnblogs.com/--00/p/erlang_ets_something_about_cache.html 在文末, 说到了判断single record 内存占用量. 这次继续说说Erlang 数据项内存的相关问题.

Erlang efficiency_guide 文档中, 较为清楚的表述了Erlang 系统中不同数据类型的内存消耗, 在这简单贴一两个:

Small integer 1 word
On 32-bit architectures: -134217729 < i < 134217728 (28 bits)
On 64-bit architectures: -576460752303423489 < i < 576460752303423488 (60 bits)
List 1 word + 1 word per element + the size of each element
Atom 1 word. Note: an atom refers into an atom table which also consumes memory. The atom text is stored once for each unique atom in this table. The atom table is not garbage-collected.
String (is the same as a list of integers) 1 word + 2 words per character

从文档中,可以看出Small integer 占用了1个字节, Atom 占用1个字节, List 占用的字节主要取决于element amount 和 size of each element .

举个栗子:

["123", "234"] 占用的内存量的计算 1 + (1 + (1 + 2 * 3)) + (1 + (1 + 2 * 3)) = 17 就是17 个字节.

Tips:

  注意Atom 在Erlang 系统中只占用1 个word, 这一点对于Message 有很大的帮助.

Erlang中atom数据类型能够做的唯一的运算就是比较;在erlang中模块名和方法名都是原子;Atom用来构造Tag-Message,Atom的比较时间是常量的,与Atom的长度无关(如果拿binary做tag,比较时间是线性的);Atom就是为比较而设计,除了比较运算不要把Atom用在别的运算中.

扩展阅读参见坚强的blog.

了解了Erlang 各种数据项在Erlang 系统中的内存分配规则,那么怎么才能快速的计算呢? 有没有现成的API函数, 总不能每次都手动计算一次吧?

那就首先来看看Erlang 系统所提供的各种size:

  • 其中对于所有数据项都通用的有: erlang:external_size/1erts_debug:size/1erts_debug:flat_size/1
  • 适用于二进制串有: erlang:size/1erlang:byte_size/1erlang:bit_size/1
  • 适用于元组的有: erlang:size/1erlang:tuple_size/1

其中,比较重要的erts_debug 两个函数:

erts_debug:size/1 和 erts_debug:flat_size/1 都是不在正式文档中的函数, 可以用来计算erlang数据项在内存中所需要空间. 各种数据项的空间占用可以在这里找到: http://www.erlang.org/doc/efficiency_guide/advanced.html#id68912. 这两个函数区别在于, 在具有共享内存的数据结构中, erts_debug:size/1只计算一次共享的数据大小, 而erts_debug:flat_size/1则会重复计算.

这是erlang源代码中的例子:

%% size(Term)
%%  Returns the size of Term in actual heap words. Shared subterms are
%%  counted once.  Example: If A = [a,b], B =[A,A] then size(B) returns 8,
%%  while flat_size(B) returns 12.

文档中有另外一个例子: http://www.erlang.org/doc/efficiency_guide/processes.html

总的来说, erts_debug:size/1是erlang数据项在内存中所占用的空间大小, erts_debug:flat_size/1是同一节点内, 跨进程移动数据项(包括ETS操作)所需要拷贝的数据大小.

OK, 先做个简单的test :

 1 $ cat test_for_ets_record_flat_size.erl
 2 -module(test_for_ets_record_flat_size).
 3
 4 -compile(export_all).
 5
 6 start() ->
 7     A = ets:new(a, [named_table, public]),
 8     D = {[{} || _ <- lists:seq(1, 100)], [self() || _ <- lists:seq(1, 10)], [{<<"1234567890">>, {}} || _ <- lists:seq(1, 1000)]},
 9     io:format(" ** data words size ~p~n", [erts_debug:flat_size(D)]),
10     io:format(" ** before insert ~p~n", [ets:info(A, memory)]),
11     ets:insert(A, D),
12     io:format(" ** after insert ~p~n", [ets:info(A, memory)]).

执行结果:

1 $ erl
2 Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
3
4 Eshell V6.3  (abort with ^G)
5 1> test_for_ets_record_flat_size:start().
6  ** data words size 10324
7  ** before insert 305
8  ** after insert 10633
9 ok

从结果来看, flat_size 的方法差了4个字节. (erts_debug:size/1 能相差 8095 个字节) 为什么?

erts_debug:size/1 只计算一次共享的数据大小, 而erts_debug:flat_size/1则会重复计算. 但为什么erts_debug:flat_size 还是会相差4个字节呢?

带着这个疑惑去Google erlang erts_debug flat_size 找到了 Erlang-MailList :

After looking at this more I have realized the documentation of the memory information is correct as would be expected.  Sorry for the noise about this.  Some comment that talks about erts_debug:flat_size/1 (and erts_debug:size/1) providing the process heap size only, with an additional 1 word excluded for the register or stack storage of the top-level term would help make things clearer.  This may be straight-forward for some since it makes logical sense, but I didn‘t know about these internal details and I wanted to be sure I was looking at the size correctly.

提到了erts_debug:flat_size ONLY 提供占用进程heap size .

回过头看 源代码:

Returns the size of Term in actual heap words.

事情进展到这, 有一点已经搞明白了: erts_debug:flat_size/1 只能计算Erlang Term 在进程heap 中占用的内存, 并不能计算所有的内存占用量.然后通过上面的Erlang-MailList 找到了github 上的这个开源项目: erlang_term

摘取其中关键性的一段代码:

 1 byte_size_term(Term, WordSize) ->
 2     DataSize = if
 3         is_binary(Term) ->
 4             BinarySize = erlang:byte_size(Term),
 5             if
 6                 BinarySize > 64 ->
 7                     BinarySize;
 8                 true ->
 9                     % in the heap size
10                     0
11             end;
12         true ->
13             0
14     end,
15     % stack/register size + heap size + data size
16     (1 + erts_debug:flat_size(Term)) * WordSize + DataSize.

从上面的代码可以看出, Erlang Term 的内存占用量应该是process heap 的内存占用量(通过erts_debug:flat_size/1 计算), stack 占用量以及共享内存占用量的总和.

好, 继续上test code :

 1 $ cat test_for_ets_record.erl
 2 -module(test_for_ets_record).
 3
 4 -compile(export_all).
 5
 6 start() ->
 7     A = ets:new(a, [named_table, public]),
 8     D = {[{} || _ <- lists:seq(1, 100)], [self() || _ <- lists:seq(1, 10)], [{<<"1234567890">>, {}} || _ <- lists:seq(1, 1000)]},
 9     io:format(" ** data words size ~p~n", [erlang_term:byte_size(D)/8]),
10     io:format(" ** before insert ~p~n", [ets:info(A, memory)]),
11     ets:insert(A, D),
12     io:format(" ** after insert ~p~n", [ets:info(A, memory)]).

测试结果:

1 $ erl -pa ./ebin -pa ./
2 Erlang/OTP 17 [erts-6.3] [source] [64-bit] [smp:8:8] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]
3
4 Eshell V6.3  (abort with ^G)
5 1> test_for_ets_record:start().
6  ** data words size 10325.0
7  ** before insert 305
8  ** after insert 10633
9 ok

why ?? 为什么还差3个字节呢? 好吧, 只能开issues请教作者了.

So, the erlang_term module can help you manage caching, but the real situation in the Erlang VM with the many memory pools is more complex.

没辙了, 要搞清楚这3个字节在ets table 中用到了什么地方, 就需要详细了解ets 的内存管理方式.只能先暂时搁置了(待续).

总结:

1, erts_debug:flat_size/1 只计算了Erlang Term 在process heap 中的size ;

2, erlang_term is so amazing .

时间: 2024-10-08 02:35:07

Erlang ets -- something about cache continue的相关文章

Erlang ets -- something about cache

都说用ets 写一个cache 太简单, 那就简单的搞一个吧, 具体代码就不贴了, 就说说简要的需求和怎么做(说设计有点虚的慌). 需求场景 >> 查询系统,对于主存储而言,一次写入多次查询 所以,cache 需要能实现: UserA 在查询 RecordA 时, UserB 也需要查询RecordA, 就让UserB waiting, 待UserA 查询完成之后, 共享RecordA 的查询结果. >> 限制单个ets 表的内存使用量,先进先出 那就需要个queue,求 queu

Erlang ETS Table

不需要显示用锁,插入和查询时间不仅快而且控制为常量,这就是Erlang的ETS Table. 为什么而设计? Erlang中可以用List表达集合数据,但是如果数据量特别大的话在List中访问元素就会变慢了;这种主要是由于List的绝大部分操作都是基于遍历完成的. Erlang的设计目标是软实时(参考:http://en.wikipedia.org/wiki/Real-time_computing),在大量数据中检索的时间不仅要快而且要求是常量.为了解决快速查 询的问题,Erlang提供的机制就

Erlang --- ETS表

ETS和进程字典都是Erlang所独有的. ETS是Erlang Term Storage 的缩写,它是一个基于内存的KV( Key Value) Table,支持大数据量存储以及高效查询. 要使用ETS表,首先就要先新建ETS表. 1.ets:new(?ETS_SYS_BUILDING,[{keypos,#ets_sys_building.sysBuildingId},named_table,public,set]) 其中 ?ETS_SYS_BUILDING:是表名 {keypos,#ets_

erlang ets学习笔记

ets全称“erlang term storage” erlang项式存储. ets打破了erlang“不变数据”的原则,使得进程之间可以共享数据.首先引起的思考是为什么会出现ets?下面是对网络资料的整理和分析: <why:为什么要引入ets?> <坚强2002> Erlang中可以用List表达集合数据,但是如果数据量特别大的话在List中访问元素就会变慢了;这种主要是由于List的绝大部分操作都是基于遍历完成的. Erlang的设计目标是软实时(参考:http://en.wi

记一次erlang 节点CPU严重波动排查过程

新服务上线后观察到,CPU在10 ~ 70%间波动严重,但从每秒业务计数器看业务处理速度很平均. 接下来是排查步骤: 1. dstat -tam 大概每10s一个周期,网络流量开始变得很小,随后突然增大,CPU也激增. 网络流量变化和从性能计数器结果上并不符合,服务相关业务较为复杂,先找出那个业务占用网络流量. 2. iftop 找出流量最大的几个目标IP,并且周期的流量变为0随后激增. 通过IP 知道是外部http接口地址,因为接口调用是异步进行的,性能计算是执行开始记录的,而不是结束记录,因

Erlang/OTP 中文手册

http://erldoc.com/ Open Telecom Platform application array asn1rt base64 binary calendar code dbg dict erlang ets file filelib gb_trees gen_tcp inet io lists make maps math mnesia net_adm os proplists random re rpc string sys unicode Erlang并发编程 Erlan

[Erlang 0126] 我们读过的Erlang论文

我在Erlang Resources 豆瓣小站上发起了一个征集活动 [链接] ,"[征集] 我们读过的Erlang论文",希望大家来参加.发起这样一个活动的目的是因为Erlang相关的出版物很少,很多时候都是从学术论文中寻找答案,而发现合适的论文是第一步,这个活动就是为了解决这个问题. 在一个极小的知识点可能都会有一篇精彩的论文为你条分缕析,抽丝剥茧,甚至可以拼凑起来一个完整的Erlang知识系统,我们开始吧... <面向软件错误构建可靠的分布式系统> Making rel

memcache-client-forjava 源码分析之MemcachedCacheManager

接上文<memcache-client-forjava 源码分析之DefaultCacheImpl分析>,主要分析ICache另外一个针对Memcached缓存实现,重点实现了memcached的高可用能力. 由于底层访问复用了java_memcached-release包的实现,memcache-client-forjava只是在上层做了简单封装.本文重点分析下如何进行的封装,以提高自己的设计经验.个人认为,java_memcached-release源码阅读,比spymemcached更简

Erlang--etc结构解析

Erlang中可以用List表达集合数据,但是如果数据量特别大的话在List中访问元素就会变慢了;这种主要是由于List的绝大部分操作都是基于遍历完成的. Erlang的设计目标是软实时(参考:http://en.wikipedia.org/wiki/Real-time_computing),在大量数据中检索的时间不仅要快而且要求是常量.为了解决快速查 询的问题,Erlang提供的机制就是ETS(Erlang Term Storage)和DETS(Disk Erlang Term Storage