Archive

Archive for the ‘study’ Category

CouchDB —— 可以期待的美好未来

March 17th, 2010 :: jackyz

有关 CouchDB 的最新动向是: Damien Katz 已经离开了 IBM ,他和他的团队,在 200w $ 投资的帮助之下,开始创业。美国投资人的魄力与专业素养,让我这个中国软件人羡慕不已,什么时候我们中国也会出现这种“纯粹谈技术就能够决定是否投你”的投资人呢?新公司名叫 Couch IO (请注意,名字当中,没有出现 DB 这两个字母)。是的,开源软件界备受瞩目的新星 —— CouchDB 团队已经踏上了全新的旅程,他们正朝着下一个目标进发。我很愿意见到他们取得成功!

CouchDB 狐狸书

CouchDB 狐狸书 中,作者 Jan Lehnardt 描述了一种全新的 Web 开发模式,有这样一句话来形容这种新的开发方式。

CouchDB has changed the way I think about developing web applications

在有着 10+ 年 Web 开发经验的我看来,尤其是在确实理解了他们心目中的开发方式之后,我觉得,这并不是一句阿谀奉承的面子话。而是,实事求是地说 —— 完全没有夸张的成分。

我在这里极其“概略”的介绍一下这种匪夷所思开发方式的若干基本要素:
Read more…

study , ,

素数求解,兼谈Erlang的性能特性

November 24th, 2009 :: jackyz

javaeye 的 dachidahu 同学不久前提了一个关于 Erlang 的问题 —— 《Erlang 求解1到N 素数的效率问题》。我试了一下,这个问题并不复杂,但结果相当有趣。对于初学 Erlang 的朋友而言,这个程序作为一个了解 Erlang 语言性能特性的例子,非常具有典型性。因此,特地整理一番,与众初学者共享。

Read more…

study

[荐]Erlang和Python的互通

September 2nd, 2009 :: jackyz

陈皓同学的博客建在 ZDNet 上,他最近正在研究 Erlang ,在官方文档上花了 24 个小时的研读之后,终于调通了 Erlang 通过 Port 机制调用 Python 的代码。或许,他的文章和代码能帮助更多的朋友节约更多的 24 个小时,尤其是对于那些想要写 Port 或正在用 Python 的朋友。故,在此推荐之。地址在[这里]。

其实 Port 对 Erlang 而言是一个很重要的扩展设施。在 Disco,在 CouchDB,在很多重量级的 Erlang 项目中,我们都能看到 Port 的使用。在大量的 Erlang 项目实战中,最初的快速原型开发确定之后,再将发现的瓶颈部分改为更加高效的本地 Port 来实现(如果能够确认这样的实现真的更加高效的话),这似乎也是比较通行的做法。放在这样的应用场景下考虑,此时的 Port 其实充当着“ Worker 调用者”的角色,而 Erlang 自身则变成了动态机器集群的“粘合系统”。与“将一个机房/一堆机器当作一台机器来使用”的架构目标达成了极佳的重合。

对于大多数的 Erlanger 而言,在实际的工程实践中,对“效率”和“适用性”上的考量应该被放在第一位,对“是否纯粹”的执着则应该被看得更淡一些。

study

回“老赵”关于“Erlang中最大的问题”

July 10th, 2009 :: jackyz

活跃在博客园的“老赵”,是一位研究 .NET 非常深入的同学(因为我本人也是老赵——jackyz.zhao,所以,特地加了引号)。他最近很关注“在 .NET 下实现 Erlang 语言特性”的课题,并为此写了一系列的技术文章,相当不错,我一直都在关注。他自己写了一个名为 ActorLite 的小东西,此前做过介绍,是个不错的尝试。

最近“老赵”同学写了一篇《一种适合C# Actor的消息执行方式(上)》,其中提到“(在消息执行上) Erlang 中最大的问题”。这是一个很有意思的观点,而且因为富于代表性因而显得很有价值,很有必要拿出来和大家探讨。
Read more…

study , ,

[转]Erlang的类型系统和静态分析

June 9th, 2009 :: jackyz

转载说明

“Erlang 是动态类型的语言,因而不能进行静态分析,所生成的文档也不包含有助于理解的类型信息”——这是惯常的看法,广为流行,而且被看作是 Erlang 在开发大型系统时的一个短板(大型系统意味着更强烈的静态分析需求和更严重的依赖文档进行沟通)。

然而 Erlang 是一个有着 20 多年历史的成熟系统,它早已发展出了一套自己的类型标注系统,不仅用来生成文档,更重要的是可以据此对源码进行静态分析,通过程序来排除一些低级的和隐藏的错误。在这方面, Erlang OTP 的源码本身及其文档就是最好的例子。在 《Erlang 程序设计》 的附录A部分,对于这个系统的使用已经进行了充分的说明。

需要强调的一点是在 Erlang 语言的背后还有一个活跃的社区(后者更为重要),其 EPP 过程一直都在持续不断地推进语言本身的进化。这方面最新的成果便是:在 R13 中,将此前文档级的 @spec,@type 标注升级为语言级的 -spec,-type 标注。可以预期的一点是,在未来的版本中,这些方面仍将持续推进。

litaocheng 同学的这篇“Erlang类型及函数声明规格”,风格严谨,论述详尽,涵盖了最新的语言特性,是任何一个程序员想要开发“严肃 Erlang 程序”的必读文档。

Read more…

study , , ,

[转] couchdb 上手指南

May 6th, 2009 :: jackyz

张沈鹏同学写了一篇不错的 couchdb 上手指南,以 wiki 格式发在 erlang-china 的 maillist 里。这里整理了一下格式,原文转贴出来,并郑重推荐之。

启动

balin couchdb #  ./utils/run

参数有

-h display a short help message and exit
-V display version information and exit
-a FILE add configuration FILE to chain
-A DIR add configuration DIR to chain
-n reset configuration file chain (including system default)
-c print configuration file chain and exit
-i use the interactive Erlang shell
-b spawn as a background process(作为后台进程)
-p FILE set the background PID FILE (overrides system default)
-r SECONDS respawn background process after SECONDS (defaults to no respawn)
-o FILE redirect background stdout to FILE (defaults to $STDOUT_FILE)
-e FILE redirect background stderr to FILE (defaults to $STDERR_FILE)
-s display the status of the background process
-k kill the background process, will respawn if needed
-d shutdown the background process(关闭)

配置

balin couchdb # vi etc/couchdb/local_dev.ini

可以指定端口号等,常用的有:

[httpd]
port = 12345
bind_address = 0.0.0.0

[admins]
用户名 = 密码

使用

http://123.123.123.123:12345/_utils/

可以创建数据库

python 中的使用

http://123.123.123.123:12345/_utils/database.html?python-tests

以操纵这个数据库作为演示,python库有几个函数比如update([...])不能用,不能用用户名密码等等,也许要修一下…

from couchdb import client
from couchdb.client import Document
server = client.Server('http://123.123.123.123:12345/')
 
#打开数据库
db = server['python-tests']
 
#创建一条数据
doc_id = db.create({'type': 'Person', 'name': 'John Doe'})
 
#获取一条数据,这个doc接口和字典一样
doc = db[doc_id]
 
#_rev是版本,_id是uuid
doc.items()
[(u'_rev', u'1-2963977070'),
(u'_id', u'4a36f238f4facbe08762b1a958cef39e'),
(u'type', u'Person'),
(u'name', u'John Doe')]
 
#可以自己指定主键
db['JohnDoe'] = {'type': 'person', 'name': 'John Doe'}
 
db['JohnDoe'].items()
[(u'_rev', u'1-2744716443'),
(u'_id', u'JohnDoe'),
(u'type', u'person'),
(u'name', u'John Doe')]
 
#更新
badman = db['JohnDoe']
badman[age]=1234
db['JohnDoe'] = badman
 
#删除,可以用db.delete(doc)来删除
del db['JohnDoe']
 
#遍历
for row in db.view('_all_docs'):
  
print row.id
 
#看数据库信息
db.info()
{
u'compact_running': False,
u'db_name': u'python-tests',
u'disk_size': 24381,
u'doc_count': 13,
u'doc_del_count': 0,
u'instance_start_time': u'1241518867280531',
u'purge_seq': 0,
u'update_seq': 21}
 
#文档可以有2进制的附件 put_attachment 用这个函数上传
# 查询,map_fun是一个js函数,emit是emit(key,value)。key,value均可是null
# web页面上有Select view查询,可以直接搜索测试
# 好像要用unicode字符 不然找不到 囧啊
 
db['/logo/xxx1.jpg']={"type":"logo","size":1}
db['/logo/xxx2.jpg']={"type":"logo","size":2}
db['/logo/xxx3.jpg']={"type":"logo","size":3}
db['/logo/xxx4.jpg']={"type":"logo","size":4}

Map函数

map_fun = u'''
function(doc) {
   if (doc.type=='logo')
       emit(doc._id, doc.size);
}
'''
 
for row in db.query(map_fun):
  
print row

输出

<Row id=u'logo/xxx1.jpg', key=u'logo/xxx1.jpg', value=1>
<Row id=u'logo/xxx2.jpg', key=u'logo/xxx2.jpg', value=2>
<Row id=u'logo/xxx3.jpg', key=u'logo/xxx3.jpg', value=3>
<Row id=u'logo/xxx4.jpg', key=u'logo/xxx4.jpg', value=4>

我们还可以加上reduce函数,比如:

  1. reduce_fun = u'''
  2. function(keys, values, rereduce) {
  3.    return sum(values)
  4. }
  5. '''
  6. for row in db.query(map_fun,reduce_fun):
  7.    print row

输出

<Row key=None, value=10>

reduce 中 rereduce变量的含义如下

  1. rereduce为false
    • key为array,element为:[key,id],key为map function产生的key,id为Document对应id
    • values为array,elements为map function产生的结果
    • 比如 reduce([ [key1,id1], [key2,id2], [key3,id3] ],[value1,value2,value3], false)
  2. rereduce为true
    • key为null
    • values为array,element为前一次reduce返回的结果
    • 比如reduce(null, [中间结果1,中间结果2,中间结果3], true)

[这里]有一些map/reduce演示的例子,比较好懂.

Creating Views
View 可以理解为索引了 不过这个索引不是实时的…
接着上文的例子

db["_design/test"]={
 
"views":
 
{
  
"all": {
    
"map": "function(doc) { if (doc.type == 'logo')  emit(null, doc) }"
  
},
  
"size_large_than_2": {
    
"map": "function(doc) { if (doc.size && parseInt(doc.size)>2)
emit(null,doc) }
"
  
},
  
"total_size": {
    
"map": "function(doc) { emit(null,parseInt(doc.size)) }",
    
"reduce": "function(keys,values) { return sum(values) }"
  
}
 
}
}

然后刷新

http://123.123.123.123:12345/_utils/database.html?python-tests

可以看到 select views中多了test,也可访问:

http://123.123.123.123:12345/python-tests/_design/test/_view/all

可以加上limit这一类参数

http://123.123.123.123:12345/python-tests/_design/test/_view/all?limit=2
http://123.123.123.123:12345/python-tests/_design/test/_view/all?limit=2&skip=1

这样可以分页,不过[这里]提到

A simpler method of doing this is to use the skip parameter to work out the starting document for the page, however this method should be used with caution. The skip \parameter simply causes the internal engine to not return entries that it is iterating over. While this gives the desired behaviour it is much slower than finding the first document for the page by key. The more documents that are skipped, the slower the request will be.

所以最好配合下面的startkey之类的来用skip
类似参数还有:

  • 排序 descending=false
  • 开始结束 startkey=”abc”&endkey=”abcZZZZZZZZZ” 可以用 docid startkey_docid=null
  • 合并结果 group=true 用法有点复杂,可以看[这里]

key可以是复杂的key,比如:

The query startkey=["foo"]&endkey=["foo",{}] will match most array keys with “foo” in the first element, such as ["foo","bar"] and ["foo",["bar","baz"]]. However it will not match ["foo",{"an":"object"}]

python中可以这样访问

for row in db.view('_design/test/_view/all'):
  
print row.id

输出

logo/xxx1.jpg
logo/xxx2.jpg
logo/xxx3.jpg
logo/xxx4.jpg

又如

for row in db.view('_design/test/_view/size_large_than_2'):
  
print row

输出

<Row id=u'logo/xxx3.jpg', key=None, value={u'_rev': u'1-3347158087',
u'_id': u'logo/xxx3.jpg', u'type': u'logo', u'size': 3}>
<Row id=u'logo/xxx4.jpg', key=None, value={u'_rev': u'1-1107796651',
u'_id': u'logo/xxx4.jpg', u'type': u'logo', u'size': 4}>

网络资源

  1. 根据网上的测试表明:couchdb 写入速度 比 mysql 慢4倍,创建索引速度 比 mysql 慢50倍
  2. couchdb 只写入不删除,需要定期做整理,类似垃圾回收的copy+删除,需要预留大量磁盘空间.
  3. 索引不是实时的,你可能看到的是旧的数据

我的个人看法
单单看性能,couchdb的确很不理想,但是couchdb可以把数据以view的方式展现,要什么,就新建什么样的view,这种随心所欲索引方式,在不少应用的场合,通过view的方式把这种查询结果持久化,可以大大减少了把传统意义上的重复且相似查询.

study

Tail Recursion in C [一]

March 27th, 2009 :: ph4nut

看了《写出正确的尾递归代码》一文,对尾递归的作用有了表面上的理解,但是关于编译器如何优化尾递归为一条跳转指令以及对递归堆栈的处理还不是很清楚。所以有了这篇小文,有什么错误的地方还请各位看官指出。

先来看一下在C语言里一个尾递归的例子:

  1. #include &lt;stdio.h&gt;
  2.  
  3. void add( int  n)
  4. {
  5.     printf( " %d\n",n);
  6.     add(n + 1);
  7. }
  8.  
  9. int main()
  10. {
  11.     add(0);
  12.     return 0;
  13. }

编译这段代码:gcc -o  add.exe add.c

如果运行这个程序的话,当n增加到一定值的话,程序就会推出,因为栈空间不够。

再来用gcc的O2选项(这个选项增加了对尾递归的优化)来优化这段代码并运行:gcc -o add.exe add.c  -O2

如果你运行这个程序,你会发现一直打印递增的n,而没有停止。

接下来我们来比较一下没有优化尾递归和优化后的汇编代码。

未优化尾递归的汇编代码:gcc -S add.c

  1.  .file "add.c"
  2.  .section .rdata,"dr"
  3. LC0:
  4.  .ascii " %d\12"
  5.  .text
  6. .globl _add
  7.  .def _add; .scl 2; .type 32; .endef
  8. _add:
  9.  pushl %ebp
  10.  movl %esp, %ebp
  11.  subl $8, %esp
  12.  movl 8(%ebp), %eax
  13.  movl %eax, 4(%esp)
  14.  movl $LC0, (%esp)
  15.  call _printf
  16.  movl 8(%ebp), %eax
  17.  incl %eax
  18.  movl %eax, (%esp)
  19.  call _add  ;注意这里,add函数的调用
  20.  leave
  21.  ret
  22.  .def ___main; .scl 2; .type 32; .endef
  23. .globl _main
  24.  .def _main; .scl 2; .type 32; .endef
  25. _main:
  26.  pushl %ebp
  27.  movl %esp, %ebp
  28.  subl $8, %esp
  29.  andl $-16, %esp
  30.  movl $0, %eax
  31.  addl $15, %eax
  32.  addl $15, %eax
  33.  shrl $4, %eax
  34.  sall $4, %eax
  35.  movl %eax, -4(%ebp)
  36.  movl -4(%ebp), %eax
  37.  call __alloca
  38.  call ___main
  39.  movl $0, (%esp)
  40.  call _add
  41.  movl $0, %eax
  42.  leave
  43.  ret
  44.  .def _printf; .scl 3; .type 32; .endef

优化了尾递归的汇编代码:gcc -O2 add.c

  1.  .file "add.c"
  2.  .section .rdata,"dr"
  3. LC0:
  4.  .ascii " %d\12 "
  5.  .text
  6.  .p2align 4,,15
  7. .globl _add
  8.  .def _add; .scl 2; .type 32; .endef
  9. _add:
  10.  pushl %ebp
  11.  movl %esp, %ebp
  12.  pushl %ebx
  13.  subl $20, %esp
  14.  movl 8(%ebp), %ebx
  15.  .p2align 4,,15
  16. L2:
  17.  movl %ebx, 4(%esp)
  18.  incl %ebx
  19.  movl $LC0, (%esp)
  20.  call _printf
  21.  jmp L2  ;注意这里,尾递归被优化成了一条跳转指令。
  22.  .def ___main; .scl 2; .type 32; .endef
  23.  .p2align 4,,15
  24. .globl _main
  25.  .def _main; .scl 2; .type 32; .endef
  26. _main:
  27.  pushl %ebp
  28.  movl $16, %eax
  29.  movl %esp, %ebp
  30.  subl $8, %esp
  31.  andl $-16, %esp
  32.  call __alloca
  33.  call ___main
  34.  movl $0, (%esp)
  35.  call _add
  36.  
  37. leave
  38.  xorl %eax, %eax
  39.  ret
  40.  .def _printf; .scl 3; .type 32; .endef

从上面两段代码看来,add函数的确有差别,但是重点在于,优化后对堆栈的处理。还是在来看优化后的代码片段(跟上面的一样,只不过我摘取了重点)

  1. _add:
  2.  pushl %ebp  ;标记1
  3.  movl %esp, %ebp
  4.  pushl %ebx
  5.  subl $20, %esp
  6.  movl 8(%ebp), %ebx
  7.  .p2align 4,,15  ;标记2
  8.  
  9. L2:
  10.  movl %ebx, 4(%esp)
  11.  incl %ebx
  12.  movl $LC0, (%esp)
  13.  call _printf
  14.  jmp L2
  15.  .def ___main; .scl 2; .type 32; .endef
  16.  .p2align 4,,15

从标记1 – 标记2,这段代码在add函数递归的过程只执行了一遍,这就说明了,优化后的代码,在堆栈上分配参数的时候,只分配了一次,也就是在第一次调用add函数的时候,现在我们终于明白了尾递归为什么不消耗栈的原因了。同样的,如果在add函数里定义了一些变量(当然要在递归调用的前面),变量在堆栈上也只是分配了一次。

到此,尾递归这个问题也应该告一段落了,但是还有一个问题值得一提,上面的add函数是个无限的递归,永不返回,如果再加一个递归终结的条件,当add函数(也就是最深一层的add函数)返回的时候,它不是向上一层递归地返回,而是直接跳到第一个add调用者(在这个例子应该是main函数)的下一个语句。这个下一篇文章在详细解释吧。

misc, study

问个非常初级的问题

January 15th, 2009 :: huzht

我用了1天把erlang的EPL协议(也就是MPL协议改的),读了一下,感觉还有点没明白,我想问问如果我只使用默认配套的.beam文件和他的运行时环境,当然我肯定要自己做一些逻辑上的模块函数什么的,不包含他的.beam,只使用他的.beam,也不更改他的任何.erl文件,这样应该可以免费闭源使用吧?希望高手指点指点,谢谢!

study

erlang 的源代码保护机制

January 14th, 2009 :: jackyz

稍微深入研究过一点 java 的同学,恐怕都知道什么叫做 “反编译” 。也就是说,随便拿一个 class 文件,找一个 jad 来,所有的 “智慧结晶” 就全都 “真相大白” 了,跟原先的 source code 相比,区别只是没有注释而已。

对于开源软件开发者来说,这本是无所谓的事,但对于商业开发者而言,这简直就是噩梦。在 java 的世界,道高一尺魔高一丈(及其反复迭代)的结果是,这件事最终演变得比较诡异,以至于专门诞生了一个名叫 “代码混淆” 的产业。在我上一次关注的时候,这个领域的最新进展是可以 “混淆” 程序执行的流程,以至于正常的人类阅读反编译出来的源码,将会导致严重的脑残。不过,传说又出了个叫做 “流程优化器” 的东东……(这个故事未完待续)。

其实,这件事困扰的不仅只是 java ,几乎所有 “有源代码” 的程序都有这个烦恼。比如,饱受折磨的还有 php, asp 以及 .net。不知道有没有高人能从 “机器码” 反编译出 C 和 C++ 的源程序呢,反正我挺好奇的。不过,话说回来, “没有源代码” 的程序,恐怕还真的没有。保护源代码,在我们现如今 “处处是山寨,遍地是豺狼” 的产业现状之下,似乎仍然是个不得不认真对待的事情。

在源代码保护的问题上,Erlang 的表现又会如何?今天体验了一把,应该说,设计得很细致,至于说这样的设计是否能够完全杜绝源代码的泄露,这个问题恐怕仍然需要留给 “专家” 们去研究。好吧,口水就喷到这里,下面上干货。
Read more…

study , ,

Mnesia的分布式

December 18th, 2008 :: Godwit

Mnesia的分布式如何使用呢?做了个简单的测试:
代码如下:

  1. -module( db ).
  2. -compile( export_all ).
  3. -include_lib( "stdlib/include/qlc.hrl" ).
  4. -include_lib( "eunit/include/eunit.hrl" ).
  5.  
  6. %% player
  7. -record( player, {
  8. oid,        %% object id
  9. username
  10. } ).
  11.  
  12. table_data() ->
  13. [ %% The player table
  14. { player, 1, "david1" },
  15. { player, 2, "david2" },
  16. { player, 3, "david3" },
  17. { player, 4, "david4" },
  18. { player, 5, "david5" }
  19. ].
  20.  
  21. set_table_data() ->
  22. mnesia:clear_table( player ),
  23. F = fun() ->
  24. lists:foreach( fun mnesia:write/1, table_data() )
  25. end,
  26. mnesia:transaction( F ).
  27.  
  28. install( Nodes ) when is_list( Nodes ) ->
  29. mnesia:stop(),
  30. mnesia:delete_schema( Nodes ),
  31. catch ( mnesia:create_schema( Nodes ) ),
  32. mnesia:start(),
  33. case mnesia:create_table ( player, [
  34. { disc_copies, Nodes },
  35. { type, set },
  36. { attributes, record_info( fields, player ) }
  37. ] ) of
  38. { atomic, ok } ->
  39. ok;
  40. _Any ->
  41. io:format( "create table error!~n")
  42. end,
  43. ok.
  44.  
  45. do( Q ) ->
  46. F = fun() -> qlc:e( Q ) end,
  47. { atomic, Val } = mnesia:transaction( F ),
  48. Val.
  49.  
  50. demo( select_player ) ->
  51. do( qlc:q([ X || X
  52. mnesia:start(),
  53. set_table_data(),
  54. mnesia:stop().

俺用的是windows系统,在控制台下:

erl -sname a
Eshell V5.6.3  (abort with ^G)
(a@godwit)1> db:install( [node()] ).
ok
(a@godwit)2> db:init().
stopped

=INFO REPORT==== 18-Dec-2008::22:42:54 ===
application: mnesia
exited: stopped
type: temporary

(a@godwit)4> mnesia:start().
ok
(a@godwit)5> tv:start().   %% 这里你可以看到数据库内容。

然后在另一个控制台下:

erl -sname b -mnesia extra_db_nodes [a@godwit]
Eshell V5.6.3  (abort with ^G)
(b@godwit)1> mnesia:start().
ok
(b@godwit)2> tv:start(). %% 查看数据库内容,已经拷贝过来了!!!

Bingo!一般人我不告诉他!
PS:博主,我不知道我贴这么幼稚的文章行不行?:)

study