2017年8月3日 星期四

神經網路加速器大戰開打

http://www.eettaiwan.com/news/article/20170726NT01-neural-accelerator-battle-begins?utm_source=EETT%20Article%20Alert&utm_medium=Email&utm_campaign=2017-07-27

隨著許多嵌入式系開始變得「智慧」且「自主」,以人工智慧(AI)神經網路為導向的嵌入式系統市場即將起飛,神經網路加速器大戰一觸發…

嵌入式神經網路加速器市場持續升溫,從智慧音箱、無人機到智慧燈泡等越來越多的系統準備在本地執行神經網路,以取代傳送至雲端進行運算的途徑。

Movidius副總裁兼總經理Remi El-Ouazzane日前在接受《EE Times》訪問時,將這個成長中的趨勢定義為「讓事情變得更智慧與自主的一場競賽」。

20170726_Movidius_NT01P1Remi El-Ouzaane,Movidius副總裁兼總經理Remi El-Ouazzane

英特爾旗下子公司Movidius在上週推出了一款採用USB外形的獨立式人工智慧(AI)加速器。El-Ouazzane說,這款名為Movidius神經運算棒(Neural Compute Stick)的AI加速器設計,可輕鬆簡單地插入Raspberry Pi或X86 PC,讓大學研究人員、獨立軟體開發人員與程式增補人員易於為嵌入式系統進行編譯、調整以及加速深度學習應用。

Movidius在去年秋天被英特爾收購後,如今已成為英特爾新技術部門的一部份。Movidius之前曾經開發出業界首款視覺處理器——Myriad 2 VPU。如今,El-Ouazzane表示,推出這款運算棒的最終目標在於讓Movidius VPU成為可在邊緣執行神經網路的「參考架構」。

20170726_Movidius_NT01P2Movidius Myria 2 VPU方塊圖 (來源:Intel/Movidius)

儘管目標遠大,但業界分析師隨即指出,Movidius Myriad 2 VPU當然不是嵌入式系統中可在邊緣執行神經網路的唯一選擇。

催生新產品類別:神經加速器

Tirias Research首席分析師Jim McGregor表示:「從技術上來看,您可以使用任何具有處理元件的開發板,並用於執行一種模型。例如機器學習(Machine learning)/AI模型已經執行於各種廣泛的處理器和SoC了,特別是針對行動領域。」

高通(Qualcomm)以Snapdragon系列實現的影像辨識可說是最佳的例子。高通從Snapdragon 820開始採用自家開發的模型,McGregor說:「Snapdragon基本上就是推理引擎」。

具有平行處理元件(如GPU、DSP和FPGA)的處理解決方案非常適於作為推理引擎。McGregor解釋說,許多正在開發中的客製化晶片解決方案都採用可內建於SoC的DSP或FPGA。

Linley Gwennap首席分析師Linley Gwennap對此表示贊同。他在最近出刊的《微處理器報告》(Microprocessor Report)中寫道:高通、蘋果(Apple)和英特爾(Movidius)都在「打造一種新的產品類別:神經加速器。」

Gwennap解釋說,對於這些以客戶端為基礎的加速器需求來自於要求極低延遲的自動駕駛車。Gwennap在該報告的評論中指出,在本地進行處理的新技術將會開始「滲透至一些較低成本的應用」。他預測,「在消費裝置中,小型的神經加速器可能是SoC中的一個重要區塊,就像是繪圖核心或影像處理器一樣。幾家智慧財產權(IP)供應商開始提供這一類加速器,期望盡可能地降低額外的硬體成本。」

Gwennap在《EE Times》的訪談中指出,Movidius Neural Compute Stick對於開發人員幾乎沒有什麼不同。「對於開發人員來說,這並沒什麼不起。典型的PC就能產生至少100GFLOPS,特別是如果它還內建繪圖卡,直接在PC上進行開發會更好。對於需要較低功率處理器的嵌入式系統,高通Snapdragon 835提供超過250GOPS的效能,對於大多數的推理應用來說都夠了。」

20170726_Movidius_NT01P3(來源:Movidius)

不過,Movidius的El-Ouazzane並不贊同這樣的看法。

首先,Movidius Neural Compute Stick利用USB讓神經網路更易於存取,有利於製造商或程式增補人員用於開發深度神經網路原型或進行調整。

其次是電源效率的問題。El-Ouazzane指出,Movidius的解決方案可讓神經網路在邊緣運算時旳功耗小於2瓦(W)。

然後,還有成本的考量。El-Ouazzane說:「我認為這是一個殺手級因素。Movidius Neural Compute Stick要價79美元,可望讓神經網路的發展變得『超級普遍』。」

他預期開發人員能使用Movidius的神經運算平台(Neural Compute Platform)應用程式介面(API),「將經過訓練的Caffe、前饋卷積神經網路(CNN)輸入工具套件中,並進行配置,然後編譯成一種可用於嵌入式部署的調整版本。」

以AI為導向的嵌入式應用?

隨著許多嵌入式系開始變得「智慧」且「自主」,El-Ouazzane預計以AI為導向的嵌入式系統即將爆發。

但是,不久的將來即將出現什麼樣的AI裝置?

McGregor預測,「我認為在2025年以前,每個人所接觸的每一款新系統/平台都將具有某種程度的AI元素,它可能內建於裝置本身,或存在於網路、雲端,或是分散在其間。」

他補充說:「它可能就像是使用者介面或應用一樣簡單,或者是像虛擬助理或自主控制般複雜的某種裝。目前已經有許多應用已經導入了AI,包括虛擬助理、搜尋引擎、財務建模、與文章寫作.....等等。」

他說:「而在裝置上,AI將因各種不同的原因而持續成長,特別是資料的安全、即時互動/處理以及頻寬限制等。然而,在裝置上進行訓練才是真正的挑戰,最終可能需要新的處理模型。」

英特爾想掌握一切…

當然,Movidus現在是英特爾的子公司,但其目標並不僅限於有更多嵌入式系統在邊緣執行神經網路。El-Ouazzane將其產品發佈架構在英特爾端對端AI產品組合的更大脈絡之下。

他指出,英特爾擁有廣泛的AI產品組合,並為其提供一整套完整的開發工具和資源。

「無論是在英特爾Nervana雲端上訓練AI神經網路、最佳化新興的工作負載(如AI、、VR和AR),以及使用英特爾Xeon Scalable處理器實現自動駕駛,或是以Movidius視覺處理器技術將AI導入邊緣,」英特爾宣稱該公司已為下一代AI驅動的產品和服務提供了全面的AI產品組合、工具、訓練和部署選擇。

然而,Movidus是否真的能在即將變得無所不在的AI嵌入式系統中成為其關鍵參考架構,目前還不而知。

Tirias Research的McGregor儘管認同Neural Compute Stick是「一款可在新設計中快速評估AI模型訓練效能的有利工具,」但他仍指出,「設計人員仍然必須在最後的系統設計階段執行類測試,特別是如果他們並未使用Moviduis的晶片和/或Caffe架構。」

The Linley Group的Gwennap還認為,Movidius無法達到其目標——實現在邊緣運算神經網路的嵌入式系統設計。

區隔推理引擎與訓練

Gwennap指出,「值得注意的是,新的AI時代並不會有『參考架構』。諸如TensorFlow和Caffe等架構將會把軟體(神經網路)和硬體分開來。」他補充說:「映射到Myriad VPU的網路可以輕易地映射到Snapdragon或其他處理器上。因此,AI處理器將在性能和效率的基礎上進行競爭,而不是鎖定於指令集。」

同樣地,他也不認同「同樣的工具適用於訓練和推理」的論點。Gwennap說:「一旦網路開始接受訓練,就可以輕鬆地部署在支持主流架構的任何平台上。而且,主流的架構都是開放源碼,以避免單一家廠商鎖定。」。

El-Ouazzane看好AI處理器抽取底層硬體的能力,它讓設計者可將推論與練訓分離開來。但他重申先前的觀點,從長遠來看,能夠使用相同的工具進行神經網路的訓練和部署,才有助於系統設計人員。

他以Movidius的開發藍圖為例表示,未來三年,深度學習的效能預計將會增加50倍。「為了在不增加功耗的情況下實現這一目標,我們可以在訓練方面設計許多架構技巧。」El-Ouazzane補充說:「當你在斟酌這些技巧時,讓學習和推理方面置於同一平台是至關重要的。」

編譯:Susan Hong

(參考原文:Neural Accelerator Battle Begins,by Junko Yoshida)

2017年7月30日 星期日

生態缸組合#水芙蓉#荷花 功用

生態缸組合#
[水芙蓉功用] 
水芙蓉藏在水面下的根系非常發達,可以附著微生物以吸收分解水中的汙染物,
包括有機污染物及無機污染物。
另外水芙蓉植物體(biomass)含有2.5%左右(base on dry weight)的氮,
這表示在生長及繁殖過程中會從水中攝取大量的氮,
故可以降低水中氮的濃度,經其淨化過的水對於水體優養化的威脅大幅降低。
由於水芙蓉是浮水性植物,
其生長所需的營養塩係直接從水中攝取,而不是來自於底泥,
所以不會像荷花等挺水性植物扮演營養泵的角色。

[挺水性水生植物]
包括草本木本兩類。
它們通常生長在水邊或水位較淺的地方,根也是長在土裡,而葉片或莖卻挺出水面,
再者,挺水性植物的花多數在水面上開放,
典型的草本挺水植物有野慈菇水稻蘆葦茭白筍荷花香蒲等。
 另有挺水性木本植物,這通常是指生活於河流的出海口及半淡鹹水的湖沼旁,
而以紅樹林植物,如水筆仔等為主要代表植物。

2017年7月20日 星期四

JavaScript 傳奇:從跑龍套到挑大樑的程式語言

https://buzzorange.com/techorange/2016/09/30/legend-of-javascript/

接觸程式設計幾十年,看過多少程式語言的起起落落,這其中最讓我驚奇的,就是 JavaScript 語言了。許多人可能聽過 JavaScript 誕生的故事:1995 年 5 月,Netscape(網景)的 Brendan Eich 在公司要求下,花了 10 天創造出來的。只有 10 天!這麼倉促寫出來的程式語言,如今卻跑在全世界幾十億台運算裝置上。至今 JavaScript 開發者仍急速增長,並雄踞「GitHub 熱門程式語言排行榜」榜首。
當年最耀眼的明星是 Java(準確的說是 Java applet, 今年剛被 Oracle 宣告終止 的一種技術)。我印象還很深刻:1995 年我在 Stanford 唸研究所的時候,有一天在同學宿舍的電腦看到瀏覽器裡跳來跳去的 Java Duke,當時大家那種興奮的心情。那是我們第一次在瀏覽器上看到會動的東西啊!至於 JavaScript(當時叫 LiveScript),大家跟本聽都沒聽過,更不用說去研究了。如果程式語言的世界也有勵志故事,JavaScript 的故事肯定能激勵其它程式語言:從小很窮幫人賣一些雜貨,沒人要做的髒活、苦活它都願意幹,辛苦奮鬥十幾年,終於有了今天的成就……
27422431136_b4d01321d5_z

JavaScript 簡史

進展緩慢的 15 年

JavaScript 誕生於 1995 年。我寫 JavaScript,是從 1997 年的 Netscape/IE4 瀏覽器上開始的。在那個大家都用 C 寫 CGI 程式、用 Perl 語言就已經很潮的年代,JavaScript 不過是開發過程中的配角,那種電影結尾 roll 字幕才會出現名字的小角色。當時它多半負責檢查表單、alert 訊息、popup 視窗這類雜事。後來 DHTML 出現,JavaScript 可以修改網頁元件,戲份多了一些,但仍屬於配角。直到 2004 年 Gmail 推出,帶動 XHR/Ajax 相關技術發展,它才逐漸成為網站前端開發的主角。接下來幾年,JavaScript 與 Adobe Flex 分庭抗禮,成為當時 rich client 應用程式開發的兩大陣營(也許勉強再加上微軟 Silverlight)。
然而,JavaScript 語言的粗糙原始、加上瀏覽器(特別是 IE)相容性這兩大問題,15 年來並未獲得改善。用 JavaScript 開發應用程式,一直是件令人痛苦的事,是軟體開發者能不碰就不碰的語言。YUI、jQuery 等程式庫相繼出現,廣受歡迎,減輕了開發者的痛苦,但這並未改變 JavaScript 語言本身存在重大缺陷的事實。與 C++、C#、Java、Python 這些成熟語言比起來,JavaScript 顯得很寒酸,像個玩具語言、拼裝車。

突飛猛進的 5 年

2010 年 ES5(JavaScript 語言標準──ECMAScript 第 5 版)的推出,與 2011 年 Node.js/npm 的出現,使事情開始發生變化。ES5 看似改變不大,其實解決了很多關鍵問題,是 JavaScript 邁向成熟語言的分水嶺。Node.js 則是幫助 JavaScript 切斷與瀏覽器的臍帶,使它成為一門獨立存在的語言,開啟了各種全新應用;於此同時,npm 解決了 JavaScript 多年來缺乏模組化套件資源庫的問題,各種 JavaScript 套件在 npm 平台如雨後春筍般冒出來,很多公司紛紛開始採用 JavaScript 開發大型前後端程式。(我在 2012 年寫的一篇英文 blog:“JavaScript, a rising star”,記錄了我當時看到的一些現象,也預言 JavaScript 的興起,當然很多人也都看到了這一趨勢。)
2015 年 6 月,眾多開發者期待已久的 ES6(ECMAScript 第 6 版)終於定案。對 JavaScript 開發者而言,這可是一件大事!5 年前的 ES5,修正了 JavaScript 諸多缺陷,並促使 IE 瀏覽器往標準靠攏;ES6 則為 JavaScript 注入現代語言的重要特性,使 JavaScript 成為一個足以挑大樑的全方位程式語言。其實早在 ES6 正式定案之前,JavaScript 社群早已迫不及待開始用新標準開發程式了,各家瀏覽器也紛紛提早支援 ES6。比起當年 ES5 支援慢吞吞才到位的景象,不可同日而語。
不過在這期間,JavaScript 發生過一次重大危機。

io.js 分裂危機

過去 Node.js 的發展路線(roadmap)由 Joyent 公司主導,然而 Node.js 社群是由眾多志願者貢獻心力,才得以蓬勃發展。2014 年 12 月,幾位 Node.js 核心開發者不滿 Joyent 幾近獨裁的主導方式,決定另起爐灶,成立 io.js 計劃。(Node.js 的授權條款允許任何人重製改寫程式碼,只是不能使用 Node.js 的名字。)一開始 Joyent 態度強硬,無意改變他們的做法,但隨著越來越多使用者轉投 io.js 陣營的懷抱、加上 Node.js 因核心開發者的離開而陷入停滯,Joyent 終於讓步,接受 io.js 提出的條件,成立一個中立的 committee 主導未來 Node.js 發展。2015 年 6 月,雙方陣營一致通過,在新成立的 Node.js Foundation 之下共同合作。2015 年 9 月,由 Node.js v0.12 與 io.js v3.3 程式碼合併而成的 Node v4.0 正式發佈,Node.js 社群的分裂危機也隨之落幕。
以下我就分別介紹 ES6 的重大特色,以及我認為 JavaScript 專案開發應該注意的要點/常用工具。

一、ES6 重大特色

ES6 是 JavaScript 有史以來的最大改變,匯集了眾多頂尖工程師的智慧。與 Java、C#等由單一商業公司主導,或 Perl、Python、Ruby/RoR 等由 BDFL(Benevolent Dictator For Life──仁慈的獨裁者,一種技術開發社群給該技術創始人的稱號)主導的程式語言不同,現代 JavaScript 的發展,是由一些 JavaScript 社群的意見領袖、及許多 Internet 重量級公司指派的代表所組成的 ECMA TC39 Committee,在廣納各方意見之後做出的決定。這避免了過度仰賴單一公司的問題,也減少技術創始者個人偏好造成的影響。

Arrow Function

Arrow function 是 JavaScript 語法上的擴充,將原本 function (x) {...}.bind(this) 簡化為 x => ... 型式。別小看這個改變,它使得 JavaScript 能充份發揮 functional programming 特點,也使程式的可讀性、維護性及程式員生產力大為提高,是我最欣賞的 ES6 特色之一。
早期 JavaScript 雖然陽春,卻從一開始就有了 first-class function,也就是函式在語言中是一等公民(first-class citizen),可以被當作一般物件傳遞、回傳、指派。在那個物件導向程式設計獨領風騷的年代,這不能不說是 Brendan Eich 的遠見。(他的另一遠見,是讓 JavaScript 一開始就具備 prototype 物件導向的特性,這兩件事加在一起,使得 JavaScript 成為一個深具潛力的語言。)以下是 Brendan Eich 的一段話 :
I’m not proud, but I’m happy that I chose Scheme-ish first-class functions and Self-ish (albeit singular) prototypes as the main ingredients.

Object-Oriented Programming

ES6 終於賦與了 JavaScript 物件導向程式設計(Object-Oriented Programming)的正式語法,包括 classextendsconstructor 等等。在這之前,JavaScript 雖然能寫物件導向程式設計,但其語法蹩腳、違反直覺又不統一,每個程式員有他偏好的寫法,嚴重影響軟體開發維護的成本。
例如以下是新的 ES6 物件導向繼承寫法(假設類別 Shape 已經定義過了):
class Rectangle extends Shape {
  constructor (id, x, y, width, height) {
    super(id, x, y);
    this.width  = width;
    this.height = height;
  }
}

class Circle extends Shape {
  constructor (id, x, y, radius) {
    super(id, x, y);
    this.radius = radius;
  }
}
以下則是 ES5 的繼承寫法:
var Rectangle = function (id, x, y, width, height) {
  Shape.call(this, id, x, y);
  this.width  = width;
  this.height = height;
};
Rectangle.prototype = Object.create(Shape.prototype);
Rectangle.prototype.constructor = Rectangle;

var Circle = function (id, x, y, radius) {
  Shape.call(this, id, x, y);
  this.radius = radius;
};
Circle.prototype = Object.create(Shape.prototype);
Circle.prototype.constructor = Circle;
嚴格來說,JavaScript ES6 的物件導向程式設計,骨子裡還是 prototype-based,與 C++、Java 這些語言的物件導向運作原理不同。但對大部份開發人員來說,可能察覺不到這種差異,因為 ES6 的物件導向語法與傳統語言長得很像。ES6 物件導向語法的設計,採用了所謂「maximally minimal」的原則,也就是儘量縮小這次改變的範圍。至於日後 ES7 是否會為 JavaScript 注入更多物件導向特性,還有待觀察,因為不少人持反對意見。
另外要注意的是,以上我雖然舉了類別繼承的例子,其實很多時候採用物件複合才是更恰當的做法,也就是所謂的「favor composition over inheritance」。關於這個問題,可以參考我前幾天翻譯的文章: 物件導向程式設計:為何說 composition 優於 inheritance?

Promises

JavaScript 是一種單一執行緒、以事件為基礎(event-based)的非同步程式語言。對於習慣用同步程式語言(synchronous programming language)寫程式的開發者,JavaScript 的單一執行緒似乎是件奇怪的事。但當你掌握訣竅之後,你會發現 event-based 的 JavaScript 處理各種非同步事件,其實比起傳統 synchronous multithread programming 更有效率。
不過這是有代價的。JavaScript 的 callback 對人類較不自然,比循序的程式難閱讀,callback 多層之後產生的「callback hell」,更是 JavaScript 開發人員的惡夢!維護這種程式既花力氣,又容易出錯。
Callback hell 大概長這樣:(實際上更難看,因為還有錯誤處理的程式碼,而且 callback function 可能定義在其它地方)
getData(function (a) {
  getMoreData(a, function (b) {
    getMoreData(b, function (c) {
      getMoreData(c, function (d) {
        getMoreData(d, function (e) {
          ...
        });
      });
    });
  });
});
生命會尋找出路。有人開始把 concurrent programming 裡的 promise 搬過來用,將層層 callback 轉為一連串簡單的循序呼叫,幫助 JavaScript 開發人員遠離 callback hell 惡夢。在 ES5 時期,就已經有很多第三方 Promise 程式庫了,ES6 則進一步把 Promise 納入標準,成為 JavaScript 的標準物件。

Generators

我第一次接觸 generator 這個觀念,是 2002 年左右在 Python 2.2 看到的,那時我已經用 Python 加 C++ extension 寫大型 Python 程式好幾年了(從 Python 1.5 開始)。對於習慣 synchronous programming 的我,第一次看到 generator 真的很陌生:函式執行到一半可以先返回,下一次被呼叫再接著往下執行。我到後來才慢慢搞清楚這件事。
ES6 的 generator 除了作為 iterator,另一個重要用途是把 yield 當作呼叫非同步函式的關鍵字。搭配像 co 這樣的程式庫,在 JavaScript 裡處理非同步呼叫(例如呼叫外部伺服器 API、存取資料庫、操作 cache layer 等等)變得異常簡單。Promise 已經大大簡化了非同步程式的開發,generator(加上 co)則進一步把它變成一行簡單的函式呼叫。在 ES7 的 async/await 到來之前,generator 是 ES6 送給 JavaScript 開發者的大禮,也是我最欣賞的另一個 ES6 特色。

其它 ES6 重要特性

以上 4 個是我認為最重大的改變。當然還有一些 ES6 特性也很重要,例如:
  • const/let:有了 const 與 let,JavaScript 開發者應該把 var 收到抽屜裡了。var 有許多奇怪特性,let 則文明得多。此外,能用 const 就用 const,你會發現程式碼可讀性大大提高。(別小看這件事,一旦你知道一個變數是 const,它在你讀程式過程的 cognitive load 就會輕很多,能幫助你大腦騰出更多空間裝其它東西。)
  • string interpolation:有了 string interpolation 語法,從此少掉一堆字串相加的算式,讓字串歸字串,程式歸程式。
  • module import/export:模組的定義和引用,終於成為 JavaScript 標準的一部份,從此不再需要用 require 作為替代方案。
  • 其它:for-of,property shorthand,array matching,object matching,Object.assign……等等。

二、關於 JavaScript 專案開發

對 JavaScript 語言的掌握

和其它語言相比,JavaScript 語言有一些較特殊的地方,例如:
  • var scope/hoisting
  • closure
  • this
  • strict mode
  • ==/===的差異
……等等。這裡面有許多細節(甚至地雷),JavaScript 開發者對這些應該要清楚掌握,以免不小心犯錯,造成程式 bug。

Node.js 與 npm

2009 年,Ryan Dahl 以 Google 的 V8 JavaScript 引擎為基礎,加上 event loop 及一套非同步 I/O 程式庫,創造出 Node.js。去掉對瀏覽器的依賴,Node.js 成為可以在 Linux 環境下獨立執行的 JavaScript 直譯器,解開封印,為 JavaScript 開啟了一個全新時代。(第一個 Windows 版本的 Node.js,則是 2011 年由 Joyent 與 Microsoft 共同完成。)Node.js 雖然基於 Google V8 JavaScript 引擎,卻有它自己的執行環境與程式庫(例如 EventEmitterStreamhttp.Server 等等),與瀏覽器端的 JavaScript 行為有諸多不同之處。
2011 年,Node.js 的套件管理員(package manager)npm 推出,為 Node.js 開發者提供極大方便,也使 JavaScript 套件的協作、共享變得非常容易。npm 是 JavaScript 專案開發不可或缺的工具,搭配簡單的 shell command,在不使用 Grunt、Gulp 等建置工具的情況下,npm 甚至足以處理許多專案的建置與部署需求。事實上,有不少人(包括我在內)認為用 npm 就可以了,例如這篇文章:Why I Left Gulp and Grunt for npm Scripts

ES6 轉譯

由於使用者端的瀏覽器並不一定支援 ES6,將 ES6 程式碼轉為瀏覽器能執行的 ES5,就成了部署 JavaScript 程式碼的標準程序之一。過去這種轉譯工具(transpiler)有很多,例如 6to5、Google traceur 等等。自從 6to5 改名為 Babel 之後,Babel 似乎就成為大家的標準工具了。
除了將 ES6 程式碼轉換為 ES5,Babel 還能處理更多的事,例如轉換 React JSX 語法、移除 Flow 型別註記、甚至支援 ES7 async/await 等等。

程式碼檢查工具

JavaScript 是一種動態程式語言(dynamic programming language)。對於習慣靜態程式語言(static programming language,例如 C/C++、Java)的開發者而言,動態程式語言一開始很方便。不用事先宣告變數型別,太棒了;不用 compile 就能執行,多好!也不會有 compile error 了。不過很快你就會發現,這只不過是把 bug 留到 runtime 再爆開來而已,bug 並沒有自動消失。相反的,runtime 才出現的 bug,代價比 compile-time 要高很多,很可能從 5 分鐘變 5 小時、甚至 5 天。
修復 bug 的成本,由小到大依序是:compile-time,testing,production runtime。對於沒有 compile-time 的動態程式語言來說,如何避免在 runtime 才發現 bug,就成為一個重大課題。(當然靜態程式語言也是,但它們通常在 compile-time 就能找出許多 bug,也有更多程式碼檢查工具可供選擇。)
在 JavaScript 開發社群中,使用 linter(一種靜態檢查程式碼錯誤的工具)的做法非常普遍。早期被普遍使用的 JavaScript linter,是 Douglas Crockford 寫的 JSLint。後來 JSHint 出現,變得很受歡迎。ESLint 則是 Nicholas Zakas 開發的另一種 JavaScript linter,也是我個人目前在用的。無論哪一種,專案團隊都應該採用 linter 檢查程式碼,並藉此統一 JavaScript 程式碼的 coding style。

型別宣告及檢查

缺少型別宣告(type declaration)及 compile-time 型別檢查,是動態程式語言的另一項弱點。在 JavaScript 開發社群中,解決這個問題的常見選擇有兩種:TypeScript 或 Flow。TypeScript 是微軟開發的 JavaScript 擴充語言,有方便的 IDE 環境,並且對 Windows 開發環境有很好的支援;但若你的專案原本沒有採用 TypeScript,想要漸進導入,可能會面臨一些挑戰。Flow 是 Facebook 開發的 JavaScript 型別檢查工具,採用與 TypeScript 類似的型別註記語法;因為 Flow 可以自動分析既有程式碼的型別,有助於專案漸進導入型別註記,但對 Windows 開發環境的支援度則較弱。Flow 比較類似「非侵入性」的解決方案,如果開發環境允許,我個人推薦 Flow。

單元測試

動態程式語言比起靜態程式語言更需要單元測試(unit test)。JavaScript 的單元測試框架有很多種,早期被廣泛使用的是 Jasmine 與 Mocha。近年則有許多人提倡回歸到更簡單、更模組化的單元測試框架(例如 這篇文章),包括 Tape 與 AVA 等;其中由 Tape 演變而來、加入平行執行能力(因此能大幅縮短單元測試執行時間)的 AVA,特別引起 JavaScript 開發社群的注意,短短一年多,已在 GitHub 上獲得大量關注。
26886865873_73cb530e6b_z
單元測試不是免費午餐。測試程式碼一樣要花人力撰寫維護,因此它的品質也很重要。根據我過去的工作經驗,測試程式碼的品質通常較差,這可能是因為很多人(尤其主管或 PM)只看到 test coverage 數字,這部份需要注意。

了解你用的每樣東西

由於 npm 及 GitHub 帶來的便利,世界上已經有超過 20 幾萬種 JavaScript 套件與各種工具。這雖然給 JavaScript 開發者更多選擇,卻也常導致 JavaScript 專案有過多的、不必要的外部依賴關係(external dependency)。開發團隊應該謹慎評估每一個要使用的套件與工具,確實了解它們的用途及必要性,而非盲目引入,以免造成日後專案維護的負擔。例如:你用了建置工具 Gulp,原始碼就有了 Gulp code,團隊成員要學習 Gulp;你用的每個 Gulp plugin,在日後開發環境版本更新時有可能壞掉,如果 plugin 作者不再維護更新、或更新速度較慢,你就得自己跳進去維護;等等。任何工具都有它的用途,能解決某些問題,但我們也要注意它的總體擁有成本(Total Cost of Ownership,TCO)。

結語

JavaScript 是普及率最高的程式語言之一,很多開發者對它都不陌生,這使得 JavaScript 進入門檻相對較低。另一方面,如果團隊採用 JavaScript 作為開發的主要語言(包括伺服器端),還可以統一前後端語言,在團隊專業經驗培養上具備更大優勢。
今天的 JavaScript 早已不是當年那個陽春的小玩意兒,而是有著豐富成熟語法、大量第三方程式庫支援,令人寫起來愉快的現代化語言。 許多知名公司 包括:LinkedIn、Netflix、New York Times、PayPal、Uber、Yahoo 等,早已使用 Node.js/JavaScript 開發產品多年。充份運用 JavaScript 語言的最新特性,輔以適當的工具與流程,JavaScript 將會是一個高效能、高生產力的開發利器。

限於篇幅,以上只是簡單介紹,希望這篇文章,能幫助大家對現代 JavaScript 語言有一個初步認識!

物件導向程式設計:為何說composition優於inheritance?

https://tw.twincl.com/programming/*662v


物件導向程式設計:為何說composition優於inheritance?

http://joostdevblog.blogspot.tw/2014/07/why-composition-is-often-better-than.html
學過物件導向程式設計的人,都知道繼承(inheritance)與複合(composition)這兩種觀念。前者是「is a」,後者則是「has a」的關係。近年來很多人提倡「favor composition over inheritance」,我也是支持者之一,但為何這樣,三言兩語又很難講清楚。我只能說,以前維護Java程式碼的時候,那一層又一層的繼承關係讓人腦袋發脹。

最近在整理一門JavaScript進階課程教材的過程中,剛好寫到JavaScript物件導向程式設計這個單元。我想起一年多前讀過的一篇好文:Why composition is often better than inheritance,作者Joost van Dongen是一位C++遊戲開發者,文章內容及舉的例子都很精彩,是那種有多年實戰經驗的人才寫得出來的東西。

我認為「composition over inheritance」這個觀念值得弄清楚,所以花了一些時間翻譯這篇文章,並與大家分享。


Photo credit: geraldford@flickr

以下是這篇文章的摘要翻譯。

在物件導向程式設計裡,要採用複合或繼承,是一個常見的問題。有時答案很明顯,例如「椅子有坐墊」屬於複合關係(has a),而「椅子是一種家具」則是繼承關係(is a)。但很多時候,該採用那種關係,答案並非顯而易見。例如遊戲中的某個角色,應該要「有碰撞外框」(has a collision box),或者應該「是一種可碰撞物件」(is a collidable object)?這兩種不同的做法,都可以用來處理碰撞問題,到底該用哪種好呢?根據我的經驗,若順著直覺,我們傾向採用繼承,但它可能造成許多問題;在很多情況下,複合其實是更好的選擇。

讓我們來看一個實際例子。在遊戲中,我們建立了一個類別,用來處理遊戲角色的物理行為,包括重力、反彈、滑行、跳躍等等。我們可以讓Character(角色)繼承PhysicsObject(物理物件),也可以讓Character有一個PhysicsObject物件。

簡化過的程式碼大概長這樣:(CharacterInheritance及CharacterComposition分別採用繼承及複合兩種做法)

class PhysicsObject
{
  Vector2D position;

  void updatePhysics();
  void applyKnockback(Vector2D force);
  Vector2D getPosition() const;
};

// Using PhysicsObject through inheritance
class CharacterInheritance: PhysicsObject
{
  void update()
  {
    updatePhysics();
  }
};

// Using PhysicsObject through composition
class CharacterComposition
{
  PhysicsObject physicsObject;

  void update()
  {
    physicsObject.updatePhysics();
  }

  void applyKnockback(Vector2D force)
  {
    physicsObject.applyKnockback(force);
  }

  void getPosition() const
  {
    return physicsObject.getPosition();
  }
};
如同你看到的,CharacterInheritance的程式碼要短得多,而且看起來更自然,不需要再另外寫applyKnockback、getPosition這類輔助方法。然而,在同時採用這兩種寫法多年之後,我學到的教訓是:複合的架構在這類情況下比起繼承更有彈性、不容易有bug,也更容易了解。

1. 彈性

讓我們從彈性(flexibility)開始講起。

假設我們想在遊戲中創造一種新的敵人角色,這種敵人由兩個物體組成,中間連著一條能量鏈,任何碰到能量鏈的人都會受到傷害。這兩個物體可以各自移動,增加打敗這種敵人的挑戰,因此使遊戲變得更吸引人。兩個物體都有各自的物理行為,碰撞其中一個物體並不影響另外一個物體,但它們實際上是同一個角色:同一組生命值,同一個AI(人工智慧),同時出現在一個小地圖(minimap)上。


Picture from Joost's Dev Blog

若我們採用複合架構,創造這種新敵人角色就變得很容易,因為我們只需讓新角色擁有幾個PhysicsObjects物件即可。若採用繼承架構,我們就沒辦法用單一角色做到,因為單一角色不能繼承PhysicsObjects兩次。我們可能會採取其它變通方式,但這很快就會使程式碼變得更複雜、更不直覺。

如果你認為這種情況不常發生,不值得擔心,那你可能還沒在一個有相當規模、「遊戲可玩性就是一切(gameplay is king)」的遊戲專案裡待過。遊戲設計者三不五時就會想出一些新的遊戲機制(game mechanics),是你目前程式架構無法處理的例外狀況。如果你說「不」,就會嚴重影響遊戲品質;畢竟最終,遊戲樂趣才是唯一的考量點(好吧,也許加上是否能在專案期限內達成)。

遊戲程式開發的一個重要目標,就是彈性:使你的程式碼具備彈性,不管遊戲設計者今天提出甚麼古怪想法,都能相對容易的加進遊戲裡。在絕大多數情況下,複合要比繼承有彈性得多。

2. 可讀性

我下一個反對繼承的論點,是可讀性(readibility)。可讀性總是伴隨著bug出現的頻率:可讀性越高的代碼,bug出現機會越低,因為如果程式員讀不懂程式,他就容易在修改代碼時把程式弄壞。

我們團隊的其中一條重要原則,是讓每個類別的代碼保持在500行內。雖然有時做不到,但它的目標很清楚:讓類別代碼相對短一些,更容易了解,以便程式員能把整個類別的運作機制裝進腦袋裡。

舉個例子,隨著很多新功能加進來,我們的代碼越長越大,Character和PhysicsObject都長到500行了。我們還加進了Pickup(撿拾)和Projectile(投射),兩者都用到PhysicsObject,也都長到500行。

                        +-----------------+
                        |  PhysicsObject  |
                        +-----------------+
                                 ^
           +---------------------|---------------------+
           |                     |                     |
  +-----------------+   +-----------------+   +-----------------+
  |    Character    |   |     Pickup      |   |   Projectile    |
  +-----------------+   +-----------------+   +-----------------+
在這種情況下,採用繼承架構的代碼,常常令人很困惑。即使我們已盡可能將更多成員保持在私有(private)狀態,最終還是會出現越來越多的保護(protected)及虛擬(virtual)成員。在以上例子,PhysicsObject已經和它的3個子類別Character、Pickup和Projectile糾纏在一起,難以分開。在我的經驗裡面,這件事情幾乎總是會發生:隨著時間過去,繼承的類別一起產生更複雜的行為,彼此糾纏得越來越緊密。

這本身還不是最大問題。現在當我們要了解其中一個類別,就必須了解它們全部:例如,當我們為了給Projectile加進一個新功能而改寫PhysicsObject,此時Character和Pickup也必須同時納入考量。要了解這整件事,程式員現在必須將4個類別、2000行程式碼都裝進他腦袋。以我個人經驗,這很快就變得不可能:程式碼已經多到沒辦法一次同時了解。結果就是代碼可讀性降低,程式員則更容易因為疏忽而造成bug出現。

當然採用複合的架構,並不會使所有問題自動神奇消失,但它確實有助於讓程式碼保持在簡單與可讀的狀態。採用複合架構,就沒有保護(protected)及虛擬(virtual)成員,所以PhysicsObject在一邊,Character、Pickup和Projectile在另一邊,它們之間的界線很清楚。這避免了類別之間隨著時間纏繞在一起,使它們更容易真正保持分開。理論上,採用繼承也能保持類別分開,但根據我的經驗,這在現實上很難維持,而複合則強制你做到這一點。在我們的代碼裡有數不清的例子,採用繼承架構的類別程式碼長得太大,大到再也無法輕易理解。

菱形問題

任何有關繼承與複合的討論,如果不提菱形問題(diamond problem)就談不上完整。例如,類別A繼承B和C,而這二者同時又繼承D,會發生甚麼事?A會繼承D兩次,問題也因此產生。

           +-------+
           |   D   |
           +-------+
               ^
       +-------|-------+
       |               |
   +-------+       +-------+
   |   B   |       |   C   |
   +-------+       +-------+
       ^               ^
       +-------|-------+
               |
           +-------+
           |   A   |
           +-------+
在C++裡,菱形問題的解法有幾種。例如你可以直接接受A有兩個D,或者你也可以用虛擬繼承(virtual inheritance)。但這兩種解法都會造成一堆問題與潛在bug,所以最好是一開始就避免菱形問題。

在遊戲程式的初始架構裡,這種問題通常並不存在,但隨著新功能加入,它可能會偶爾開始出現。問題在於,當它一旦出現,通常很難有好的解法,除非做大量的程式碼重構(refactor)。

不過在我12年的物件導向程式設計經驗裡,菱形問題只出現過幾次,所以它也許不是反對繼承的強有力論點。

(順便一提,即使沒有出現菱形問題,多重繼承的架構也容易變得很骯髒,這在我之前寫的「Hardcore C++: why "this" sometimes doesn't equal "this"」文章中有探討。)

繼承可以用,但少用

這些論點,代表我整體上反對使用繼承嗎?不,絕對不是!繼承對一些事情很有用,例如多型(polymorphism),或者設計模式裡的偵聽者(listner)、工廠(factory)模式等。我的重點是,繼承不像它表面上看起來那麼好用。在多數情況下,繼承也許看似最乾淨的解法,但實務上,複合很可能才是更好的選擇。

Hadoop 不是萬能,破除七大迷思讓你做好 Big Data/Cloud Analysis

https://tw.twincl.com/programming/*662v
https://buzzorange.com/techorange/2013/06/04/7-myths-on-big-data-avoiding-bad-hadoop-and-cloud-analytics-decisions/
Hadoop 是 Apache 軟體基金會(Apache Software Foundation)因應 Cloud Computing 與 Big Data 發展所開發出來的技術,Hadoop 使用 Java 撰寫,並免費開放原始碼,在此背景之下,就足以讓 Hadoop 在資訊技術發展史上占有一席之地。

像 Yahoo 就利用 Hadoop Framework 的技術,建立網頁索引資料庫的計算,同時使用 1 萬個 Linux 平台的處理器核心,處理 1 兆個網頁連結,從 4PB 的資料,運算得出 300TB 的索引數據。

然而,盛名之下必有所累 -- 關於 Hadoop 的應用迷思也隨之而起。而這些迷思可能會導致企業的資訊部門決策者一廂情願/過於樂觀地制定相關決策。

資料與資料的使用量正以驚人的速度持續增長中,IDC(The International Data Corporation)在 2013 年 1 月所釋出的分析報告就指出 -- 今年在資料儲存部份的增長速度將達到 53.4%,而 AT&T 也指出 -- 無線資料的流量在過去的 5 年內增加了 200 倍。

在 IDC 針對 2012-2016 年亞太區(不含日本)Big Data 市場所做的分析與預測報告:「APEJ Big Data Technology and Services 2012-2016 Forecast and Analysis」指出,亞太區的 Big Data 市場規模預估將從 2012 年的 3 億美金成長到 2016 年的 17.6 億美元,年複合成長率達 47%;而台灣的市場規模則從 2012 年的 1.13 千萬美元成長到 2016 年的 4.61 千萬美元,年複合成長率亦達到 40%。

所以,如果你也重視自己所擁有的對外互動管道的話,你會發現來自網路的內容、電子郵件、App 訊息、社交訊息等數量正顯著地增加中,這也是為什麼 從 McKinsey 到 Facebook 到 Walmart 等企業都將 Big Data 的應用視為營運的重點之一。

但是,Big Data 會不會如同 90 年代的網路泡沫化與 2000 年的 Web 2.0 泡沫化一樣,讓一些決策者做出斲傷企業的假設與決策?

Hadoop 目前無疑是企業們用來因應 Big Data 需求的主要投資項目之一,像 Facebook、Yahoo 這些大企業都宣稱自己在應用 Hadoop 來處理 Big Data 分析上有相當良好的成效,這將使得之後面臨 Big Data 問題的企業會起而效尤。

然而,這會不會造成 Functional Fixedness(是指有時候人們會受制於過去的經驗,自覺/不自覺地認定某個事物只具備某種特定功能用途,而習以為常地只朝特定方向去思索的認知結構)的認知偏差呢?

接著,就讓我們跟隨在雲端服務領域已有多年經驗的 Adam Bloom,帶領我們從幾個不同面向來看 Hadoop / Big Data 的應用。

Big Data 僅僅只是數據量爆增( Volume)的問題而已?
到目前為止,對於 Big Data 尚未有統一的定義,因此,除了數據資料量的增加之外,你還會經常聽到一些科技領導廠商與研究機構所提出的幾個關於 Big Data 的特性,像是像是 Variety(異質資料多樣性,指的是結構性、半結構性與非結構性資料,包含文字、影音、網頁、串流等)、Velocity(即時性)和 Value(價值)等。

Big Data 的特性並不只是數量上的成長 —— 事實上,它正朝向用更好的方法,來達成在最佳時間內分析整合來自企業內部分散各地的結構性(Structured)/非結構性(Unstructured sources)資料,以探勘出隱藏的價值,提供給企業決策者做決策的參考依據。

處理結構化資料的 SQL 無法在 Hadoop 上使用?
當 Facebook、Twitter、Yahoo 等科技龍頭企業,都選擇 Hadoop 來處理自家內部大量資料的分析的同時,表示他們瞭解 Hadoop 主要是用來處理非結構化資料,因此,HDFS 和 MapReduce 在處理結構化的資料上就必須選擇像 Hive、Pig 與 Sqoop 等為工具。

由於 SQL 發展的歷史已經超過 25 年了,這表示現在的許多資料是透過 SQL 來做管理的,Hadoop 的掘起雖然是時勢所驅,但,這並不表示我們已經不再需要 SQL,更何況這兩者的功能是互補的,因此,有些企業開始致力於 Hadoop 與 SQL 的相容性開發,以使其能發揮綜效。

例如,EMC Greenplum 所發佈的 Pivotal HD 的元件 HAWQ 就是一個很棒的例子 ── 它是一個與 SQL 相容的平行查詢引擎,速度比 Hadoop 查詢引擎要快上 10 – 100 倍之多,而且能夠處理 PB 規模的資料量。

Hadoop 將取代大型主機(Mainframe),成為唯一的 IT 資訊平台?
實務中,企業裡的 IT 產品組合中,有很多是需要長期投資的項目,像用來做伺服器的大型主機就應該跟隨著企業系統(如,ERP、CRM、SCM 等)的演進而做適當的調整。

資訊決策者若不想汰換大型主機,或還無法就現有的資訊架構做調整,就必須賦予它新的定位與存在的價值。

像我們公司有不少客戶在大型主機的運作上都遇到了速度、規模與成本的問題,但還是有方法可以解決這些問題的,例如,VMware 的雲端計算應用平台 vFabric SQLFire,就是利用虛擬化技術來處理大量資料,並藉由分散式快取技術節省磁碟存取次數,進而達到降低延遲時間與分散系統負荷的目的。

虛擬化技術對效能提升有幫助嗎?
Hadoop 最初的設計只是在實體伺服器上運行,然而隨著行動商務與網路商機的發展,為了能更方便分享及取用資料,企業紛紛把資料從大型主機轉移到 Web 服務供應商的資料中心(Data Center);而服務供應商為了提供更穩定與更迅速的服務,也需要一個新的服務架構,把運算資源與儲存空間做更有效率的利用。

隨著雲端服務的興起,許多企業開始希望 Hadoop 能把上述的需求整合在一起,以延伸傳統資料中心的功能。而基於成本與效能的考量,虛擬化技術也開始受到重視。

為什麼企業需要虛擬化技術?

我們首先考慮到的就是:彈性。你可以想像,如果我們能夠動態地調整計算資源,就能夠將大量且複雜的計算程序自動拆解成多個較小的子程序,再由多部伺服器所組成的叢集進行分散和平行計算分析,將處理結果回傳給雲端使用者。

也就是虛擬化技術能讓電腦分工合作,讓系統的運作與使用者利用資訊的方式更有效率。

資訊架構也可以透過租賃方式取得?
2012 年, 被 VMware(虛擬化技術應用大廠之一,同時也是雲端計算發展的領導企業之一)收購的 Cetas,是一家提供軟體即服務(Software as a Service, SaaS)基礎架構的雲端服務廠商。

Cetas 所提供的雲端服務,讓他的企業用戶不需要花大量的時間與費用來建置專屬的資料中心(Data Center),透過其所提供的服務就可以在雲端上運行 Hadoop、SQL 與資料的即時分析(Real-Time Analytics),取得自己所需要的設備與技術服務,而不必再自己從無到有評估軟體授權、硬體與軟體建置/安裝/升級等。

對於建立公有雲(Public Cloud,通常與「租賃」有關,也就是建立雲端資料中心後,不只是提供自己使用,還可以把多餘的運算資源分租給別人,也就是透過收取費用把雲端資料中心的服務與資源「分享」給其他對象)的企業來說,使用 Java 的開發人員,或許可以從 VMware 針對 Apache Hadoop 所推出的 Spring Data 產品,以及從 GitHub 所提供的線上版本控制服務所提供的免費/付費資源中獲益。

虛擬化技術只會提高企業的成本?
Hadoop 通常在商用伺服器上運行,因此有人會認為新增虛擬層(Virtualization Layer)除了會帶來額外的成本支出之外,能不能獲得對等的價值回饋還是一個未知數。然而,這個觀點其實存在缺失 -- 實務中,資料和資料分析都是進行動態處理的,而動態處理的機制要做到好還要更好,所耗費的資源是非常可觀的。

如果你希望藉由 Hadoop 這個工具來平衡企業內部因應 Big Data 所帶來的問題,舉凡資訊架構的調整,增進系統間互動的效率,資料探勘所帶來的創新價值等,你就必須瞭解企業內部的資料是存放在不同的位置與系統中的,以及為了進行分析這些分散的資料所必須花費的時間與速度等。

導入虛擬化的資訊基礎架構可以減少你不斷採購硬體所占用的土地面積,而且,它會讓企業的 CAPEX(Capital Expenditures,資本支出)和採購非虛擬的設備相差不多,但,透過虛擬化技術,卻能讓使用者享受到比傳統資料傳輸方式更為快速、簡單且更優惠的好處,進而降低企業的 OPEX(Operating Expense,運營成本)。

Hadoop 無法在 SAN(Storage Attachment Network)或 NAS(Network Attached Storage)架構中運行?
Hadoop 通常都在區域性的磁碟上運行,但中小型的叢集在共用的 SAN 環境裡,運行的效能也不錯,但這視叢集的成本和效能而有不同。

另外,高頻寬的網路,像 10 GB 的乙太網路、 FoE(Power over Ethernet,指透過網路線來獲取電力)、iSCS I(Internet SCSI,將傳統上只能用於本機傳輸的 SCSI 協定,可以透過 TCP/IP 網路承載,以達到資料高速傳輸的需求)等都有益於效能的提升。

備註:(1) SAN,是一種連接外接儲存設備和伺服器的架構,特點是,連接到伺服器的儲存設備,將被作業系統視為直接連接的設備;(2) NAS,是將儲存設備透過一台用來進行資料存取的檔案伺服器直接連到區域網路上,因此,企業內其他的伺服器就不須同時兼負資料存取的動作,藉以優化效能。

本文主要的目的在於,藉由以上所提及的幾項思考,能協助對 Big Data 應用有興趣你,用更客觀的角度來看待相關的問題。Hadoop 只是一個用來協助處理大量數據分析的工具之一,它並不是市場中唯一的選擇,你應該依據實際的需求與環境來採購適合企業的應用工具,所以,如果你面臨的是處理大量數據的時效性(Velocity)問題,那麼 GemFire 與 SQLFire 或許是最好的選擇。

而像 RabbitMQ 這類訊息導向(Message-Oriented)的中介軟體(Middleware),可以利用來在不同的程式語言、平台、與電腦之間交換訊息。

當然,如果你期望將企業的產品更快地推向市場,可能就意味著你需要一個提供 SaaS 基礎建設的雲端服務供應商,而 CETAS、Redis 更能滿足你的需求。