燕子矶

作为几乎是很多人脑海里南京最北端的地方,燕子矶平时算是人迹罕至。我上一次去这个公园是99年小学毕业的夏天,一晃都已经13年(13年!)过去了。

当年通往燕子矶的小街已经被新修的永济大道取代,不过当时的和平街、临江街依然存在。燕子矶公园的门原本开在临江街上,现在这个门已经几乎倒掉,公园在小院的另一侧开了一个面向永济大道的新门。以前这门口是各式拖挂卡车来来往往,环境整治以后现在到公园门口这一段没有卡车,所有的卡车从前面一条小街“新燕街”开到江边的码头。所以万变不离其宗,江边的主题依然是各种大型运输工具。

燕子矶 32.149074, 118.812354

刚到公园门口,就听见有人感叹自己也十年没来这个公园。看来这个公园已经小到、偏到不隔个十多年都想不起他来。然而可贵就可贵在,这么长时间小公园几乎就没有变化。规模小,又有一点特殊的历史背景,再加上有政府的补贴,也不必因为自负盈亏去折腾去影响气氛。说难也难,说简单也简单,就这么无欲无求地保存下来。

DSC_0004

DSC_0010

DSC_0013

DSC_0041

DSC_0037

DSC_0028

DSC_0020

DSC_0044

Python’s Valentines Day Gift to Clojure

Inspired by meh’s Ruby-Clj module, I created the python equivalent “pyclj” last weekend. Pyclj is a clojure literal reader/writer for python. It enables data exchange between python and clojure, in a clojure-native way. It’s Valentines Day today, I’d like to release it as the gift of python to clojure :)

The API is very simple. It’s all like python’s data modules (json, pickle)

import clj

clj.loads("[1 2 3]")
clj.dumps({"a":1, "b":2})

Clojure types are mapping to python data structures :

Clojure Python
list list
vector list
set set
map dict
nil None
string string
int int
float float
boolean boolean
char string
keyword string

But how we win clojure’s heart from ruby?

We are faster.

Considering clojure literal below:

[1 2 3 true false nil {:a 21.3 :b 43.2} "Hello"]

Comparing ruby-clj(0.0.4.5, ruby 1.9.3p0) and pyclj(0.1.3 python 2.7.2):

require 'clj'

s = "[1 2 3 true false nil {:a 21.3 :b 43.2} \"Hello\"]"

t1 = Time.now()
for i in 0...10000
  Clojure.parse(s)
end
puts Time.now()-t1
import clj
import time

s = "[1 2 3 true false nil {:a 21.3 :b 43.2} \"Hello\"]"

t1 = time.time()
for i in range(10000):
  clj.loads(s)
print time.time()-t1

The result:
ruby: 13.451157809
python: 0.712423086166
Edit 20120216 13:30
ruby-clj 0.0.5.3 has resolved the performance issue :)
The new result ruby-clj/0.0.5.4 Vs pyclj0.1.4 (on my laptop):
ruby-clj: 2.044872364
pyclj: 1.19659209251

The project is hosted on github. Feel free to join the development and enhance it.

HeatCanvas performance enhanced

heatcanvas

时隔半年日日沉浸在clojure世界里的时候,多亏了github上Daniel Azuma的提示,现在HeatCanvas通过Image Data数组来绘制图像。过去由于不太熟悉Canvas API,我用的是fillRect来填充1像素大小的区域,模拟像素的渲染。但是这种方式导致浏览器渲染的效率非常低。

ImageDataArray允许用户开辟一个固定大小的buffer,并设置每一像素的像素值,然后一次性地渲染到canvas上。详情可以参考这里:Pixel manipulation with canvas

这次性能的提升基本没有影响API,唯一的区别是如果原先自定义了value-color的映射函数的话,现在不再接受hsl的css字符串了,新的API需要你返回一个四个元素的数组,分别代表h, s, l, a,值域[0-1]。

感谢关注HeatCanvas的朋友。

China Clojurians Meetup #2 is calling you

大家期待已久的第二次Clojure中国用户聚会来了,这次聚会将在三月初的北京举行。感兴趣的朋友请猛击这里报名。如果你有任何关于Clojure的心得,都欢迎加入分享,不要害羞~和上次一样本人会继续抛砖继续不害羞,介绍我的RPC框架Slacker。

For English readers:
If you are interested in Clojure and also living in Beijing, feel free to join our meetup. Please register here. We will see you there!

Slacker Cluster

Cluster support is one of the big thing in slacker 0.6.x. Cluster enables high-availability and load balancing on slacker client and server.

Slacker cluster has a centralized registry, a zookeeper node, stores information of all the namespaces and servers instances in the cluster. Once a client declared remote functions, by calling `defn-remote` or `use-remote`, it reads all available servers offering that namespace from the registry and create connection to each of them. We the user issues a request, the client randomly pick up a connection from them. So the load is eventually distributed to every instance of slacker servers. And thanks to zookeeper’s notification feature, the client watches certain znode. It will be notified when 1. a connected server goes offline 2. a new server serving required namespace added into the cluster. Thus you don’t have to change client code or restart client when server changes.

To start a slacker server and add it to a cluster, you have to provide cluster information using the new :cluster option:

(start-slacker-server (the-ns 'slacker.example.api)
                      2104
                      :cluster {:zk "127.0.0.1:2181"
                                :name "example-cluster"})
  • :zk is the address of zookeeper node
  • :name is a znode qualified string, to identify the cluster

On the client side, it’s important to use APIs from `slacker.client.cluster` instead of `slacker.client`:

(use 'slacker.client.cluster)
;; arguments: cluster-name, zookeeper address
(def sc (clustered-slackerc "example-cluster" "127.0.0.1:2181"))
(use-remote 'sc 'slacker.example.api)

;; call the function from a random server
(timestamp)

If all servers provide ‘slacker.example.api go offline, slacker client will raise a “not-found” exception.

Slacker cluster is also designed with simple and clean in mind. You don’t have to change you business code to make it remote or cluster. Everything is transparent and non-invasive. Enjoy it.