顯示具有 architecture 標籤的文章。 顯示所有文章
顯示具有 architecture 標籤的文章。 顯示所有文章

2023年11月12日 星期日

Chisel 學習筆記 - Scala 與 Chisel 基礎語法

標題為筆記, 但這篇比較屬於心得

延續上一篇的環境建立, 這次計劃藉由 Jserv 最新的課程安排來學習 Chisel, 當然個人目標是能夠按照 Jserv 的課程規劃在期限之內完成 Lab 3, 由於個人並非 digital designer (現在這年紀也算老貓學新把戲...), 因此對於 chisel 的一切也是從基本開始, 目前計劃於一週內按照 Chisel Bootcamp 的章節完成 Chap 0 ~ 3 的所有內容. (Chap 4 部份在後續會依照進階需求選擇回來看)

週末花了點時間把 Chap 0 ~ 2 的說明研讀完, code block 的操作也跑過一次流程, 另外是在 try and error 下完成當中的所有 exercise & test. 個人覺得 Chisel Bootcamp 多少是面向俱備 Verilog / HDL 有些許基礎的人. 儘管無基礎並不會造成學習的困難, 但多少難以體會 Chisel 相較之下的優點與強項.

基本上Chap 0 ~ 2 的重點其實並不多

  • Chisel 的特性
  • Scala 的基本語法
  • 建構於 Scala 之上的 Chisel DSL
  • 電路的基本: module, combinational, control & sequential logic
  • 除錯與測試方式

由於並非第一次學習建構在另一語言的 Domain Specific Language. 過往學習 Halide 的過程就是這樣的一個經驗. 然而與先前 Halide 不一樣的地方是 Halide 所依附的 C++ 個人有所基礎, 但是對於 Scala / Chisel 這樣的組合卻是完全從頭學起. 而這大概有幾個面向是需要反覆思考與刻意練習的.

第一個主要的點在於 Value 與 Typing System, 類似於 C++ 與 Halide 有著各自的 value & typing system, C++ 主要用在撰寫與控成 Halide code 的流程, Halide 體系著重在產生 target code 上. Scala 與 Chisel 也有各自的 value & typing system. Scala 也是用在撰寫 Chisel 主體與流程上, Chisel 是用以定義 hardware 使用到的 module 與產生 instance. Chap 1 ~ 2.1 強調分清楚 Scala 與 Chisel 的 value & typing system 確實是很重要的一件事.

第二個是 Chisel 的實作撰寫必需了解的 3 個階段 - circuit generation, circuit simulation 與 testing. (Halide 也有著類似的階段, 甚至都使用 Generator 這樣的名稱) Scala 與 Chisel 在使用上容易搞混的是 generation, Scala 是讓 code generation 能透過參數化來產生對應不同的 circuit (按照教學說法, 這是 Chisel 強大的地方). 因此這些都是在 compile time 決定的, 而 Chisel 的撰寫則是決定了 circuit generation 的結果. 對於一些 if / else 的思考必須區隔希望影響的是 compile time 的行為抑或者是 runtime 時的所需. (習題上來說, 觀察前後輸入的 data type 就可以快速反應)

儘管這些並不是什麼艱深難懂的心得, 然而困難的點是在熟悉與快速應用 Scala / Chisel. 重要的還是需要刻意練習來讓思維方式有著正確的對應. 若儘只是研讀一番並不足以深刻體會, 或是習得實務上的撰寫能力. Chisel Bootcamp 的作業與測試非常值得一一花時間下去練習. 藉由這些能夠一次次找到認知錯誤的地方或是盲點. 個人認為能嘗試完成 2.2 Arbiter, 2.3 Finite State Machine 與 2.5 FIR Filter Generator 這三個 exercise 應符合了基本要求. ( 2.2 中提供的 Cheatsheet 第一頁對於閱讀內容與作業相當有幫助)


2020年6月23日 星期二

Halide 與處理器架構上匹配性問題心得

老生常談, 老狗學不會新把戲 - 今晚來談談 Halide 與架構的匹配性

這幾年間 Halide 是個人常用的工具之一
上週與朋友一同用餐, 言談間討論到了 Halide
其中多少談論到一些處理器比較的部份
這文章分享的並非什麼新東西或是驚人的發現
基本上於不同地方的私下討論個人都早已分享過
在此僅是反覆說明自己對一件事情的看法 - 為何 Halide 套用在不同計算架構平台上效益落差很大

這個問題在不同場合, 不同的對象一同討論過(甚至當時在 MTK 工作時分析架構於內部都提及過)
個人的觀點是 - 不同平台上 locality 這部份的影響 (另外的 redundant work / parallelism 多數架構上這都不構成問題)
一言以蔽之 - memory model / architecture
必須說 locality 是 Halide 對於計算考量核心精神中 scheduling 三角的一角, 對於 Halide 而言而能凸顯這面向的還是以 transparent cache 為主, 任何需要透過搬動 (無論是額外的 SIMD, 俱備 DMA 與否) 來 reuse data 的 scratchpad / TCM ... 等等, 非 cache based 的方案都面臨幾個問題:
  • reuse data 的額外開銷, 而且這部份隨著要使用的資料愈多 cost 也愈高 (house keeping code, 2D/3D DMA usage ... etc)
  • cascade or fused kernel 每增加一級, data layout 的安排複雜度(無論對 compiler or coder 而言)呈指數增加
  • 再者一旦決定了 scheduling, 基本上也不俱備 scalability. 增加 size 無法改善效率, 但是減少 size 會讓原有的 code 無法運行. (cache-based 架構則是會有隨著 size 不同而呈現的效能曲線)
這幾個因素讓 Halide 在一些架構上, 在這個 Halide 最重要概念的三角形中的 Y 軸 (也就是 locality) 僅僅存在著數量非常有限的選擇, 最終這個 scheduling space 的三角形因為 control & complexity 而退化為小面積, 線或更甚者為數點. 而最終還是會落入需要經驗導向的 flow 調整, 別出心裁的發想, 特殊指令的套用 .. 等等
 

 

2018年5月28日 星期一

對於 Image Processing 未來的一些猜想

5月底了, 去年 Blog 重新開張後希望每月至少能維持一篇
今日來談談 NN 在 ISP 功能上個人粗淺的認識以及應用的可能

個人在剛投入影像處理對於 ISP 懵懵懂懂的時候看到了 FlexISP 這篇 paper
要了解其論述必須先了解在晶片中處理照相功能的 ISP 為一個 multiple-stages 的硬體

也就是每張拍出來得到的照片, 在其源頭都是特定的 CFA (Color Filter Array) 序列, 再經過上述一連串的步驟所產生出來, 而常用的 CFA 格式為 Bayer Format.

在 FlexISP 中最重要的觀念是相當基本的: 每一級的處理程序在過程中都會產生數值偏差

因此 FlexISP 的核心思想是去建構自 Source 到 Bayer 間最後結果每一階段的數理 Model, 合併後透過線性條件限制的方式取得反向計算的解, 即可將 ISP 中多級的計算, 一次性地套用在輸入的影像上, 產生較少誤差的結果:

而甚至在 Google HDR+ 的論文中亦有與其的比較(而也算有趣的是, 因為 Google HDR+ 演算與硬體架構不搭, 所以後處理上實作了 Software ISP,  這點可以閱讀其論文內容), 儘管 FlexISP 的計算量驚人, 然而結果也的確相當出色 (請注意第二行衣服的細節, FlexISP 是與 Ground Truth 最接近的):

FlexISP 證明了一件事情 - 傳統的多級 ISP 處理方式依然有著相當大的進步空間, 而這空間即是傳統 ISP 多級的處理方式自己所產生的, 因而原則上處理的步驟要儘可能地減少.

在近日個人又注意到 CVPR 2018 的一篇論文 - Learning to See In the Dark
其使用了 Convolution Network 來作為 ISP 的角色, 並且在極端的暗景下與 DSLR 比較各自的輸出結果, 其運作的方式如下:


而 RAW, DSLR 輸出與其結果比較如下:
結果相當吸引人, 而這只是論文中提供的一組, 另外建議去提供的聯結點看 "Additional material" 來看更多的比較圖.

在將 FlexISP 與上圖的比較圖相較後, 應能夠將該 ConvNet Model 視為一暗景所特製的 ISP, 而其數理意義上該 Model 做的事等同於去近似在 FlexISP 中欲以各級數理模型疊加影響所反推的函數.

儘管目前有著相當多的 Deep Learning Accelerator (DLA), 然而或許該探究的是, 這樣的 Network 是否可能真的硬體化成為 NN-based ISP?
在 Learning to see in the dark 該論文中, 其預設使用的 NN  為 U-Net , 下圖為 U-Net 的架構:

U-Net 官方的 model size 為 89MB, 其所使用的 Convolution 最大為 3x3, 而對於 tile-based 來說, 應需要 3 條 line buffer (ring buffer), 其他會需要的是擺放各階段的 kernel, 若以 16b 計, 以上述的最 critical 的 stage 需要 1024*3x3*1024 * 2 = 18 MB, 因此事實上不做一些特別處理還是不容易硬體化.

然而若延遲性許可,  frame-based 的處理, 那麼一般 DLA 即可套用在這樣的應用情境上(畢竟使用者為了極端的情況得到較佳的照片是願意等的), 當然這僅只是以 U-Net 作舉例, 設計上還能搭配不同場景適用的 Model 或是 network, 甚至 cascade/parallel network 來達到不同的目的, 而或許當這些針對 ISP 功能的所設計的 NN 有著顯著的優勢(其實 ISP 的 tuning 很花時間), 儘管以 Network 方式實作 ISP 特定功能的論文近年不少(eg: DeepISP , 這並不是創新的想法, 而且愈來愈多), 然而單純以單一 network/model 來取代, 而缺乏系統化與參數調整的考量都不會是 ISP 實務上會考量的架構, 然而手機/監視應用/汽車產業在急遽追求影像成像品質的今日, 基於 NN 的處理方法或許是個不可避免必然的趨勢, 然而這仰賴 ISP 相關工作者與 ML/DL 工作者高度的合作, 那麼在 ISP 中內嵌 DLA 或許是未來可以預見的事?

2017年9月4日 星期一

Kirin 970 NPU - 看圖說故事

從 Image Sensors World 看到了華為旗艦手機晶片 Kirin 970 中 NPU 的投影片
其中有趣的是第五張圖


"25X performance, 50X efficiency"
相較於 CPU 有著 25倍性能 與 50倍的效率
由於 Performance 有相當多比較的面向, 因此不清楚是用絕對的指標(total GFLOPs, or per core) 或是相對指標 (OPs/cycle/core), 甚至是實際應用的時間來做比對(eg. FPS)
但是若是 Efficiency 談的比較可能的是 Performance Per Watt (另外還有 area efficiency, 甚至一起綜合看)
Efficiency 比較上為了有較好的數字, 比較可能的是與大核等級的 CPU 相比, 因此這裡以 Kirin 970 中的 Cortex-A73 為對比
以 NEON 的 GFLOPS 來看2.4Ghz Cortex-A73 單核的 GFLOPs 為:
2.4Ghz * 4 lane * 2 mad * 2 unit = 38.4 GFLOPS 
(寫到這就知道 Performance 不是以絕對 GFLOPS 計算)
而若 TSMC 10nm 製程能將 2.4Ghz Cortex-A7x 最大功耗(Peak Power)壓低至 1 Watt/Core(這是個人經驗粗略推估數字), 那麼 50X 效率可以推得:
38.4 x 50 = 1920 GFLOPs/Watt =  1.92 TFlops/Watt
(寫到這覺得蠻剛好的)
又可以從投影片上得知華為對外宣稱其 Kirin 970 內的 NPU 有著高達 1.92 TFLOPS/s (FP16) computation throughput




那麼可以由上面的比較, 對於 NPU 的最大功耗能推估為 1 Watt
以此看推估觀察運作的功耗圖介於 0.3~0.7 Watt 看來也是合理的(畢竟應該有著 Thermal/Power 管理面向的 DVFS)


2017年5月12日 星期五

Android 軟體架構轉變的進行式

今日在 Google 的 Android Developer Blog 上貼出了篇名為的 "Here comes Treble: A modular base for Android" 貼文, 這是一件對於 Android 生態系統的大事, 也是 Google 方面嘗試來解決 Android 的碎片化與安全性上的問題.

這件事必須先從 Android 裝置的軟體是經過什麼樣的流程抵達每個使用者手上, 在這篇文章中即是一開始的五步驟圖:
  1. Android 團隊向世界發佈最新版本的原始程式碼
  2. 晶片製造公司為了讓他們各自的晶片在 Android 裝置上俱備更多優勢, 針對他們特定的硬體, 修改了來自 1. 的原始碼與增添驅動程式.
  3. 這些晶片製造商接著將這些修改提供給他們的客戶 - 也就是那些設計與生產 Android 裝置的品牌商. 這時裝置生產商再次針對他們的產品對 1. 的軟體做了修改.
  4. 裝置生產商與電信網路商測試與驗證新的版本.
  5. 裝置生產商與電信網路商提供新的版本給他們的使用者

 在以往 Android 裝置間是透過相容性文件 Compatibility Definition Document (CDD) 所規範的介面以及現在已經涵蓋上百萬對應測項的相容性測試套件 Compatibility Test Suite (CTS) 來避免軟體相容性上的問題. 然而面對來自上述 2. ~ 5. 項目的修改與限制, 造成了 Android 軟體層上碎片化不容易解決, 1. ~ 3. 表示裝置生產商會 follow 的不是 1. 的官方實作而是 2. 的 chip vendor 的 Android Framework 的修改, 此外 4. 與 5. 反映了 Android 裝置的安全性修正完全取決於裝置生產商自己的更新意願(特別是裝置生產商不願意透入成本在維護已發售許久的裝置).

為此 Google 在 Android O 開始要著手解決這樣的問題, 提出了 Project Treble, 其目的在於強化對於 Android 系統軟體這塊的控制與掌握, 這必須談到在以往即已經存在, 但是界線並沒有明確規範的 Vendor Implementation (廠商實作層), 現在 Google 將明確的對 Android Framework 與 Vendor Implementation 切出這一條線:
因此先前所採用的 CDD + CTS 方式, 將轉為 Vendor Interface + Vendor Test Suite(VTS), 這件事最大的意義在於 Chip Vendor(晶片商) (或是很有可能以後包含 Device Vendor(裝置生產商) )將不再能夠直接修改上述五步驟中 1. 裡面的 Android Framework 的原始碼, 而優化與性能提升的方式也被限縮在圖中灰黑色(這顏色是不是特別選過阿...) 的 Vendor Implementation 這一層, 介面也是所定義規範出的 Vendor Interface.
藉由明確規範 Vendor Interface 的好處如上圖, 也就是 Android OS framework 的更新將獨立於 Vendor Implementation, 而在該篇文章是這麼說的 "device makers can choose to deliver a new Android release to consumers by just updating the Android OS framework without any additional work required from the silicon manufacturers", 翻成中文就是裝置生產商能夠無需藉助於晶片商額外的處理下, 就能夠自行更新 Android OS framework 的部分(這或多或少都是裝置商對於更新推托的理由與藉口). 是的, 目前 Project Treble 並沒有強勢到一次將 Android Framework 客製的權力全部收回, 這或多或少是出於市場考量, 但是至少讓 Google 頭疼的源頭 - Chip Vendor 的客製已然受限, 當然這樣作法背後還是在於 Google 對於 Chip Vendor 的客製只能道德勸說並無其他的約束能力, 而 Chip Vendor 還是會嘗試對客戶提供他們自己客製的 Android Framework, 一方面這樣介面的建立會讓裝置系統商意識到不正確與不當的客製修改, 另一方面來說對於裝置生產商來說來自 Google 相關的軟體生態系(Google Play)的授權是相當重要的, 因此 Google 還是有一定其他要求與控制能力. 一旦架構建立了 Treble Architecture, Google 即更能順利的要求裝置廠商去提供裝置的軟體更新(而以往的理由會凸顯背後的問題, 讓 Google 更能第一時間掌握有問題的平台).

最後 Google 還在文末說道目前 Pixel 上運作的 Developer Preview of O 已經採用了這樣的架構方式.

2017年5月11日 星期四

有趣的 cache line 效應

在忙著處理一些個人事務的時候, Jim Huang 請我幫忙回覆學弟妹碰到的 一些問題
主要是 Jim Huang 修改了之前 Matrix Transpose 的例子作為範例給學弟妹們練功使用
而學弟妹碰到了一個問題, 就是對於 Matrix Transpose 的寬做調整時
執行時間與長寬度的調整的分佈圖如下:
如上圖所示, 隨著對寬作 4096 + (i*64)的調整時經分佈呈現鋸齒狀的變化!
依照文中敘述的實驗平台為俱備 6MB cache 的 Core-i5 6300HQ
以 Intel Skylake 架構而言, 為 64 bytes cache line, 8-way associative cache
基本上俱備 6MB/64/8 = 12288 sets (entries)
以這個 int matrix 而言, 原始的寬度為 4096x4=16384 bytes
16384/64 = 256
因此每增加一行記憶體基本上會跳躍 256 cache line index

接著再觀察 naive transpose 的實作:
https://github.com/yangyang95/prefetcher/blob/master/impl/naive_transpose.c
若 buffer 對應的 cache line index offset 為 X

首先考慮最簡單的 i == 0 的情況
對於第 n 行的 cache line index 計算為: (X + 256*n) % 12288
1. 12288 / 256 = 48 (自 cache line 0 ~ 12288, 每次跳 256 可以消耗 48 行)
2. 12288 % 256 = 0 (每次的 cache line index shift)
所以填完 output 第一行來說, 這也代表著當資料(二維座標以(x,y)表示)自 (0, 0), (0, 1) .... (0, 4095) 要讀 (1, 0) 時
而相關的 cache line 早已被反覆地填入了 4096/48 = 85.33次 > 8
當 CPU 需要讀入 (1, 0) 時, 因為讀入 (0, 0) 所讀入的 cache line 有相當高的機會早已被 replace

接著當每次寬度增加 64 時(也就是 256 byte, 4 cache lines)
這裡我們先考慮 i == 1 的情況
對於第 n 行的 cache line index 計算為: (X + 260*n) % 12288
1. 12288 / 260 = 47.26 ...
2. 12288 % 260 = 68
260 與 68 兩者最小公倍數為 4420
4420/68 = 65  (表示反覆繞 cache line index 0 ~ 12288 到第 65 次才開始 cache line index 有所重疊)
而 4160/47.25 約為 88.02, 88.02 / 65 = 1.35 < 8
這表示在讀取 (1, 0) 時, 有很大的機會 (0, 0) 所填入的 cache line 還在

再考慮 i == 8 的情況
對於第 n 行的 cache line index 計算為: (X + 288*n) % 12288
1. 12288 / 288 = 42.6666....
2. 12288 % 288 = 192
而 288 與 192 兩者最小公倍數為 576
576/192 = 3 (表示反覆繞填 cache line index 0 ~ 12288 到第 3 次 cache line index 就有所重疊)
而 4608/42.666 約為 108, 108 / 3 = 36 > 8
這表示在讀取 (1, 0) 時, (0, 0) 所填入的 cache line 有相當高的機會早已被 replace

由類似的計算可以得知, 造成這樣的時間分佈的原因, 而 SSE 版本的成因也是基於相同道理
而這樣的效應更顯示程式的記憶體使用方式, data layout 與 cache 影響程式的效能甚巨

2017年2月10日 星期五

OpenCL Programming for Intel FPGA 初探 - II

在進入針對 OpenCL for FPGA 的優化過程之前
人們必須先了解 FPGA 是如何的裝置, 與 CPU 與 GPU 在本質上的不同之處

Architecture Overview

若以低階的 Altera Cyclone V 來看的, 我們可以從其 Handbook Volume 1 看到其架構組成
由上圖可以看出 FPGA 的主體主要由三種 block 組成
  • CoreLogic Fabric and MLABs : 主要為邏輯與簡易計算的 blocks, MLAB (memory logic array block) 基本上是通用的 SRAM array, 亦可以被設定作為 RAM/ROM 的用途
  • M10K internal Memory Blocks : dual port 的記憶體 blocks, 可以存放 processor code, 用以實作查表設計, 與實作較大的記憶體應用. (像是 OpenCL 中的 local_memory)
  • Variable-Precision DSP Blocks : 可變精準度的 DSP blocks, 透過使用數量不等的區塊可以串聯出整數/浮點數精準度不同的乘法與運算單元.

與 CPU 不同的是, FPGA 的性能主要取決於上述三種 resource 的數量, 另外應用上使用的 resource 若無法壓低, 可能無法塞入 FPGA 上執行, 這是要注意的事情, 所以應用的選擇上必須注意規格, 下圖即為 Cyclone V SE SoC 內 FPGA resource 的表格:
從上途中可以觀察出一些數字是彼此相關的, 像是 18x18 multiplier 的數目與 DSP blocks 是直接成倍數關係的, M10K memory 大小 與 M10 memory blocks 數目也是如此, 這兩個部分分別對應了三種區塊的 M10K memory 與 DSP blocks, 然而第一項的 CoreLogic Fabric 呢?

Logic Elements

這部分需要從 LEs 這個數字解釋起, LE為 Logic Elements 的縮寫, 從 Altera 的解釋, LE 為其 邏輯部分的最小單位, 提供了下列功能:
  •  4 組輸入的 lookup-table(LUT) 功能, 可以實作任何 4 變數的函數
  • 可程式的 registers
  • carry chain connection
  • register chain connection
  • 多種功能的 interconnection
  • register packing, feedback
而上述的部分必須又必須從 Adaptive Logic Module(ALM) 來解釋, 下圖為 ALM 的 block diagram:

對於 ALM 官方文件是這麼描述的 "The ALM uses an 8-input fracturable look-up table (LUT) with four dedicated registers to help improve timing closure in register-rich designs and achieve even higher design packing capability than previous generations.", 而 LE 官方文件的說明為 4 組輸入, 由此可得知 ALM 為 > 2 LE 的組合, 這推斷也與官方提供的表格數字間符合.

DSP blocks

在程式中所使用到的乘法與浮點運算必須使用到 DSP blocks, 因此對於 FPGA 而言是相當重要的運算資源, DSP 提供了18b 與高精準的兩種運算模式:
此外亦單一 DSP block 提供了單精準浮點數運算以及串連4個 DSP block 形成倍精準浮點數運算

了解這些 FPGA 內部資源與用途與後續的 optimization 有很大的關聯

2017年1月25日 星期三

解析 Qualcomm Hexagon 680 架構 III - Thread Model and Execution Model

Thread Model

最後讓我們講述 Hexagon 680 的 Thread Model
在 Qualcomm 開發 DSP 的過程中一直都有 Hardware Threads 的支援
因此在談 Hexagon 680 的 Thread Model 前, 讓我們先來看看 V5x 的 HW Threads

上圖為 Qualcomm 在 2013 年 Hotchips 會議上投影片的 Page 11
從這圖可以看到 Hexagon 680 的 V6 基本上與 V5 架構差異不大
也是 4-slot VLIW, 兩個是 Data Unit, 兩個是 eXecution Unit
同個 DSP 核心具有3個 Thread Context, 在硬體設計上已考慮簡化多工上的需求

在 2015 年度 Hotchips 中 Qualcomm 簡報的 Page 8:
由此可見, V6 Scalar DSP 以 2Ghz 頻率運作
俱備 4 HW Threads 支援, 每個 thread 以 500Mhz 運作
而 HVX coprocessor 以 1Ghz 頻率運作, 具有兩個 context, 一樣每個 thread 以 500 Mhz 運作
因此同一時間可以有兩個 Thread 控制著 HVX, 另外兩個處理 Scalar 工作
但光以上看投影片, 不知道是否有人懷疑過, 或許 V6 DSP 為 quad core, 而 HVX 為 dual core?

這裡我們回到 HVX 官方文件當中:
從這張圖可以看出, V6 DSP 具有的是 1C4T, 而 HVX 為 1C2T
但是, 官方在投影片與到此中所說明的, 並非 Hexagon 680 所有的模式
如果還有印象, 在系列文II當中留了一些不確定:
1. HVX 對於 L2 的總寬度為 512b x2, 而非 1024b x 1
2. 對於 VPF/VRF 的描述, 我加了 "基本上" 這個有所保留的詞

再談這之前, 先來介紹 HVX 的 Vector Modes:

也就是 HVX 中支援了兩種不同長度的 Vector
  • 64B 模式, Vector 寬度為 512 bits
  • 128B 模式, Vector 寬度為 1024 BITS
而這樣的模式並不是像 NEON 中分為長短 vector 並且可以混用, 而是如上圖, 直接將所有的單元(VX, VPF, VRF), 以 512b 寬度的方式等分為二

所以除了 1C2T(128B) 的方式外, 事實上 HVX 還存在另一種 HW thread 的型態 - 1C4T (64B)
在這個模式下, 4 個 V6 HW Threads 各自都可以控制一個 64B Vector Mode 的 VX Thread Context. 所以 Hexagon 680 提供多元且彈性的運作模式. 而這也說明了 系列文II 當中沒交代清楚的部分.

Execution Mode


Qualcomm 曾經官方撰文介紹過 Hexagon DSP 的軟體開發
在 Qualcomm 裝置上, 在 DSP 上執行工作需要幾個步驟:
  1. 下載與安裝 Qualcomm Hexagon SDK
  2. 將你於 DSP 上實作的功能 API 以其 IDL (Interface Description Language) 方式描述.
  3. SDK 會自動產生 Header files 與 stub (下圖中應用處理器端, 也就是 ARM 處理器端提供給 client 的呼叫介面, client 通常是其他的 library 或是 executable)及 skel (DSP 端去呼叫你實作功能的進入點) 等相關函式庫.
  4. 在 DSP 端實作你的 API, 並與第3點中自動產生的 skel 函式庫 link 成為一個 shared object 並放置於該平台裝置中.
  5. 將你的 native APP 與 link 到第3點自動產生的 stub 來呼叫你的 API.
  6. (選擇性) 建立你的 API 專屬的 Java bindings 好讓 APK 能夠使用.
而這些背後所仰賴的機制的核心即是上圖所描述的 FastRPC

一旦 Native APP 呼叫所需的 function, 便會執行自動產生的 Stub lib, 它會 透過 ARM 與 DSP 間橋接的 Driver-Framework, 載入先前所產生的 DSP 端的 shared object, 而 DSP 端上的 OS 會動態地載入該 object, 並且以 Skel 介面處理來自 ARM 處理器端的請求.

2017年1月22日 星期日

解析 Qualcomm Hexagon 680 架構 II - Hexagon Vector eXtension (HVX)

在講解 680 中的 SIMD 單元 - HVX 之前, 還是先以系列文 I 的 blocks diagram開頭, 並且今日重點會是文中提到第3點的官方文件

從 blocks diagram 中可以看到 HVX 由三個主要部分所組成
  • VX : Vector Extension Unit, 也就是 HVX 的 SIMD 指令執行單元
  • VPF : Vector Predicate register File, 這部分是用來處理 SIMD 的中較不易處理的 branch, if-else 等 divergence. 以 8bit 運算來說, 若需要提供 SIMD 中每個 lane 所需的 true/false boolean 資訊, 需要 1024/8 = 128 bit, 而 Predication Register 寬度, 基本上就是 128b.
  • VRF : Vector Register File, 這即是 VX unit 中用來存放的暫存器, HVX 為 1024b vector, 因此基本上這部分為 1024b
而對外的介面有兩個:
  • Co-Processor Instruction Port: 用來與 V6x 處理器溝通的, V6x DSP 是透過這個介面將指令送給 HVX 單元執行
  • 兩組 512b-wide L2-cache/L2-TCM port: 相較 V6x 處理器對於 L2 的頻寬為 256b port, HVX 對於總寬度為 512b x2 , 這樣的設計很重要, 這讓 HVX 能夠更快的取得所需要的 vector 資料, 由於 HVX 為寬度 1024b vector 的 SIMD Unit, 因此這樣的寬度是有必要的. 至於為何是兩組 512b 而非一組 1024b, 這部分與其 thread model 有關, 在下一篇會介紹這個部分.

首先來談 VPF/VRF 的 Register File 部分, 在 HVX 中分別有著 32組 vector register 以 V0 ~ V31 表示, 而 predicate register 共有 4組, 以 Q0 ~ Q3 表示, 這些表示法用在 SDK toolchain 所產生的 .s assembly 中, 對於 V0 ~ V31, 並沒有區分資料型別, 因此為了表示使用的精確度, 在指令中對於 register 使用的方式必須有標記所
從語法中可以看出兩點, 首先是並不支援 float 運算(當然以這樣的notation 要新增也不是難事就是了), 再者是 SIMD 的 operation 與 data type 是脫勾的, 重點是搭配的 register data type, 以上面的例子來說
V2.h = VADD(V3.h, V4.h)
這個 asm 指令是指套用的是 add 指令, 並且資料型別是 h 所代表的 signed halfword (也就是通常 C 中的 short), 因此這行所代表的意義為: 將 V3 與 V4 的資料以 signed 16b 的方式相加後存入 V2. 這樣的 asm 設計也直接影響到了 HVX 在 C intrinsics 上的 coding style, 而這樣的設計其實有好有壞. 好處顯而易見的是 asm 很簡單, 而且能有效的降低寶貴的 register 浪費, 但這樣的設計當 export 到 C 的層次的時候, 就缺乏了對於各資料型別的 vector type, 因此沒有對應型別的 operator overloading, 但這點若採用 C++ 去自己實作也並不難就是了.

接著進入 HVX 的指令類別, HVX 有著六類的指令類型, 在系列文 I 當中也討論過 V6x 各個 slot 的支援, 而 HVX 也是使用相同 VLIW4 的設計, 針對這六類支援的指令, 於官方文件中有著下列表格:
從表格中可以出六類分別為:
  • Aligned Memory Load
  • Aligned Memory Store
  • Unaligned Memory Load/Store
  • Multiplies and special ALU
  • Vextract
  • Simple ALU, Permute, Shift
對於表格的呈現方式, 各位或許會覺得很奇怪, 為何不是像 V6x 處理器中那樣, 是以 slot 為主要分類, 而是以 instruction type 為主要分類. 這主因在於對於同一 cycle 中4個指令的使用上搭配要考慮的不是 slot 本身而已, 因為對於 HVX 來說支援每個 instruction type 所動用到內部的 "resource" 不同. 那什麼是 resource  呢?

在官方文件中有說明, HVX 本身由六種 resource 所組成, 這些 resource 讓 SIMD 指令得以運作
因此弄懂每個指令背後所動用到的 resource 也是重要的:
等等!!! 不是6類嗎, 怎麼變成11類了? 其實細看一下, 你會發現它只是將一些指令細分, 特別是第4類與第6類再去細分, 但是基本上還是那六類. 像是 Multiply 區分了 8b 與 16b 的部分, 這裡請注意, 8b 與 16b 這兩者分別使用的 Multiply Resource 數目不同, 所以這裡讓我們以官方在 Hotchips 中的 slides 對於效能作進一步解釋的說明:
上面這張 Qualcomm 官方投影片很常出現在一些淺談 Hexagon 680 與 HVX 的文章中, 但請注意其中的第2大點的 8b 與 16b 的乘法數目, 相信很多人很難理解(或者根本沒弄懂)為何 8b 的乘法數目為 16b 的 4倍, 因為 1024/8 = 128, 而 1024/16 = 64, 單以 SIMD 的寬度是無法解釋這樣的落差, 但這時若將指令背後 resource 的消耗數目納入考量就會很清楚了, 因為 HVX 處理器中提供了兩組 Multiply Resource, 而 16b 的乘法指令按照上面提供的表格一次即會消耗完兩組,  而 8b 的乘法指令只會消耗一組, 因此同一個 cycle 中, 至多只能塞入一個 16b SIMD 乘法 也就是 1024/16 = 64 個, 而 8b 可以一次執行兩個 8b SIMD 乘法, 也就是 (1024/8)*2 = 256 個.

所以若不考慮 data dependency 因素, 一個指令能否再塞進一個 VLIW 指令中以同一cycle執行, 這背後必須要同時考量 slot 是否 conflict, 以及 resource 是否有餘裕, 這兩者同時都滿足, 才能夠塞進同一個. 對於這樣的考量, 官方文件提供了一份整理好的總表
在這份總表中, 查找所需滿足的條件, 即反映了是否需要額外的 cycle 去執行所想加入的指令. 另外需要注意一件事, 請注意 resource 使用的平衡性, 像是 shift unit 只有一個, 千萬不要不明就裡, 將乘法全以 shift 替換就以為是優化, 在這樣的情況下, 有可能增加的 cycle 數目是以倍數來計, 對於這樣的處理器, 優化的策略在於儘量在乘法與Shift所需要的運算數目中取得平衡. 其他比較常考慮的就是 load/store unit.

對於 HVX 指令中是否有讓我覺得印象深刻的? 我個人認為是 vdelta/vrdelta 以及 permute 所屬指令, 這個類別與一般 DSP 常提供的 PACK/UNPACK 太不同了, 它提供了一個 general permute network 來做兩個 vector register data 的次序調移.
其實當下我覺得這東西一時間我只想到 sorting network, 而且還搞不清楚狀況, 直到看到了一個圖解, 發現這 1 cycle 運算真是潛力無窮, 所有 1-1 mapping 的方式的次序改變, 只要能算得出 Vv, 就可以快速地作 Vu 至 Vd 的轉換. 而對於像我腦子這麼簡單的人, 它也提供了入門版的 shuffle 與 pack 指令.
最後來談 HVX 的資料取得方式來源 - L2-cache/L2-TCM, 對於一般 DSP 在資料傳輸與核心運算的平行部分, 儘管 HVX 文件沒有特別強調 DMA, 但實際上 HVX 提供了對應功能的 L2FETCH, 以一個多媒體為訴求的 DSP  來說, 其也支援了 1D/2D 資料的傳輸.

而到此並沒有特別的地方. 值得一提的是, 這裡的 Level-2 是只對於 V6x 而言, 但對於 HVX 的角度來說, 它就是如同 L1 的存在, 最特別地方在於 V6x 架構中 L2 可以扮演兩個角色, 在 block diagram 中已經標明了
  • L2 data cache
  • L2-TCM
這設計是很有趣的, 因為這兩種記憶體各自有著不同且互補的特性:
  • cache本身有著 transparent 特性, 對於 programmer 本身並不需要特別改變, 只需要注意沒有不利於 cache 管理機制的存取方式,  cache 能夠帶來效能的 scalability (隨著 cache 的加大而效能改善), 功能上的 portability (無論 cache 大小, 功能本身的正確性不會影響), 這樣的特性對於一些高度 locality 的程式很有幫助, 也能降低程式的實作複雜度, 但是 cache 本身有著難以精確控制的特性, 對於一些資料的存取並不能有時間上的保證.
  • Tightly-Coupled Memory (TCM), TCM 有著指定的位置, 距離 processor 很近, 有著存取時間上的保證, 但是由於使用上 Programmer 必須意識到它的存在, 並且自己管理, 所以 TCM 若容量增大也在不修改程式的情況下無法直接增加效能; TCM 若容量變小在不修改程式的情況下程式可能無法正常運作. 但是它卻能有時間上存取的保證, 因此對於一些像是 Lookup-Table (LUT) 的需求, TCM 顯得相當重要
在 V6x 中能將 L2-cache 做分割, 雖然相關機制未知, 但是運作上能將部分 L2 作為 Cache 而部分作為 TCM 是標注在 V6x 的 features list 中:
下篇文, 沒意外的話也是系列文最後一篇, 要談論的是 Hexagon 680 的 Thread Model 以及 Execution Model

2017年1月21日 星期六

解析 Qualcomm Hexagon 680 架構 I - V6x 架構

Qualcomm S835 於不久前發佈
其中內建了強大的 Hexagon 682 DSP
雖然在 S835 的發佈宣傳文中僅說明了新增了對於 TensorFlow 與 Halide Language 的支援
但是還是有發佈專文說明 Hexagon 682 DSP 即便與 Adreno 540 GPU 相較下
在 TensorFlow 與 Machine Learing 方面的效能與功耗效率的其表現出色
儘管 DSP 處理器這字眼對於程式工作人員並不陌生
但除了特定領域面向的工作者外, 有機會接觸者並不多
這與應用平台的可見度與普及性
以及各家 DSP 相關的軟硬體在架構與軟體資訊較為封閉有關

Qualcomm 自 S820 加入的 Hexagon 680 這顆運算性能強大的 DSP
(事實上先前即有 V5 的 DSP,  但是 HVX 讓 V6 增色不少)
相關文件與 SDK 皆有公開釋出, 所以是個很好的說明範例
而 Hexagon 682 應該與 680 相去不遠
因此這裡以系列短文介紹探討這顆 DSP 的架構

探討的內容為 Qualcomm 官網內的一份簡報與兩分文件 (需註冊帳號, 免費)
1. Architecture of the Hexagon 680 DSP for Mobile Image and Computer Vision
2. Hexagon V60/V61 Programmer's Reference Manual
3. Hexagon V60 HVX Programmer's Reference Manual

Hexagon 68x 為 Hexagon V6x 架構的處理器
而 HVX 為 Hexagon Vector Extension 的縮寫, 以 Coprocessor 的方式與 V6x DSP 整合運作
下圖取自官方文件 3. 的系統 blocks diagram
首先讓我們先以上圖來討論 Hexagon V6x DSP
Hexagon DSP 主要由 5 個部分所組成, 其中各個 block 意義如下:
  • D$ - Data Cache
  • RF - Register File
  • I$ - Instruction Cache
  • XU - eXecution Unit
  • MMU - Memory Management Unit
另外有3個對外的控制介面, 分別是:
  • AXI, AHB 對外的 Bus 與 Host 端系統整合
  • 256b-wide L2-cache port
  • Co-Processor Instruction Port 控制 Hexagon VX Co-processor
其中值得注意的地方是 AXI/AHB 以及 L2-cache port 中, V6x 內部中以 L2 Control 與 cache state/tag table 的 function block 連接著, 整個 Hexagon DSP 的資料, 都仰賴著 V6x processor 統一對外透過 AXI/AHB 而取得, 若了存入 L2-cache, 相關資訊則會以 L2 state/tag 紀錄, V6x processor 本身即能夠完全掌握系統中 L1 與 L2 一致性控制的管理. 而 HVX co-processor 即是在 V6x processor 控制流程與 data control 的方式下, 以兩個 512b-wide L2-cache port 存取資料來運作. 即便 HVX 所需資料發生 cache miss, 負責處理這問題的是 V6x proccessor 本身.

首先是 Cache Size, MMU 的相關資訊:
這樣的設計方式與其他 DSP 不同的地方在於它的 instruction 處理部分, 預設是採用 I-Cache 的方式, 以架構來說,  缺乏 I-TCM 的方式對於一些 hard realtime 的應用稍有不利的地方, 但是其支援了 hardware instruction prefetch 的能力彌補來這部分. 而 MMU 部分提供了 4GB 的定址空間, 作為 DSP 的 Virtual Address 與 Physical Address 間著轉換. 由於對於 ARMv8 系統都提供了 > 4GB 定址空間, 因此對於 V6x DSP 使用的記憶體區間應有軟體上或硬體上的限制.

接著來探討 V6x 的剩下的 XU 與 RF, 讓我們用官方的 architecture 圖來說明
透過官方架構圖, 可以看到需要探究的東西增加了, Control Registers 基本上是控制流程(Loop Regs, Predicate Regs)與提供流程相關(Status Regs, Program Counter)資訊的用途, 這裡就捨去不談, 這裡主要談 XU 衍生的 Sequencer + S0 ~ S3 以及 RF 所代表的 General Registers R0 ~ R31

對於 S0 ~ S3 來說, S 代表的是 Slot 的意思, 看到這個字眼稍有敏銳的人就知道接著要說的是 Very Large Instruction Word (VLIW), V6x processor 本身即為 VLIW 架構, 許多的 DSP 都採用這樣的方式, 其優點是:
  • 簡化硬體設計: 對於 DSP 來說若採用 SuperScalar 架構會增加面積在 hardware instruction scheduler 的實作上, 除了增加成本外也增加 power/thermal 的風險
  • 可精確分析與預期效能: 由於 VLIW 的指令排程是透過 compiler 來完成, 而硬體上來說的處理相對簡單, 因此執行上的時間, 若以 single loop iteration 而言 compiler 即能提供資訊, runtime 所需時間, 透過 simulator/emulator 就能得到很精確的時間估測
對於 VLIW 而言, 弄清楚每個 slot 能夠處理的指令是一個重點, 以下為 V6x 的 Slot-Instruction 配置說明:
可以看出, 除了共通的 ALU32 指令外, 每個 Slot  都有著其所被賦予的任務:
  • Slot 0, 1: 主要負責記憶體的 load & store, 其中 Slot 0 更負責了 cache 維護, maintenance 與匯流排的操作.
  • Slot 2, 3: 負責主要的負責數理運算, 以及 Vector 相關指令, 程式內部的流程控制 (J/JR/CR)
到這邊可能會有人想喊 "等等!!! Vector 運算 !?"
是的, V6x 本身即支援了初步的 64b-wide Vector 運算, 下圖為官方的指令範例:
但 V6x 有一點是特別的, 就是它的 Register File
主要是它將 General Register 與 Vector Register 整合在一起:
在操作上亦提供了便利的 32/64b Register Notation
對於 V6x 就以此簡短的介紹, 有興趣者可以再透過官方文件深入
下一篇會介紹讓 Hexagon V6x 大幅提升計算能力的 HVX

在 ARM 平台上使用 Function Multi-Versioning (FMV) - 以使用 Android NDK 為例

Function Multi-Versioning (FMV) 過往的 CPU 發展歷程中, x86 平台由於因應各種應用需求的提出, 而陸陸續續加入了不同的指令集, 此外也可能因為針對市場做等級區隔, 支援的數量與種類也不等. 在 Linux 平台上這些 CPU 資訊可以透過...