發表文章

目前顯示的是 六月, 2010的文章

Python 2.6 的好東西

我目前在用 Ubuntu 8.04, 但 Ubuntu 8.04 只提供 python 2.5 的 deb檔。想換 Ubuntu 10.04, 但 10.04 卻只有 python 2.6 的 deb 檔。只好先暫時守著 Ubuntu 8.04 + python 2.5。平時看文件偶而會看到一些 2.6 才有的好東西, 不能用真是太難過了。這篇暫記一些看到的好東西, 避免一段時間過去沒用過就忘光了。
with: 2.5 可用 from __future__ import with_statements 取得。multiprocessing: 2.5 可從 python-multiprocessing 裝。namedtuple: 產生 value object 的 factory function。之前傻傻地寫過類似的 code generator, 功能弱多了。productpermutationscombinations: 產生排列組合的函數。用 product 可以將多組參數的組合攤平成一個迴圈, 語意上更容易理解。參考官網的示意說明, 我自己寫了個 product 來用。sys.getsizeof: 取得物件在記憶體中占的 byte 數。最佳化和協助理解 python 內部結構的好東西啊, 我一直很困惑為什麼沒用多少東西就吃掉一堆記憶體。之後看到再陸續補上吧, 有些已忘掉了...。

查看 Linux / Ubuntu 下的硬體資訊

參照這篇這篇的回答, 學到一些看硬體資訊的好工具。 serverfault.com 果然是系統管理員的好朋友啊。

dmidecode 超級好用, 而且 Linux 都可使用, 配合 -t 會只顯示細部資訊, 像是:
dmidecode -t processor: 明白是幾 CPU 幾 core, 有無 hyperthead。dmidecode -t cache: 查詢 L1, L2, L3 cache 的大小。dmidecode -t memory: 知道插了幾張 RAM 卡, 各張卡大小為何。dmidecode -t slot: 查詢有那些插槽可用, 像是有幾個 PCI Express x16, 有幾個 PCI 32 bits 等。各個 slot 會顯示有無插卡。dmidecode -t system: 主機的廠商。比方說買 Dell 的電腦, 可從這裡看到主機型號和序號。dmidecode -t baseboard: 顯示 on-board 的設備, 像是網卡、音效卡之類的。其它幾個被提到的指令有 lspci 和 lsusb, 和檔案 /proc/cpuinfo 和 /proc/meminfo。可以看到不同細節。lspci -vv 和 lspci -nn 不錯用。有人提到查 driver 時可配合它使用。之後有需求時再試吧。Ubuntu 另有 lshw 會顯示詳細資訊, 不過用 dmidecode 就解決我原本的疑問了, 沒有仔細試它。
待查事項:  目前還不知道要怎麼查那個 slot 上插的是什麼卡。比方說 dmidecode -t slot 顯示 PCI Express x16 和 x8 各插了一張卡, 而 lspci 會列出各張卡的詳細資訊。但是要怎麼知道那張槽是插那張卡呢?

用 decorator 計時

最近讀 open source project 時看到的小技巧, 稍加改寫如下:
def log_it(function): def wrapper(*args, **kwargs): start = time.time() try: return function(*args, **kwargs) finally: stop = time.time() logging.debug('%s: %.3fs' % (function.__name__, stop - start)) return wrapper # usage @log_it def some_function(...): ...
如此一來 call some_function 時就會記錄它的執行時間到 log file 裡。之前沒想過 finally 可以這麼用, 配上 decorator 太簡潔啦。先存下 function 的傳回值, 寫入 log 再回傳也可做到同樣效果, 不過當 function 丟出 exception 時, 就沒記到時間。而上面的寫法仍然會 log 時間。若要區別 function 是否有正常執行完, 得在 try 和 finally 之間加個 except:, 做完該做的事再執行 raise 將 exception 重丟出去。

試改 Chrome Extension

extension Create Link 滿好用的, 可以自定取出網址和網頁標題的格式, 方便貼到 blog 或是 plurk。美中不足的是, 它沒支援短網址。有不少 extension 提供短網址服務, 可惜沒提供像 CreateLink 那樣自制的格式, 讓我能按一個鍵就取出短網址並以我想要的格式排版。比方說按個鍵產生 "SHORT_URL (TITLE)", 就能直接貼到 plurk 上了。

看了一下 Shorten URLs 和 Create Link 的程式碼後, 決定抽出 Shorten URLs 產生短網址的程式碼, 加到 CreateLink 裡。比想像中簡單許多, 懂 JavaScript 的話, 只要了解 Chrome Extension 的規範, 就能輕易上手。不過不知是不是 Chrome 的問題, 有時 Extension 會沒反應, 但重開 Chrome 就好了。

改完的結果在這裡, 整個修改流程如下:
閱讀 Chrome Extension 的入門教學。照著做一遍就會了, 再看一下如何除錯就能上工了。在 github 上 Fork CreateLink 的專案, open source 真好啊。在 Windows 上用 TortoiseGit 連 github, 這一步花掉我最久的時間, 實在是很挫折的事。參考官網說明。途中遇到不少問題, 最後不知怎麼弄對了, 懶得理了。下回考慮用 andLinux 連 github。不過這樣得溫習 git 的指令, 原本就是懶得查指令, 結果讓 TortoiseGit 能和 github 連線, 反而花了更多時間...。載入本機未封裝的 CreateLink, 改一下 manifest.json, 確定自己的修改有發揮作用。個人認為改程式時, 這是最重要的第一步, 愈早完成愈好。看一下 Shorten URLs 的程式明白怎麼用 XmlHttpRequest 透過 GET 連網站, 再查一下相關參數說明, 將 async 設成 false 即可。將程式寫成等三秒沒結果就放棄短網址改用原網址。原本有考慮用 jQuery 做, 看到原作者全部檔案加起來都比 jquery-1.4.2.min.js 小 (34.2KB vs. 70.4KB), 就打消這個念頭了。為了能用 JavaScri…

在 Ubuntu 8.04 上透過 gmail 用命令列寄信

2012-03-01 更新 找到更通用的作法, 用 Python 的 smtplib, 細節見 segfault.in » Sending Emails Via Gmail SMTP With Python。 ( 之前寫的舊方法 ) 用命令列寄信好處多多, 像是程式掛的時候寄個信通知自己。但若又懶得自己架個 mail server, 可以透過 gmail 幫忙寄信。
安裝參考這篇設定 ~/.mailrc 和 ~/.msmtprc。這篇有更詳細的 msmtp 設定。之後用命令列的 mail 寄信時就會透過設好的 gmail client 寄信。簡記過程如下。
$ sudo aptitude install heirloom-mailx msmtp $ vim ~/.mailrc # 見下文 $ vim ~/.msmtprc # 見下文 $ chmod 600 ~/.msmtprc $ echo "Hello, world!" | mail -s "Test from command line" somebody@somedomain.org ~/.mailrcset from="YOURNAME@gmail.com (YOURNAME)" set sendmail="/usr/bin/msmtp" set message-sendmail-extra-arguments="-a gmail" ~/.msmtprcdefaults logfile /home/USER/msmtp.log # gmail account account gmail auth on host smtp.gmail.com port 587 user YOURNAME@gmail.com password YOURPASSWORD from YOURNAME@gmail.com tls on tls_trust_file /usr/share/ca-certificates/mozilla/Equifax_Secure_CA.crt # set default account to use (not necessary with single account) account defaul…

計算 64 bit long 內有幾個 bit 為 1 的速算法

原本在讀 bit array 為啥威, 結果看到 Hamming weight, 即計算一個 "string" 內有幾個 1 的速算法。

基本版 (popcount_1) 的想法還算好懂, 典型的 divide & conquer, 對半切的解法, 看了開頭就猜到後面要怎麼做了。先以 2 bit 為單位算出有幾個 1, 並存在原本的那 2 bit 裡, 再用同樣的手法以 4 bit 為單位算出 4 bit 裡有幾個 1, 並存在原本的那 4 bit 裡, ..., 最後就會在較低的 32 bit 裡得到全部答案。

進階版 (popcount_2) 的第一個寫法有點妙, 我最後才參透它。8、16、32 bit 時最簡單, 若 bit 數的最大值不會超出儲存的空間, 就不用擔心相加會溢位, 而能直接位移並做相加。2 bit 的作法沒變, 4 bit 的作法和 8 bit 開始的作法一樣, 只是先清乾淨「高位元」的地方, 確保後面處理 8、16、32 bit 時不會溢位。

回頭看 x -= (x >> 1) & m1, 將式子展開會得到 x = x - ((x >> 1) & m1)。而原本的算法是 (x & m1) + ((x >> 1) & m1), 將兩式合起來看會得到:
x - ((x >> 1) & m1) = (x & m1) + ((x >> 1) & m1) x - (x & m1) = ((x >> 1) & m1) + ((x >> 1) & m1) 整理左式:
x - (x & m1) = x & (m1 << 1) 整理右式
((x >> 1) & m1) + ((x >> 1) & m1) = 2 * ((x >> 1) & m1) = ((x >> 1) & m1) << 1 = x & (m1 << 1)
所以這個算法沒錯, 將上面的過程反過來推導, 就會發覺可以用 x - ((x >> 1) &am…

Django 和 Python 操作 database 時的額外負擔

為了搞清楚 Django ORM 的效率瓶頸, 做了個簡單的測試, 每筆實驗只有跑個兩三次, 不過結果差不多。
SQL x 1, new object x N從本機的資料庫取出大量資料 (>1m筆), 然後將結果寫入 /dev/null, 資料型別為 utf-8。結果如下:

methodtime (min / sec)memory (G)MySQL client0'080.54python client1'021.4django client without using iterator17'395.xdjango client using iterator3'161.7django client using raw sql1'021.5
實驗細節說明:
Django 1.2, MySQL 5.0。用 time 測時間。memory 是我用眼睛注意 htop 的數據。django client without using iterator 跑太久了, 只好邊玩猴子守城四代邊跑實驗, 最大值就沒抓準了。
(2012-02-02 更新) 只需定時執行grep VmPeak /proc/PID/status | awk '{print $2}' 就不用「辛苦」地邊玩遊戲邊抓數據了。年輕時不懂事, 了解一些系統知識差異真大。MySQL client 的用法: mysql < my_select.sql > /dev/null。沒做 encoding 轉換。python client 的操作內容只有從 utf-8 轉成 unicode (MySQLdb 做的) 再轉回 utf-8, 補個換行字元, 就寫入檔案。django 會自動把 utf-8 轉成 unicode。django client 的操作和 python client 幾乎一樣。從上面的結果可以看出, 資料大時還是用 raw SQL 較好, 時間差了兩倍。看來生成 Django 物件比生成 Python 物件貴了一些。

至於 python 花了 mysql client  8 倍的時間, 只好當作用 python 的必要成本。不知其它語言 (C++、Java、Ruby、PHP) 這方面的額外負擔有多大。

為了弄清楚時間花在那裡, 另外試了 ja…

在 http response header 裡設 expire 以減少 request 數量

High Performance Web Sites: Rule 3 - Add an Expires Header 提到可以在 http response header 裡設 expire time 減少 client 送出 http request。藉由 last modified time 和 etag 只能省去 client 重下載檔案的時間, client 仍會送出 http request。若一個頁面載入十個檔案 (JavaScript、CSS、圖檔等), client 仍會送出十個 http request。別小看 http request 的時間, 數量一多若有其中一兩個被拖到, 網頁就會拖慢。

Apache 設定 expire 的方式很簡單:
a2enmod expires  # enable mod_expires編輯 /etc/apache/conf.d/cache.conf (檔名隨便取):

ExpiresActive On ExpiresByType text/css "access plus 4 weeks" ExpiresByType text/js "access plus 4 weeks" ExpiresByType image/gif "access plus 4 weeks" ExpiresByType image/jpeg "access plus 4 weeks" ExpiresByType image/png "access plus 4 weeks"這樣會把 CSS、JavaScript 和常用圖檔設成使用者拿到檔案的四週後才會過期。也可以設成依檔案修改時間來設過期時間, 而不是使用者拿到的時間, 參見 mod_expires 看更詳細的說明。

但設了 expire time 後帶出一個新問題, 萬一 server 端在 expire time 前有更新檔案怎麼辦? 在該頁的留言裡有討論幾種解法的優缺點:
加版本編號到檔名裡, 如 yahoo_2.0.6.js, Yahoo 是這麼做的。缺點是要改 code (HTML header、CSS 內文等), 也得想想怎麼和版本控制整合, 變成每次 deployment 就得改檔名。…

用 pip 裝 lxml

剛學 python 時覺得 lxml 超難裝, 因為它用到一些 C library, 不過我一直沒搞清楚它到底用到什麼, 後來都用 ubuntu package 裝 lxml (aptitude install python-lxml)。

一兩個月前看了這篇《Tools of the Modern Python Hacker: Virtualenv, Fabric and Pip》, 想說就來試看看這三個東西吧。陸續試用後, 發覺果然是強大的工具, python 開發者的必備幫手啊!! 最近用的套件要用到 lxml, 這回決定用 pip 裝 lxml, 確保能裝在 virtualenv 裡, 方便管理。

結果果然不是 pip install lxml 就會搞定的事。看到錯誤訊息裡提到 lxml 要用到這些東西:
Cython (optional)development packages of libxml2development packages of libxslt 摸索一下就找到裝法: pip install cythonsudo aptitude install libxml2-devsudo aptitude install libxslt-dev之後再 pip install lxml 就搞定了。克服以前沒做到的事, 有小小的成就感。 題外話, 未來 python 社群要轉用 pip 而非 easy_intall 裝 python package, 官網的安裝說明也都從以前的 easy_install 改成 pip 的指令了。看到社群的成長, 挺開心的。雖然用 open source 開發偶而踏到雷, 一天的時間就沒了, 但在自己沒做啥事的情況下有新功能可用, 還是格外的開心, 大家真是好人, 自己也該有所回饋才是。