论坛首页 综合技术论坛

探讨一下数据驻留模型

浏览 29668 次
该帖已经被评为良好帖
作者 正文
   发表时间:2008-02-01  
假如有一份数据, 有几个进程用到, 数据还是维护和查询这些操作. 使用纯 erlang 语言方式, 不允许使用 ets mnesia.

按 erlang 的方式, 建一个存储数据的进程, 其它进程向该进程发消息, 此进程按消息进行数据维护及查询, 之后发回对应的消息.

我看到 erlang 做这种事情都是这么来的:

func() ->
   receive
       {query} ->
           %% perform query
           func()    %% [b]下一轮收取[/b]
       %% other messages
   end.


这么一来每次收消息都是同步操作, 在 java 里相当于同步的过程, 这么做效率会很低吧.

   发表时间:2008-02-01  
这本身就是一个顺序瓶颈,无论哪种语言的实现都一样.
0 请登录后投票
   发表时间:2008-02-01  
我猜测 erlang 每个进程进入 receiver 之后就挂起了, 此进程收到消息后虚拟机把进程激活, 同时将消息压入 receiver 函数的栈里.

同样的事情在我们熟悉的命令式语言做, 通常让多个线程共享同一块内存. 大家一起读写. 也就是说共享同一个数据对象, 各自调用这个对象的读写方法.

这个做法要好好控制锁啊什么的, 较繁琐, 但不需要经过消息封包解包出入消息队列等过程, 效率会比 erlang 这种做法高很多, 同时也不会发生像上面的阻塞的情况.
0 请登录后投票
   发表时间:2008-02-01  
inshua 写道
我猜测 erlang 每个进程进入 receiver 之后就挂起了, 此进程收到消息后虚拟机把进程激活, 同时将消息压入 receiver 函数的栈里.

同样的事情在我们熟悉的命令式语言做, 通常让多个线程共享同一块内存. 大家一起读写. 也就是说共享同一个数据对象, 各自调用这个对象的读写方法.

这个做法不安全, 但不需要经过消息封包解包出入消息队列等过程, 效率会比 erlang 这种做法高很多, 同时也不会发生像上面的阻塞的情况.

第一,与Erlang比较需要考虑GC的影响,也就是说对比的时候要采用有GC的语言比如Java
第二,共享对象的访问需要锁,其访问顺序依然是阻塞的,而不是一起读一起写,除非你认为脏数据是可以忍受的.
第三,共享对象是要有锁消耗的,Erlang在单调度器上是没有锁消耗的,即便是多调度器上锁消耗依然比共享内存低.Erlang的消息消耗主要在于内存COPY上,一般使用混合堆的情况下,小数据的COPY依然比锁消耗小很多。100byte的memcpy一般在20ns左右而且这个数值是基本恒定,而进出mutex的消耗是根据锁碰撞的程度线性上升的最差的情况大约是300ns.
第四,即便是命令式语言中,并发实体间通过copy内存方式比直接共享内存块也是来的更为普遍。比如最典型的生产者消费者问题。




4 请登录后投票
   发表时间:2008-02-01  
Trustno1 写道

第一,与Erlang比较需要考虑GC的影响,也就是说对比的时候要采用有GC的语言比如Java

当然, erlang 这种方式 gc 很快, 但 gc 是另外一个话题了, 实际上命令式语言处理共享内存会使用适合的方式用不变的内存以避免垃圾回收.

Trustno1 写道

第二,共享对象的访问需要锁,其访问顺序依然是阻塞的,而不是一起读一起写,除非你认为脏数据是可以忍受的.

命令式语言固然要加锁, 但在纯粹读或者少量写的情况, 也可以换用变通的办法优先照顾读. 而 erlang 在语言级别上这么实现, 在运行方式上将内存做进程隔离, 想变通都没有办法.

Trustno1 写道

第三,共享对象是要有锁消耗的,Erlang在单调度器上是没有锁消耗的,即便是多调度器上锁消耗依然比共享内存低.Erlang的消息消耗主要在于内存COPY上,一般使用混合堆的情况下,小数据的COPY依然比锁消耗小很多。100byte的memcpy一般在20ns左右而且这个数值是基本恒定,而进出mutex的消耗是根据锁碰撞的程度线性上升的最差的情况大约是300ns.


假如共享的数据量有 1 兆(不大不小), 都堆在内存里, 每次都这样 copy 一次,很恐怖. 问一个问题, 如下的简单代码, 直接将参数传入下一次调用, 是否也要 copy 一次?
func(X) ->
   receive 
      none ->
        func(X)
   end.


Trustno1 写道

第四,即便是命令式语言中,并发实体间通过copy内存方式比直接共享内存块也是来的更为普遍。比如最典型的生产者消费者问题。

重要的是命令式语言还有别的方法, 而 erlang 只有这种方法.
0 请登录后投票
   发表时间:2008-02-01  
引用
当然, erlang 这种方式 gc 很快, 但 gc 是另外一个话题了, 实际上命令式语言处理共享内存会使用适合的方式用不变的内存以避免垃圾回收.

这不是重点,重点在于没有私有堆的GC,所有的回收都必须暂停并发线程。而命令式语言是无法做到私有堆的.


引用
命令式语言固然要加锁, 但在纯粹读或者少量写的情况, 也可以换用变通的办法优先照顾读. 而 erlang 在语言级别上这么实现, 在运行方式上将内存做进程隔离, 想变通都没有办法.

请使用ETS



假如共享的数据量有 1 兆(不大不小), 都堆在内存里, 每次都这样 copy 一次,很恐怖. 问一个问题, 如下的简单代码, 直接将参数传入下一次调用, 是否也要 copy 一次?

引用
Erlang的消息消耗主要在于内存COPY上,一般使用混合堆的情况下

如果不知道什么叫混合堆,可以去google 一下.

func(X) ->
   receive 
      none ->
        func(X)
   end.

X不Copy.

Trustno1 写道

第四,即便是命令式语言中,并发实体间通过copy内存方式比直接共享内存块也是来的更为普遍。比如最典型的生产者消费者问题。

重要的是命令式语言还有别的方法, 而 erlang 只有这种方法.
请参看第二条.
0 请登录后投票
   发表时间:2008-02-01  
惭愧, 没找到关于混合堆的有用的信息. 能不能简单介绍一下? 递归的 receiver 函数不会导致内存 copy,这是一条好消息。

考虑到 ets 不是通过纯粹 erlang 来实现的, 实用时可以用 ets, 也可以用 c 语言给 erlang 写一个 port driver, 以实现对大内存大磁盘的操作 ——这个好像就是 ets 做的. 但单就 erlang 本身来论,其驻留模式是有点问题的。
0 请登录后投票
   发表时间:2008-02-01  
inshua 写道
惭愧, 没找到关于混合堆的有用的信息. 能不能简单介绍一下? 递归的 receiver 函数不会导致内存 copy,这是一条好消息。

考虑到 ets 不是通过纯粹 erlang 来实现的, 实用时可以用 ets, 也可以用 c 语言给 erlang 写一个 port driver, 以实现对大内存大磁盘的操作 ——这个好像就是 ets 做的. 但单就 erlang 本身来论,其驻留模式是有点问题的。

你那个程序之所以没有copy,是因为X本来就是没有被跟新.如果你这么写
fun(X)->
   receive
      fun(X+1)
end.
那就是需要Copy的.

ETS本来就是Erlang的标准库,有什么不能用的?memcpy难道是C实现的?
大内存可以用Binary,Erlang自己有escape analysze会在编译期和运行时分析多少大的内存可以存入共享堆。共享堆可以在多个进程间共享,使用reference counting进行gc.

ps.在我看来,并发线程之间使用巨型内存本身就是存在问题的设计结构.

0 请登录后投票
   发表时间:2008-02-01  
Trustno1 写道
inshua 写道
惭愧, 没找到关于混合堆的有用的信息. 能不能简单介绍一下? 递归的 receiver 函数不会导致内存 copy,这是一条好消息。

考虑到 ets 不是通过纯粹 erlang 来实现的, 实用时可以用 ets, 也可以用 c 语言给 erlang 写一个 port driver, 以实现对大内存大磁盘的操作 ——这个好像就是 ets 做的. 但单就 erlang 本身来论,其驻留模式是有点问题的。

你那个程序之所以没有copy,是因为X本来就是没有被跟新.如果你这么写
fun(X)->
   receive
      fun(X+1)
end.
那就是需要Copy的.

ETS本来就是Erlang的标准库,有什么不能用的?memcpy难道是C实现的?
大内存可以用Binary,Erlang自己有escape analysze会在编译期和运行时分析多少大的内存可以存入共享堆。共享堆可以在多个进程间共享,使用reference counting进行gc.

ps.在我看来,并发线程之间使用巨型内存本身就是存在问题的设计结构.


谈 fun(X) -> receive fun(X) end 的问题是因为它是用于读取的套路.

我之所以开始说不能用 ets, 是因为 ets 解决问题的方式并不是 erlang 的方式. 比如说 memcpy, 通过 c 语言也一个字节一个字节的 copy 也可以模拟一个, 虽然效率低, 但二者是同构的. 而如果 erlang 使用 fun(X) -> receive fun(Y) end 来模拟实现 ets, 那么这么大的内存 copy 过来copy 过去, 而且还会像滚雪球一样继续增大, 无疑是一个不具备可行性的方案.

不知道其它函数式语言是怎么解决共享大数据的问题的.

最后, 能不能介绍一下混合堆是怎么回事? 和 binary heap 有关的东东?
0 请登录后投票
   发表时间:2008-02-01  
inshua 写道
Trustno1 写道
inshua 写道
惭愧, 没找到关于混合堆的有用的信息. 能不能简单介绍一下? 递归的 receiver 函数不会导致内存 copy,这是一条好消息。

考虑到 ets 不是通过纯粹 erlang 来实现的, 实用时可以用 ets, 也可以用 c 语言给 erlang 写一个 port driver, 以实现对大内存大磁盘的操作 ——这个好像就是 ets 做的. 但单就 erlang 本身来论,其驻留模式是有点问题的。

你那个程序之所以没有copy,是因为X本来就是没有被跟新.如果你这么写
fun(X)->
   receive
      fun(X+1)
end.
那就是需要Copy的.

ETS本来就是Erlang的标准库,有什么不能用的?memcpy难道是C实现的?
大内存可以用Binary,Erlang自己有escape analysze会在编译期和运行时分析多少大的内存可以存入共享堆。共享堆可以在多个进程间共享,使用reference counting进行gc.

ps.在我看来,并发线程之间使用巨型内存本身就是存在问题的设计结构.


谈 fun(X) -> receive fun(X) end 的问题是因为它是用于读取的套路.

我之所以开始说不能用 ets, 是因为 ets 解决问题的方式并不是 erlang 的方式. 比如说 memcpy, 通过 c 语言也一个字节一个字节的 copy 也可以模拟一个, 虽然效率低, 但二者是同构的. 而如果 erlang 使用 fun(X) -> receive fun(Y) end 来模拟实现 ets, 那么这么大的内存 copy 过来copy 过去, 而且还会像滚雪球一样继续增大, 无疑是一个不具备可行性的方案.

不知道其它函数式语言是怎么解决共享大数据的问题的.

最后, 能不能介绍一下混合堆是怎么回事? 和 binary heap 有关的东东?

第一,内存的copy不可能累计增长,因为GC会自动回收不用的内存。
第二,ETS怎么就不同构了?你用C语言写一个CRM,就不要用dbms了?查询不用sql,全部写成c api?
第三,本来一个语言为了解决不同层次的问题就提供了不同的的方法,你非要一根筋的用一种方法那神仙也没有办法.



0 请登录后投票
论坛首页 综合技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics