2008年5月4日 星期日

深夜,聽琴聲

好久沒有深夜聽音樂的感動了,剛好交大公用琴房漸漸地衰老,原本打算搬新家後,再買電鋼琴來規律練琴,結果最近幾個月卻和音樂愈離愈遠,少了一些感動。

這幾天重聽 YsF 的片頭曲 - 「預感」,聽著聽著,重燃當時的哀傷不捨,YsF 的劇情其實沒特別引人入勝,和 Ys6 一樣,只算中規中矩的小品,可是 YsF 的音樂相當有感情,加深劇情的影響。像這樣的日常生活,配上一些偶然的小感動,也沒什麼好挑剔的。

追求自我實現,挑戰新難關的我,總會和渴望平淡生活的自己,互相衝突。雖然沒有任何根據,我相信漸漸地,兩著會愈來愈近,直到有那麼一天,合而為一之時,也是一切走向最圓滿的時刻吧。

喔,還有身體健康的自己,也期望他和以上兩者共向美好的未來。不知為何,這類幸福的小感動,總在夜深人靜時才會浮現。

最後,補上深夜聽琴聲後,第一次留下的感動文字:

即使是夏天的夜晚,如果不是很熱的話,我寧願關掉風扇睡覺。

讓透徹的琴聲能更純粹地展現,享受黑白相間,點點滴滴落下的雨滴聲。
2007/08/22  

在那之前還有許多感動的時刻,只有等到感動隨年紀增長減弱後,才驚覺應該要留下些什麼,提醒自己失去了什麼。也許當初怕寫下去之後,就無法繼續靜靜地欣賞吧。

2008年5月3日 星期六

騎車去看「鋼鐵人」

今天打完球後,忽然有人提議要去看電影,擇期不如撞日,當然是今天就去比較容易成行。至上次和老人茶會會友們去看電影,沒想到已隔了一年半。在那之後,只有和 Ambuscade 去看「變形金鋼」,至今也差不多一年沒進電影院了。

今天的成員有阿德、姿樺、yulong、黃哥和Jalamorm,少了每次都在的前.客服小王子有些可惜,不過人生就是這樣啦,大家都過得快樂就好。這部片的劇情不錯,兩小時完全沒有冷場。事實上,我是回來看電影板的討論才知道片長有到兩小時之久。除了有電影院必備的聲光效果,喜感也是十足,劇情的鋪陳也很順,沒有一絲冷場,初期主角遇難那段轉折,讓我有些感動。硬要挑毛病的話,大概是女主角的演技不夠好,劇情緊張時的聲音聽起來有些不合。

看電影前,我忽然想起過去和固定班底去看電影時,只有看「頂尖對決」那次是好片,其它像「世界大戰」、「惡靈古堡」第二集等,真是看得超悶的。所以在進電影前一刻我追問了一下,這次是誰提議要看「鋼鐵人」的,結果心虛的阿德說是黃哥講的,他只是附議而已,還補上這樣的發言「若不好看是黃哥的錯,不過我對他有信心」。沒想到還挺不錯的,嗯,看來以後挑片要給黃哥挑,打球放槍和挑好片是互無影響的。

結束時我本來想聽完片尾曲再走,照慣例大家沒什麼興趣,所以又提早離場了。沒想到回來看電影版才知道結束後還有一小段,類似為續集鋪路吧。看來電影之神有托念給我,只是我們無緣注意到它啊!

備註

以一個理工人的角度來說,看到片中主角動手打造機器的場景,勾起高中時期給自己許下的夢想。如今的我雖然實力提昇不少,但明白自己的能力後,反而以為離目標又更遠了些。仔細想想,其實是熱情不如高中時的自己而已,喚醒過去的熱血,持續前進吧!

2008年5月1日 星期四

初始化 graph 的教訓,不熟的語法別亂用

昨晚跑了個程式,今天醒來不久接到york的電話,說連swap 在內資源都被我吃光了。

這個程式有三個步驟:

  1. 從資料庫裡取出一些資料轉成 graph G(V, E)
  2. 將 G(V, E) 轉成 G’(V, E’),E’ = { (u, v) | (v, u) in E }
  3. 利用 G, G’ 算HITS

原以為是 graph 大小超出我的預料,做了一些縮減後就重跑,吃完午餐回來看,不對,怎麼又停在產生 G’ 的部份。接著在一些錯誤的地方最佳化,最後找到問題的源頭。

在初始graph時, 我以前是這麼寫的:

ur = (1..user_ids.size).map { {} }

產生一個長度為 user_ids.size 的陣列,並在每一個元素內填入一個 hash table (i.e. {}),也就是類似 adjacency list 的存法,擁有 adjacency matrix 和 adjacency list 的好處。這可是過去試了許久找到最滿意的存法,改天再補上遲遲沒寫的 graph 表示法的心得吧。

上面那段程式以前用得好好的,後來我想試試新語法,改成這麼寫:

ur = [{}] * user_ids.size

這個寫法是原自下面這個 idiom code:

array = [0] * n

一般初始陣列時可以這麼寫,會得到一個長度為 n ,初始值為 0 的陣列。

可是 [{}] * n 表示所有元素都指向同一個 hash table (這行程式只產生了一個 hash table),於是災難發生了,超大的 hash table 導至超糟的效率,更糟的是,我又用這個 graph 產生 G’,使得 G’ 變成幾乎 complete graph,然後 memory 就爆了 ( |V| 很大,但原本是 sparse graph)。

附帶一提, Ruby Cookbook 裡有提過這問題,當時有看懂,但沒完成參透啊!比方說當 hash 內元素不存在時,要自動產生一個陣列的話,標準錯誤寫法如下:

table = Hash.new([])

因為陣列只有被初始化一次,存在 table 內,當 table[key] 不存在時, 不管 key 為何,table 都會傳回那一個陣列。詳見以下的例子:

irb(main):088:0> table = Hash.new([]) irb(main):089:0> p table {} irb(main):090:0> table[0].push 5 irb(main):091:0> table[1].push 10 irb(main):092:0> p table {} irb(main):093:0> p table[2] [5, 10]

正確寫法如下:

table = Hash.new { |h,k| h[k] = [] }

這故事告訴我們,語法要學熟,不然等痛過後就會記熟了。

2008年4月13日 星期日

IR and DM algorithms codes

http://www.igvita.com/。好站一推,不止有code教學,連觀念的講解都超清楚的。比方說這兩篇:

看來以後用Ruby寫code會愈來愈方便啊!不過愈是了解Ruby運作的方式,愈是不敢對它的效率抱以期望。

2008年4月7日 星期一

module_eval: dynamic code generation

Ruby的動態性真是超乎想像的炫,先看一個簡單的例子,code改自 “10 Things Every Java Programmer Should Know About Ruby”裡的“Item #7 Ruby is Way More Dynamic Than You Expect”

irb(main):001:0> puts 3.even? NoMethodError: undefined method `even?' for 3:Fixnum from (irb):1 irb(main):002:0> 3.class.module_eval "def even?() (self & 1).zero? end" irb(main):003:0> puts 3.even? false

第二行即時塞入數字class一個method,更炫的是,你甚至不用知道數字的 class 叫做 Integer,Item #7有太多神祕的功能,目前還不能理解它們的價值有多高。

再來這個例子很實用,但比較複雜,改自《Programming Ruby 2/e》p402。有時我們想將某些方法改用lazy initialization,這些值只會算一次。一般的做法,就是另設個private field,先檢查該field是否已設值,是的話就直接傳回,否的話先算再傳回,像singleton就是一個例子。

若有10個method想改怎麼辦?若改完後有特殊需求想改回來怎麼辦?若用C/C++、Java,自然是得改source code再重編譯code。先不提程式必須停止的影響,至少要反覆改code就不太方便。Ruby的話,可以寫兩個method,一個叫 once(),一個叫 unonce(),接著要做的,就是執行 once/unonce 即可,程式碼見這裡,說明如下:

  1. once接受任意數量的參數,參數是instance method name,once會將instance method加上一個cache,使得該method只會計算一次。
  2. 所有class都是Module的subclass,如此一來,所有新舊class都多了一個method once()。
  3. 對Ruby來說,class內的code等同於「執行」,所以 line 36 的 once :r 並不是什麼神奇的語法,而是在 class T 的內部,執行 method once 和參數 :r。
  4. line 40 顯示出,method r() 已被改變了,只會算一次 4. line 43 和 line 36 執行相反的行為。
  5. line 6 ~ 10 是核心程式,line 9 的 “[0]” 是用來處理 method 傳回多個值的情況,傳回多個值時,若 ‘=’ 左方只有一個變數,傳回值會自動變成陣列。

以上的例子說明了什麼呢?

這個例子揭露了Ruby強大的動態能力,原本我們想將method改成有cache或沒cache,得修改原始碼才行,若有10個method就要改10次,而透過module_eval的技巧,只要寫一次 once(),用一行code執行一次 once(),全部搞定!程式相當地有彈性,其它類似的雜事像 setVariable/getVariable也可以透過類似的技巧解決。Ruby內建的 attr_reader/attr_writer/attr_accessor 應該是類似的產物。

其它附帶的好處是程式可以在執行中即時修改既有的任何程式碼,而不用停止程式,事先寫好一些操作的話,除ruby interpreter更新外,程式不需要停止。

2008年3月26日 星期三

Learn To Program

Learn To Program這個站真是超酷的,由一個 ruby program 寫成的 CGI,用來即時產生書的內容(見” About the Original Tutorial”),好處是確保範例code不會有錯,亂數或時間之類的還可以每次看到不同內容。更炫的是,code、code執行結果可以自動產生,有興趣可以對照看一下“Flow Control”裡Branching 開頭的code,包含source code、兩個執行結果和中間的remark,是由下面這段code產生的:

run1 = {:input => ['Chris']} run2 = {:input => ['Chewbacca'], :remark => 'But if we put in a different name...'} progN run1, run2 do <<-END_CODE puts 'Hello, what\\'s your name?' name = gets.chomp puts 'Hello, ' + name + '.' if name == 'Chris' puts 'What a lovely name!' end END_CODE end

至至於內文寫得好不好,我就沒注意啦。花點時間看這份code挺有收獲的。

2008年3月16日 星期日

Google File System

最近 Google 在推 MapReduce 這個平行計算 framework,讀完 paper 後,覺得滿有意思的,若有些 network programming 和 functional language 的基礎,讀起來應該滿輕鬆的。除了好奇之外,一方面也是想讀點技術性的論文,看看和平常讀的 data mining、information retrieval 論文有什麼差,又找了 GFS 的 paper 來讀,結果還滿有趣的。

先提個技術無關的事,這篇的 Acknowledgments 最後一句寫著:

Many of our colleagues at Google bravely trusted their data to a new file system and gave us useful feedback.

令人會心一笑。

這篇論文一開始先說明作者的需求。依他們的使用環境,提了些使用上的假設,像是硬碟常壞掉、設備不可靠,可是 file system 的可靠度是首要目標;random write 少、append 多;還有 small random read、large sequential read。接著依這些操作特性,自訂需要的 file system,對我這類看重實務面的人來說,這種開頭很合我的喜好,有些學術性論文討論的是尚未存在的需求,較難接受作者的假設。描述整篇論文太累了,這裡摘要一些我認為有趣的設計:

  • GFS 是架在 Linux file system 之上的 file system,這樣做的好處是善用Linux既有開發,簡省開發時間。雖然作者也提到因為Linux kernel bug,使得系統有些不穩,逼迫他們自行改 Linux kernel,整體來說,作者認為這個決定是正確的。我原本還以為GFS會自己重弄一個file system。
  • GFS的主要目的是穩定和處理大量資料,所以分散式系統是理所當然的設計。但有趣的是,GFS 採用 single master server,理由是設計簡單
  • GFS由 one master、many chunkservers、many GFS clients組成。
  • 為了達到single master,整體設計做了許多對應措施,如盡可能減少master的負擔,GFS client直接和chukserver拿資料;要寫入資料時,也是chunkserver之間互傳,master只記meta data。
  • 為了讓 meta data能全塞入memory,metadata格式很簡單,並有做 file path 的 prefix compression。實驗部份指出上百TB的data,meta data只占數十MB
  • 沒有「目錄」的存在,所有檔案的是用絕對路徑表示,這樣做的好處有:省下目錄的資料結構;可以同時多個client新增同一目錄下的檔案,沒有目錄,自然不需要對目錄要write lock(但「目錄」仍然有 read/write lock,用在別的場合)。
  • 整體採用duplicate data確保availability,chunkserver有存各個data block的checksum,送出資料時有先確認沒有損毀,確保reliability。
  • 不同檔案可以有不同的replication factor,預設值為3;常被使用的執行檔可以設為上百,避免同時多個application client用到而造成bottleneck。
  • 寫入檔案時,為確保各個replication一致,做起來不簡單;master會指派一個chunkserver負責資料更新。值得注意的是,由於寫入動作的複雜度,有時會先等一會,再批次處理對同一檔案的不同寫入。
  • 更新資料分兩部份:資料流會依chunkserver相對位置傳,並把資料切成數小塊,行成一直線的pipeline傳資料,減少傳輸的lantency
  • 接著由master指派的chunkserver指示所有有關的chunkserver用同一順序寫入資料,確保即使同時有多種寫入同一資料的操作,寫入完後同一份資料在不同chunkserver上仍然一致。
  • replicate data時,不止要考慮放在不同chunkserver,還要放在不同rack,使得swtich壞了或某機房停電時,仍能保證資料的存取。
  • master待load較輕時,會在background簡查metadata的正確性、data replication是否有達到正確的數量,並依確少的程度補救,比方只剩一份replication的優先性比剩兩份大很多。
  • master若掛了,外部的monitor會發現,重新開一個master (一分鐘內即能完成)
  • 為了確保master掛了也沒問題,所有master的操作都是先將log寫入disk後,才正式執行。加上log很精簡,master重新啟動載入log不需多少時間。
  • 事實上,不管是master還是chunkserver,都沒有「停止」的指令,直接kill即可。它們都是設計成可以隨時掛掉,隨時快速複活
  • 刪除檔案即在master內將檔名改為特殊名稱並隱藏起來,仍可讀取和復原;待一段時間(三天)後,master會在background執行的garbage collection中正式清掉標記為刪除的檔案,好處是減少master短時間的high load,還有避免使用者犯錯,壞處是disk space可能實際上夠卻無法拿來使用。
  • chunkserver若發現檔案無法在master內查到meta data,即表示檔案已被砍了,減少master和chunkserver互相sync meta data的問題。master則是在新啟動時,向所有chunkserver要資料,減少master記錄的負擔。
  • 雖然GFS沒實作POSIX,卻有另外提供特殊功能:append record和snapshot
  • append record常用在 many-to-one producer-consumer queues。append可以同時對同一檔案操作,write則需要指定offset,不能同時執行。
  • snapshot指複製某個namespace下的所有檔案 (e.g. /home/www ),snapshot採用類似 copy-on-write 的設計,沒被寫入的data chunk不會被複製
  • chunk有reference count,當chunkserver發現reference count > 1又被寫入時,就會先複製一個新chunk,再把reference拆開,並更新各自的reference count。透過reference count達成copy-on-write的設計很漂亮,為了避免snapshot途中檔案有變(e.g. 多了新檔案),執行snapshot時會要求該namespace下的write lock。
  • shadow master以慢一點點的時間達到和master一樣的狀態,當作 read-only master,減輕master負擔。

整體來說,這篇論文相當有意思,許多設計都是簡單易懂,概念看來也很實用。唯一奇怪的是,實驗部份的表現看來不太好,還有我看不懂部份實驗說明,概念和設計細節都懂,反而是實驗看不懂,這到是很少碰到的情況,怪哉。