2012年3月3日 星期六

[Android] 變更 CPU 狀態、使之 offline、降頻、超頻和穩頻 @ PandaBoard

之前被要求測試 Android 4.0 的 Renderscript 是否可以做平行處理,但是隨著工作量的增加,一直把這件事擺在後頭,今天,終於測試完了!最早之前,我想到的是修改 HelloCompute 範例中的 mono.rs 檔案,裡頭的 root 函式是一個工作單位,如果一台裝置是多 CPU 且系統支援平行處理的話,理論上一次會有多個工作單位在進行。因此想說擺一個 static 變數在 root 函式中,每次把數值加一並印出,依此觀察系統運作時,印出的數字是否不連續,如果不連續或有相同時,想要把這當作有平行的依據。今天則想到一個更簡單的妙招,就是在 root 函式中,用一個 loop 讓它不結束,如此一來,在雙核心的 CPU 下,就會看到兩次訊息,單核心就只會看到一個!理由很簡單,因為工作沒做完,就不會分配新的工作。


這在 Pandaboard 上測試很成功,我看到了兩次 root 函式印出的訊息,然而,我更想要驗證自己設計的方式是不是對的,那就必須找只有一個核心的裝置,甚至四核心的裝置測試,如果在一個 CPU 的裝置只看到一次,在四核心的裝置可以看到四次,那代表驗證成功了。結果把構想跟同事分享後,同事教了我一個密技!那就是手動去更改 CPU 的狀態!對 Pandaboard 來說,就是強制把一個 CPU 關掉(offline),慶幸的,驗證對了!


這個用法其實最常看到別人修改機子的 CPU 頻率,例如一台裝置他本身的 CPU 可以跑到 500 MHz,但為了省電、穩定、商業手段等理由,系統廠會降頻跑,例如用 300 MHz 運行等等,這些則可以透過系統設定檔進行操作的,也因此有人刷機改機追求好的效能。


此例只在雙核心的板子上,手動把一顆 CPU 關掉:


$ adb devices
List of devices attached
################ device
$ adb shell ls -R /sys | grep -i cpu
...
/sys/devices/system/cpu:
cpu0
cpu1
cpufreq
cpuidle
...
$ adb shell
root@android:/ # cd /sys/devices/system/cpu
root@android:/sys/devices/system/cpu # ls
cpu0
cpu1
cpufreq
cpuidle
kernel_max
offline
online
possible
present
root@android:/sys/devices/system/cpu # cat online
0-1
root@android:/sys/devices/system/cpu # cat cpu*/online
1
1
root@android:/sys/devices/system/cpu # echo "0" > cpu1/online
root@android:/sys/devices/system/cpu # cat cpu*/online
1
0
root@android:/sys/devices/system/cpu # cat online
0


在 Pandaboard 上,因為是雙核心架構,所以會看到 CPU0 和 CPU1 的資訊,接著分別印出 /sys/devices/system/cpu/cpu0/online 和 /sys/devices/system/cpu/cpu1/online 資訊,標記 1 代表都是運作中,同時印出 /sys/devices/system/cpu/online 會是 0-1 的資訊。接著使用 echo "0" > /sys/devices/system/cpu/cpu1/online ,代表令 CPU1 下線不運作,反之 echo "1" > /sys/devices/system/cpu/cpu1/online 則是讓他醒來運作。透過這個簡單的方式,就能讓控制裝置多核心的狀態啦!真是超方便的,但別忘了執行 adb shell 時,必須有 root 權限才行。


至於 CPU 調頻、穩頻,還沒親手測過,但原理很像,就順便筆記一下:


以 /sys/devices/system/cpu/cpu0 來說,裡頭 cpufreq 目錄,裡面又有 cpuinfo_cur_freq、cpuinfo_max_freq 和 cpuinfo_min_freq 等資訊,不難猜到吧?只要把 scaling_max_freq 跟 scaling_min_freq 設成一樣就是穩頻,把 scaling_max_freq 調高則是超頻吧,有空再來試試!穩頻應該很好用,可以在一樣的 CPU 狀態下收集測試數據。


2012年2月26日 星期日

[Python] 使用 Heroku 筆記 @ Windows 7


差不多該玩一下 Heroku 了,這跟 Google App Engine 一樣,是一套 PaaS 服務架構,仔細看才發現 Heroku 竟然是架設在 EC2 上面的服務,讓我想起 Dropbox 這有名的服務是使用 S3 架構,原來有不少成功的服務不見得從機房都自己打造呢!這樣的感觸滿特別的,不需堅持自己從零開始。使用 Heroku 跟 GAE 這類服務的主因之一,是想要妥善利用身邊的資源,不見得要去租一台虛擬機器來使用,雖然權限大能做的事很多,但一開始太多資源不見得是好事,除此之外,使用 Heroku 跟 GAE 的好處是碰到流量大時,錢砸下去就可以 Scaling ,這樣的投資似乎不差吧?最大的缺點是要去習慣這些架構,轉個角度來看,或許這就是雲端的成本吧?就像把想做的事寫成符合 Hadoop/HBase 架構一樣。


對 Heroku 來說,計費方式是以 process 單位小時的方式計價,例如免費帳號就是一個月使用 750 小時不用錢,使用一隻 process 一個月是 720 小時,所以就不用錢。原先是一隻 process 用一小時 0.05 美元,其中 process 分為 web process 跟 background process 兩種。只是 Heroku 資料庫免費空間僅 5MB 而已,像 GAE 則是 1GB 大小,但是 GAE 還有計算 in/out 的資料量等等。我還沒搞懂 Heroku 有沒計算流量以及是否有限制 library 的部分 :P


好啦,回過頭講一下在 Windows 的操作方式,準備資源:



依序安裝完 Heroku Windows、Python 2.7 後,把 virtualenv-1.7.1.2.tar.gz 解壓縮後,就可以準備操作了,主要參考 Heroku - Getting Started with Python on Heroku/Cedar 這篇,先用最簡單的方式體驗一下,其中有用到 python Flask,這是一個輕量型 MVC 的 framework 囉。


步驟:


Python 2.7 在 C:\Python27\python.exe;virtualenv 在 C:\virtualenv-1.7.1.2;heroku-toolbelt 預設在 C:\Program Files\Heroku。安裝完軟體後,直接用 cmd 運作即可。


建立空目錄,如 helloflask


C:\Users\user>mkdir helloflask && cd helloflask
C:\Users\user\helloflask>


建立 virtualenv 環境


C:\Users\user\helloflask>C:\Python27\python.exe C:\virtualenv-1.7.1.2\virtualenv.py venv --distribute
New python executable in venv\Scripts\python.exe
Installing distribute...done.
Installing pip...done.


啟用虛擬環境:


C:\Users\user\helloflask>venv\Scripts\activate
(venv) C:\Users\user\helloflask>


安裝 Flask:


(venv) C:\Users\user\helloflask>pip install flask


Downloading/unpacking flask
Downloading Flask-0.8.tar.gz (494Kb): 494Kb downloaded
Running setup.py egg_info for package flask
...
Downloading/unpacking Werkzeug>=0.6.1 (from flask)
Downloading Werkzeug-0.8.3.tar.gz (1.1Mb): 1.1Mb downloaded
Running setup.py egg_info for package Werkzeug
...
Downloading/unpacking Jinja2>=2.4 (from flask)
Downloading Jinja2-2.6.tar.gz (389Kb): 389Kb downloaded
Running setup.py egg_info for package Jinja2
...
Successfully installed flask Werkzeug Jinja2
Cleaning up...


建立 app.py 程式(C:\Users\user\helloflask\app.py):


import os


from flask import Flask
app = Flask(__name__)


@app.route('/')
def hello():
        return 'Hello World!'


if __name__ == '__main__':
        # Bind to PORT if defined, otherwise default to 5000.
        port = int(os.environ.get('PORT', 5000))
        app.run(host='0.0.0.0', port=port)


建立 Heroku 環境檔案:


(venv) C:\Users\user\helloflask>pip freeze > requirements.txt
(venv) C:\Users\user\helloflask>cat requirements.txt
Flask==0.8
Jinja2==2.6
Werkzeug==0.8.3
distribute==0.6.24
(venv) C:\Users\user\helloflask>echo web: python app.py > Procfile
(venv) C:\Users\user\helloflask>cat Procfile
web: python app.py


執行 web service:


(venv) C:\Users\user\helloflask>foreman start


接著就可以用瀏覽器瀏覽 http://localhost:5000 啦,可以看到印有 "Hello World!" 字樣,要結束的話就按 ctrl+c 囉


如此一來,就完成本地端的測試開發囉。至於 Deploy 的部分,則是好好看一下官網上面的介紹,這邊就不多提了。


整體上,覺得 GAE 比較簡單一點,過程中只需安裝 python 跟 GAE SDK 環境,但 Heroku 還須使用 Ruby 環境等,或許對已習慣用 Ruby 的人來說,是非常自在的 :D


2012年2月24日 星期五

行動化

筆電與麥當勞


越來越習慣帶著筆電的生活,有沒網路倒還好,沒資源有沒資源可以把玩的事,看看很久沒翻的電子書,不然就在本機端練習一下 GAE 也好(python跟GAE都有離線文件),快不快活只是一個角度的差別。有網路時就把玩網路,換個環境其實還不錯的,有聲響也不會打亂寫程式的步調,有人影也能輕鬆望個幾眼,麻煩的大概是上廁所也要把東西帶在身上,以及走路背著幾公斤的重訓。我想,也到了該認真投資一台好筆電的時刻了吧?


回顧一下,兩個月左右,接觸了硬體界的朋友,才發現硬體界似乎不是我想走的方向,這邊一樣高深莫測,但我開始比較清楚自己想要的是甚麼東西了,剛好幾天前跟 IC 界的聊天,他說目前 IC 廠很缺上層玩 ap 的人,只是十年了,玩上層 ap 仍被認為是一件簡單的事,彷彿純軟的、沒硬體成本的容易不被重視。這讓我想起工作上曾跟硬體界的朋友開會,他一頭就劈做 xxx 的有啥難的,不就想一下演算法就解掉了嗎?說真的我無法反駁,我無法證明問題的難易度,這不像硬體可以用看的、摸的、花錢的等等的估算。


無論如何,還是要珍惜手邊的資源,把能學能掙的要盡量把握,深信某一天某一刻一定會派上用場的 :)


2012年2月23日 星期四

Android 開發教學筆記 - 研究 Renderscript 以 Carousel 為例 @ PandaBoard


這個 Carousel 例子是一個類似翻頁動作的特效,仔細看一下,是不是覺得自己在一個圓心中間,被很多照片包圍住呢?這個例子是 10 張照片,等於你走進了一個 10 面牆的房間,隨著你觸動移動事件,等於房間每面牆近似以你為中心旋轉。猜測原理後,就可以去驗證實做了。


six
此圖為正六邊形。左下角用來推邊長 len,右上角用來推各點 P1, P2, ... 的座標,θ就是兩頂點的夾角,此外,定義右下角是第一象限,左下是第二。


首先,讓我驗證這是個正 n 邊形的效果,主要是看到這段程式碼:


@ initBitmaps(): // Calculate the length of the polygon
len = RADIUS * 2 * sin(M_PI/NUM_ITEMS);


這是計算正 n 邊形的邊長公式,其中 n = NUM_ITEMS,此例就是 10 張照片


接著,開始計算正 n 邊形各頂點的三維座標資訊:


此例是把 y 軸當作 0 來看代 (x,y,z) = ( sinθ * RADIUS, 0, -cosθ * RADIUS)


@ initBitmaps(): // Calculate the vertices of rectangles
vertices[i*3] = sin(angle * M_PI / 180) * RADIUS;
vertices[i*3 + 1] = 0;
vertices[i*3 + 2] = -cos(angle * M_PI / 180) * RADIUS;


畫出正 n 邊形:


@ displayCarousel(): // Draw the rectangles
for (int i = 0; i < 10; i++) {
        ...
        rsgDrawQuadTexCoords(...);
        ...
}


rsgDrawQuadTexCoords 則是輸入長方形四個座標點,但是每一個頂點傳入的參數還有多了兩個,該數值通常定義為 (u, v),其中 u = 0 代表左邊,反之右邊,而 v =0 代表上面,反之下面,因此此例依序透過 (u, v) = (0, 1), (0, 0), (1, 0), (1, 1)來描述頂點位置。其中 (0, 1) 和 (0, 0) 使用的頂點座標位置分別是 ( r*sinθ , -(len/2), -r*cosθ ) 和 ( r*sinθ , len/2, -r*cosθ ),還記得最初定義得座標系嗎? y 軸是縱軸,此例第一張照片左上角座標為 ( r*sinθ , len/2, -r*cosθ ),左下角為 ( r*sinθ , -(len/2), -r*cosθ ),至於第一張圖的右上角跟右下角得座標,那就是用正 n 邊形第二個頂點來計算,以此規則算出 10 張照片的座標位置


至於觸動時的旋轉效果,則是透過 rotate 的功能實作的:


@ displayCarousel():
// Load vertex matrix as model matrix
rs_matrix4x4 matrix;
rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, -400.0f); // camera position
rsMatrixRotate(&matrix, rot, 0.0f, 1.0f, 0.0f); // camera rotation
rsgProgramVertexLoadModelMatrix(&matrix);


先把 camera(觀看照片的位置?) 位置移到 (0,0,-400) 位置,由於此正 n 邊形半徑為 828,因此距離照片 428 各單位。接著讓 camera 的位置對 (0,1,0) 向量旋轉,就達成這個效果囉


其他資訊:


rsMatrixLoadPerspective 對應 OpenGL:gluPerspective,以 void gluPerspective( GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar); 來說,分別為視角、寬高(長)比、前景和背景,查 wiki 可知人的視角大約 120 度,魚眼是 180 度,在此例是 30 度視角為例,接著寬高(長)比沒啥問題,而 near 和 far 的參數,若有玩單眼相機的話,應該不會太陌生,在網路上找資料的結果,通常前景是用一個大於 0 的數值,如 0.1f 等。


rsMatrixLoadTranslate 對應 OpenGL: glTranslate,以 void glTranslated(GLdouble x, GLdouble y, GLdouble z); 來說,就是切換到 (x,y,z) 座標


rsMatrixRotate 對應 OpenGL: glRotate,以 void glRotated(GLdouble angle, GLdouble x, GLdouble y, GLdouble z); 來說,代表以 (x,y,z) 向量為軸心,旋轉 angle 弧度。此外,(0,1,0) 和 (0,100,0) 都是一樣的向量,網路資料是說用 (0,1,0) 所造成的運算量較低。


而 rsMatrixLoadTranslate 和 rsMatrixRotate 定義可在 Renderscript Reference 查詢。


2012年2月19日 星期日

[Python] 使用 Google App Engine 之資料模型(Data Model)筆記 @ Windows 7


操作簡單的 GAE 後,開始來摸摸資料儲存的部分。對於 GAE DB 的部分,操作不難,跟 Django 很像,先定義一個 Data Model (資料庫的資料表),接著就可以操作了!此例僅簡單帶過,更豐富的操作方式請參考官網 GAE - 資料模型


建立 NewsData 資料模型:


from google.appengine.ext import db


class NewsData(db.Model):
        check = db.StringProperty()
        url = db.StringProperty()
        title = db.StringProperty()
        date = db.DateProperty()


新增一筆資料:


import datetime
item = NewsData(chech='1',url='http://localhost',title='TestNews',date=datetime.datetime.now().date())
item.put()


查詢資料:


使用 Data Model 查詢:


q = NewsData.all()
results = q.fetch(3)
for p in results:
        print '<a href="%s">%s</a>' % (p.url,p.title)


使用 GqlQuery 之 SQL 語法:


# 查詢 3 天內的新聞
q = db.GqlQuery("SELECT * FROM NewsData WHERE date > :1 ORDER BY date DESC", datetime.datetime.now().date() - datetime.timedelta(days=3) )
results = q.fetch(3)
for p in results:
        print '<a href="%s">%s</a>' % (p.url,p.title)


上述都很淺顯易懂,接著能嘗試簡易的 MVC 架構,把 DB Modle 定義在 mydb.py 檔,由 myput.py 和 myquery.py 作為 CGI 來操作(MVC的 VC 偷懶合在一起 XD)。


目錄結構:


app.yaml
favicon.ico
index.yaml
main.py
mydb.py
myquery.py
myput.py


app.yaml:


application: engineapp
version: 1
runtime: python
api_version: 1


handlers:
- url: /favicon\.ico
  static_files: favicon.ico
  upload: favicon\.ico


- url: /query
  script: myquery.py


- url: /put
  script: myput.py


- url: .*
  script: main.py


mydb.py:


from google.appengine.ext import db
class NewsData(db.Model):
        check = db.StringProperty()
        url = db.StringProperty()
        title = db.StringProperty()
        date = db.DateProperty(auto_now_add=True)


myput.py:


# -*- coding: utf-8 -*-
print 'Content-Type: text/html'
print ''


import mydb
import cgi, hashlib, datetime, urllib
from google.appengine.ext import db


request = cgi.FieldStorage()


newsDate = datetime.datetime.now().date()
newsTitle = 'defaultTitle' if request is None or 'title' not in request or request['title'].value == '' else cgi.escape(request['title'].value)
newsURL = 'http://localhost' if request is None or 'url' not in request or request['url'].value == '' else request['url'].value
newsCheck = hashlib.md5(str(newsTitle)+str(newsURL)).hexdigest()


if mydb.NewsData.all().filter('check =',newsCheck).get() is None:
        item = mydb.NewsData(check=newsCheck,url=unicode(newsURL,'utf-8'),title=unicode(newsTitle,'utf-8'),date=newsDate)
        item.put()
        print 'Put'
else:
        print 'No Operation'


myquery.py:


# -*- coding: utf-8 -*-
print 'Content-Type: text/html'
print ''


import mydb
import datetime
from google.appengine.ext import db


print """
<html>
        <head>
                <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
        </head>
        <body>
"""


q = db.GqlQuery("SELECT * FROM NewsData WHERE date > :1 ORDER BY date DESC", datetime.datetime.now().date() - datetime.timedelta(days=3) )
results = q.fetch(50)
for p in results:
        url = p.url
        date = p.date
        check = p.check
        title = p.title
        print '<a href="%s">[%s] %s(%s)</a><br />' % ( url.encode('utf-8'), date, title.encode('utf-8'), check.encode('utf-8') )


print """
        </body>
</html>
"""


如此一來,透過瀏覽 http://localhost:port/put (或 http://localhost:port/put?title=123&url=www.google.com ) 新增資料,透過 http://localhost:port/query 顯示資料。除此之外,還可以透過 Google App Engine Launcher 的 SDK Console ,直接用瀏覽器去查看資料庫的東西,實在方便:


GAE11


這邊容易碰到的問題是 DB 內資料的編碼問題,我在 myput.py 把 CGI 得到的東西用 unicode(data,'utf-8') 的存進資料庫,在 myquery.py 時,則是用 data.encode('utf-8') 處理印出的部分。