2018年7月31日 星期二

永別了, Computer Vision

在 LinkedIn 頁面上看到了這篇有意思的文章, 以透過將 Computer Vision 領域擬人化, 原以為終於發現一個能長久對話的朋友, 然而現在卻因為虛弱以至於死去, 必須對此撰寫一封對它的告別信. 而更有意思的是作者是服務於 Apple 於影像視訊資料處理專業的工程師. 而告別的原因不為了別的,正是因為 ML/NN 的熱絡與強大, 特別喜歡裡面的兩個句子:
  • Data will become your king, but your king has no intention of keeping you around longer than needed. (資料將成為你的王, 但你的王無意讓你再被需要)
  • After years of traditional methods, Face ID became a reality mostly because of data feeding the machine not because of some super crafty algorithm. (在傳統方法的多年之後, FaceID之所以能成為現實, 主要在於餵給機器資料而非充滿多端詭計的演算法)
作者不認為 CV 這十年會消逝, 也認為將會有其他演算的出現, 但是現實也往往傷人, FaceID 能商品化還是在於 ML/NN. 若稍有注意的話, 今年的 CVPR 2018 中有著大量使用 ML/NN 的方法與論文, 對於 CV 演算理論上或許必須要樂觀才能走下去, 然而 CV/Image Processing 是否正走在 Audio/Speech 被 ML/NN 橫掃的歷史道路上? 每個相關工作者在這樣的階段, 或許都有著不等的矛盾與挫折.

2018年7月26日 星期四

Vulkan Learning Resource



這篇主要在於集中整理先前在 Facebook 分享的各類 Vulkan 資源

Tutorial 類

Vulkan Tutorial
相當高品質的 Vulkan 教學網站, 甚至提供了電子書版本
https://vulkan-tutorial.com/

LunarG Tutorial
這是 LunarG 提供的 Vulkan SDK 文件的一部份
https://vulkan.lunarg.com/doc/sdk/latest/linux/tutorial/html/index.html
另外 LunarG 提供了相當有用的 Spec 參照
https://vulkan.lunarg.com/doc/view/latest/linux/vkspec.html

Intel  - API without Secrets: Introduction to Vulkan series
這系列文為 March, 2016 開始連載, 至今都還在更新, Part 7 為 April, 2018 發表, 日後會持續在此更新文章聯結
Part 0: Preface
Part 1: The Beginning
Part 2: Swap Chain
Part 3: First Triangle
Part 4: Vertex Attribute
Part 5: Staging Resource
Part 6: Descriptor Set
Part 7: Uniform Buffers — Using Buffers in Shaders

ARM
Vulkan API Tutorials

Misc
http://ogldev.org/www/tutorial50/tutorial50.html
https://gist.github.com/graphitemaster/e162a24e57379af840d4


主題文章

在各大 GPU vendor 有著許多說明 Vulkan 特定設計或設運作機制的說明文章, 不一定是 programming 教學, 文章都有所要深入探討的主題.

GPUOpen/AMD
以下項目主要是 GPUOpen 頁面上 Vulkan 標籤選取的

Nvidia
以下項目主要透過 Nvidia Developer 頁面的 Vulkan 標籤取得
ARM
Imagination
Intel
Khronos

2018年7月23日 星期一

在 Vulkan 中使用 OpenCL Kernel


OpenCL kernel 在 Vulkan 上的使用

OpenCL 自 2008 年底釋出 1.0 至今過了十個年頭,
在 GPGPU 面向上也累積了相當的開發者社群,
儘管 Desktop 上熱烈程度不如 Nvidia CUDA,
而 Mobile 上因 Google 的錯誤決定去強推 RenderScript,
造成手機商在配置與否的態度不一
這樣的情況造成 OpenCL 的使用程度上既不熱烈,
然而許多情況下又是唯一方案


在 Vulkan 的推出後,
由於其基於 SPIR-V 標準的特性, 讓許多應用能夠依附於此之上
其中之一即為 OpenCL kernel 的使用
透過預先以 compiler 將 OpenCL kernel 轉為 SPIR-V binary
再搭配 Vulkan 載入與使用 Compute Shader 的方式, 得以間接的使用 OpenCL 語法

所需的第一個工具為:
https://github.com/google/clspv
clspv 為將 OpenCL kernel 編譯出 SPIR-V 的 Compiler

再來即為 Vulkan 對應 OpenCL Host Code 的使用流程
網路上可以找到下列三篇提及 OpenCL Host => Vulkan 流程轉換的 Blog 文
OpenCL -> Vulkan: A Porting Guide Part I
OpenCL -> Vulkan: A Porting Guide Part II
OpenCL -> Vulkan: A Porting Guide Part III
以及該系列文作者於 Khronos Munich 2017 的簡報: OpenCL to Vulkan: A Porting Guide

若跳過對 OpenCL 與 Vulkan 的對應, 想直接切入 Vulkan Compute Shader 流程
該作者的 A simple Vulkan Compute example 一文以及 GitHub gist 幫助就相當大

個人透過上述幾篇, 實作了上述 Compute Example 等義的 CL kernel
並整合開源放上 https://github.com/champyen/vkcl
以 Intel HD Graphics 500 搭配 Mesa Vulkan 實作, 能正確執行

儘管因此能夠撰寫 OpenCL kernel 來在 Vulkan 環境中使用, 然而並不代表沒有任何限制
相關限制都在 clspv 的 OpenCLCOnVulkan 文件中

Kernel
  • argument 非 buffer/image 型別, 會透過整合到一個 buffer 方式傳入 
  • WorkGroupSize 無法透過 host 指定, 似乎只能以 __attribute__ ((reqd_work_group_size(x, y, z))) 方式指定
  • 無法呼叫其他 Kernel
  • 不能使用 local 作為 argument
  • 不能使用 half 作為 argument
(事實上, 相關 argument 自 Host 傳遞到 Kernel 的機制也並不是很方便)

Types
  • boolean 無法作為 global, constant, 而 sizeof 為未定義行為
  • 8-bit, 64-bit, event_t 型別無法使用
  • 沒有長度為 8, 16 的向量 (也就是儘支援向量長度 2, 3, 4)
  • pointer 的限制很多
Built-In Functions
這部分相當的多, 都是無法使用, 簡短提示如下:
  • atomic: atomic_xchg()
  • common: step(), smooth_step()
  • conversion: 俱備 rounding, saturation 都不可使用
  • math, integer, rational: 這部分特定的 function 無法使用, 請參考說明
  • async, shuffle, printf, vload/vstore: 請直接當不存在 ...

Vulkan GLSL Compute Shader

OpenCL kernel 在 Vulkan 1.0 上運作需要兩個 extension:

  • SPV_KHR_variable_pointers
  • SPV_KHR_storage_buffer_storage_class
此兩個 extension 於 Vulkan 1.1 中成為核心特性 (因此日後可以基於上述限制來使用 OpenCL)
若考量 Vulkan 1.0 裝置的相容性, 那麼採用 GLSL Compute Shader 方式撰寫是不錯的選擇

此外 Vulkan GLSL compute shader 的相關範例也並不多, 以下是看到覺得不錯的
https://github.com/Erkaman/vulkan_minimal_compute
https://github.com/alexhultman/libvc
https://arm-software.github.io/vulkan-sdk/basic_compute.html

撰寫 Vulkan GLSL compute shader 也需要 compiler 產生 SPIR-V
這時 LunarG 的 Vulkan Toolchain 能夠直接下載使用
https://vulkan.lunarg.com/sdk/home

update: 值得一提的是與 OpenGL GLSL compute shader 的不同, OpenGL GLSL compute shader 制定時, 能使用的 builtins 受限於既有 GLSL 部分, Vulkan 制定的時候對其做了擴充, 能使用的函式較接近原有的 OpenCL:
https://www.khronos.org/registry/spir-v/specs/unified1/GLSL.std.450.mobile.html

Facebook 開源其處理系統面 OOM(Out-Of-Memory) 問題的專案 - OOMD

儘管 Facebook 以 OOM Killer 來說明 OOMD, 但以 OOMD 在功能性上的描述以及 GitHub 專案上充斥著 detector 字眼, OOMD 應為 Out-Of-Memory Detector 的縮寫.
Facebook 旗下有著各式各樣的服務產品: News Feed, Messenger, Instagram, WhatsApp, Oculus 等等. 這些服務產品系統是基於散佈各地並以百萬計的伺服器來稱起. 當隨著基礎建設的與時地擴增, Facebook發現其主機與網路硬體涵蓋了數個世代. 橫跨多代環境的一個副作用是新版軟體或先配置更改可能導致系統在一台主機運行正常但在另一台主機上有著記憶體不足的問題.
Facebook 發現 Linux Kernel 中的 OOM Killer 有著處理時機過晚的情形,因此常會讓系統落入一種競逐資源而不斷地變換狀態的 Livelock 狀態. 為此目的 Facebook 才開發了 OOMD, 以不同的方式來解決記憶體不足的問題. (champ: 而與 Linux OOM Killer 最大的不同點有二: 一者為在於這是個 userspace 方案, 再者為 OOMD 是以預防的思維來面對 OOM 問題 ) OOM 其設計提供了兩個關鍵的功能特性:
  • pre-OOM Hook: pre-OOM Hook 主要目的在於軟體系統受到 OOM 威脅之前, 先提供對於對 OOM 問題的預警.
  • plugin system: 提供管理者能夠客製化在主機上對於每個工作負載的處理方式. 這主要在於提供了記憶體不足的問題更佳彈性的處理方式.
Linux OOM Killer 的主要職責在於保護 kernel, 使主機能正常運行; 在終止特定 process 來達成此目的時, 其並不會注意到該 process 背後可能的重要性. 因此當 OOM Killer 運作時存在著在主機上運行的應用程式都將乘受著重大風險. (champ: 對於 Facebook 這類公司而言, 風險就代表著損失的可能, OOM Killer 並沒有提供任何選擇, 相較直接終止帶來的服務終止, 使用者抱怨, 管理者發現與回復有著龐大的延遲. 在商業上應用會希望提供損失程度不同的方案選擇, 像是主機主動增加延遲, 積極地減少服務量, 背景持續將低重要性資料寫到 HDD/SSD/NVMs, 轉換為較少損失的方式來維持系統的可操做與控制性)
OOMD 的客製方案基於了兩個關鍵性的發展:
  • Pressure Stall Information (PSI): 這是一個由 Facebook 所開發正準備合入 linux upsteam 的系統工具. PSI 追蹤著三個系統主要資源: CPU, Memory 與 I/O, 觀察這些資源隨著時間的使用變化. 藉由回報因資源短缺造成的時間損失, 以此來提供整體工作負載可量化的測量.
  • Cgroup2: cgroup 為 linux 上用來做資源管理的系統. cgroup2 為新一代的設計. 而 OOMD 利用其複雜的精算機制來確認每個工作是否正常地運作.
由上述可以得知, 並非在一個系統上執行 OOMD 後就解決了問題. OOMD 是個對於系統資源提供的資訊系統, 以及預警發佈的機制. 必須有效解讀與利用 OOMD 提供的資訊, 並針對 OOM 發生前預先處理的方式有著事先的設計與規劃. OOMD 才算是正確的導入, 並且才能發揮最大的功效.
Facebook 稱實際運作上, 以此完全消弭了長達 30 分鐘的 livelock. 此外有著相關的實驗數據, 像是:
在程度不等的記憶體流失速度(a process that slowly leaks memory)下, 工作負載. 實驗所用的工作負載所需時間為 120秒,高於 120 秒時間都可以視為效能的降低. 由上圖可以比較使用 OOMD 不使用的情況下完成工作的時間. OOMD 僅有著小部分的效能耗損. 此外還有另外的兩個實驗, 詳情就留給大家自己研究.

2018年7月19日 星期四

Go Concurrency 基礎短篇

來源: https://medium.com/@matryer/very-basic-concurrency-for-beginners-in-go-663e63c6ba07
很不錯的 Go 平行處理的基礎文章, 文中所說明的 goroutines + wait group 方式應該適用超過八成的應用需求, 透過簡單的方式與範例說明 goroutine 的用法與為何需要 wait group. 以及以實際範例相較於非平行的差異. 文中的一句話很不錯:
I really love that adding concurrency in Go takes only three key presses, g, o and a space.
簡單, 貼切. 好語言不學嗎?以及

Symbol namespaces for Linux Kernel

近日 Martijn Coenen 提供了一份 patch, 主要在於提供 Linux Kernel 各 subsystem 的 symbol namespace 的功能.目前不確定是否與何時會合入 mainstream, 但是該 patch 為 Greg Kroah-Hartman (與很多developer) 所盼望的,或許不久的未來會正式支援.
在 Linux 4.17 中, exported symbol 總數已超過了 30000 個, kernel 開發者漸有了共識, 也就是 export 的範圍太大, 並且難以理解.
基本上這些 symbol 分為三類:
  • 對 drivers 有實際意義的 symbols
  • 由於功能性緣故被切分到多個模組的 symbols, 基本上不該被用在該 subsystem 以外.
  • 只對(目錄結構)樹狀內有意義的 symbols
這份 patch 的作用不在於減少 exported symbol 的數目, 而在於提供一個機制能夠提供一般性 export symbol 的秩序. 這份 patch 使得 subsystem 能夠將各自 exported symbols 放入個別的 namespace, 而模組開發者僅在需要的時候才 import 這些 namespaces.
這也使得 subsystem 維護者能夠輕易限制這些使用 namespace 的 symbol 對於 kernel 其他部份的使用. 也能夠分割出作為文件用途的 exported symbols; 像是, 一個僅作為 debugging 作用的 sysmbol set 可以放入名為“SUBSYSTEM_DEBUG” 的 namespace.
該成果受到 Greg Kroah-Hartman (LDD一書作者, linux stable kernel 維護者)的讚賞. 並稱讚使用 build system 為各別 subsystem 基於 symbol namespace 改變而自動產生 patch 令人驚豔.

邁向 ARM 64bit-only 的世界

展訊通訊(Spreadtrum Communications, 如果我開頭沒解讀錯誤)於 Linaro SFO 2017 簡報 ARM Binary Translator — Towards a 64bit Only World
 現在於 Android 裝置上通常會附上兩個版本的執行環境 — armeabi-v7a 與 armv8-a 來支援各種 Application 的相容 (主要還是 NDK 產生的 libraries & executables)
通常相關重複的函式庫資源會耗去不少儲存空間與系統的資源(最直接的就是 memory)
而這個 talk 基於今年曼徹斯特大學提出 MAMBO-X64 ARM Translator, 談論建立只有 aarch64-only 的系統環境, 而對於 32-bit 的程式需求則透過 translator 的方式來滿足
該 talk 涵蓋了自 kernel, system, code translation/generation 等等的面向: Addressing Space, Code Translation, Dispatching/Linking, Floating Point, Registers, System Calls, Memory Managment 等等
updated: 感謝好友 Trent Lo 提供不錯的觀點, 對於客製CPU 的 chip vendor , 這更能節省硬體的成本, CPU無須放入 32位元 相容實作的部分, 縮小 core size
延伸閱讀: Low Overhead Dynamic Binary Translation on ARM
https://www.research.manchester.ac.uk/portal/files/56078084/pldi_16.pdf

必須慢下來才能學著 coding 得更快

值得一讀的好文
回想過往學習 C/C++/Java, 到 Functional Programming 與計算架構
以及最近學習並套用 Halide 的過程
今日看這篇很有感覺, 很多時候欲速則不達
實際細細思考設計背後的哲學, 或針對實際的 case 深入挖掘與套用才是”真正學到”
許多人們最終看到強大與有效的工作成果(甚至背後運作原理不難理解)
該知道與了解的是其背後都有著無法立刻套用加上緩而漫長的內化過程

C 並不是一個低階語言

寫得很不錯的文章, 從許多方面探討 C 語言並非低階語言, 從近日很夯的 Spectre/Meltdown 開始一路談 CPU Architecture, Operating System, Compiler 到 C specification, 甚為精彩, 也提及許多語言與處理器來作論述參考, 言簡意賅, 然而能夠全然了解文中所述需要相當的實務與系統基礎
儘管文章內容精彩, 但對於結論個人並不是那麼地認同
 1. 許多的問題其實不單是 C, 而是 serial logic 的處理, 不可否認撰寫 serial code 對人們來說是較為直覺的, 而 serial 在 arch, compiler 中處理的問題不單單僅限於 C, 甚至人們很需要高度簡化的 serial code 實作的方式(這也是 python 受歡迎的原因之一)
 2. 文中以 GPU, UltraSPARC, ARM SVE 為例子想凸顯 C 的問題, 然而卻忽視了這類 SPMD/MultiThead/SIMD 處理器/單元對於 divergence 與例外處理 overhead 都極大, 對於應用本質需求上的問題甚至不若 C + CPU
 3. 延續 2. 這類處理器/單元 多是 Domain-Specific 的, 多半是針對特定應用範圍所提出的 Instruction Set Architect + Programming Model, 而其實這些並不與 C 的問題衝突 (甚至有些問題是相關的)
 4. 或許是作者的偏好, 內文提及多次 Erlang, 最終提到了 actor model, 許多 thread model 的確有助於 throughput, 然而考量 multiple core/thread 的語言並不是只有 Erlang 而已, 然而 throughput 也並不是唯一需要考慮的, 對於 HW/SW 相關工作尚需考量 debugging, integration, 對於 ecosystem 而言, 並不是一個語言一個 model 就可以帶過的

Shrinking the kernel 系列文

在一些環境上 Linux kernel size 依然是需要考量的問題
像是 IoT 的裝置, 較小的 kernel size 意味著騰出 DRAM 或 flash 來放讓 initramfs 或是其他資料作利用
LWN 自 2017 年底發表一系列 Shrinking the kernel 文章:
PART 1: Shrinking the kernel with link-time garbage collection:
https://lwn.net/Articles/741494/
PART 2: Shrinking the kernel with link-time optimization:
https://lwn.net/Articles/744507/
PART 3: Shrinking the kernel with an axe (trim):
https://lwn.net/Articles/746780/
PART 4: Shrinking the kernel with a hammer (XIP):
https://lwn.net/Articles/748198/

Go runtime 是如何有效率地實作 maps (而不使用泛型)

Go 語言中有個很方便的工具 — map
基本上類似於 C++ 的 std::unordered_map
與 Java 的 java.util.Hashmap
然而對於 Go 而言最大的不同點在於它是個結合了自 compiler 到 runtime 的所實作的演算工具
這篇文章詳細地解釋了 map 在其背後的設計與實作的方式與其考量, 並與 C++ 及 Java 的方式做了比較, 值得一讀

一個 Go 1.9 的修正如何對 Gitaly 服務加速了 30x

Gitlab 在運作過程中發現其 RPC 處理程序 Gitaly 的 latency 日漸增加, CPU 的使用率也顯著提高, 原以為是 resource leaking 問題 (這也是許多工程師第一時間也都會這樣認為, 這也是想分享這篇的一個點), 然而透過 pprof 與 cAdivisor(cgroup 分析工具) 分析發現並沒有 leak 的現象, 最後追蹤到了 SIGABRT thread dump 中發現問題在於 syscall.ForkLock 而該 問題指向了 clone() 的方式, 而這問題在於 Go 1.8 中使用的 fork 方式會複製 parent process memory space, 因此當系統逐漸增大後 fork cost 就會變高, 而在 Go 1.9 後便改採用 posix_spawn 來避免此問題
問題的排除, 除了過程引人入勝外, 其面對問題的思考與處理過程也是能作為借鏡

視訊編碼到了盡頭?

文長, 值得一讀
作者是任職於 Netflix 的長期 Video Coding 工作者, 透過近年視訊串流多樣的應用(gaming, VR, 360, 4K…), 加上國際區域網路基礎建設不同的串流挑戰, 標準方面的 HEVC, 到 AV1 以及正在制定的 VVC 來回應一些 “Is Video Coding Dead?” 的疑問, 並且認為未來的發展趨勢方向會有所因應與改變. 目前 video coding 的工具多半為 block-based, 未來需要導入一些 computer vision / graphics 甚至 machine learning 的做法來因應. (像是在 AV1 的 inter prediction 中就導入 block-based region warping.) 而對於 video coding 而言重點還是回歸在於工具的有效性, 可控制性與計算複雜度.
而似乎只要在一個領域一段時間, 就可以聽到 XX 已死的論調, 有時候只能微笑不表示意見帶過. 其實很清楚那只是長期發展下的一個平原時期, 直到下一個突破點被發現才又在人們眼中發光發熱(而且往往還有好事者的事後諸葛與非新意的評判), 其實每每都很想說費曼大師的那句”這下面的空間還大著呢!”, 享受這探索的過程吧~

以 BOLT 加速大型規模軟體

Facebook 開源了一個軟體優化工具 — BOLT
GitHub: https://github.com/facebookincubator/BOLT
BOLT 全名為 Binary Optimization and Layout Tool
 主要在於優化記憶體中的指令位置, 如此能節省 2~15% 的 CPU 執行時間. 與其它以往定位在解決 instruction starvation 問題的工具不同的地方在於: BOLT 能應用在任何 compiler (clang, gcc)產生的應用程式上.
以往在解決這問題使用的是 PGO (profile-guided optimizations) 來有效利用系統架構上的 cache. 對於手動控制上必須要清楚知道執行的頻率, 對於執行上也能難得知規律的部分與相較之下較頻繁的部分何者優先權較高.
實際情況上, PGO 有著許多本質上與實作層面上的限制. 若沒有 source code, compiler 就無法控制來自組語或三方函式庫. 而精確地獲取並應用 profile 到每一次的 compilation 也是相當困難的.
為此 Facebook 打造了 BOLT, 能與不同 compilers, 甚至與 link-time optimizations (LTO) 和 PGO/AutoFDO 優化選項搭配使用.
BOLT 被設計用來滿足3個重要需求:
* 與任何 compiler 或混合使用 compiler 所產生的 code (這裡指 binary/instruction code)相容.
 * 能夠支援不以 source code 型式存在的 code
 * 能支援手動撰寫的組語並優化其 layout
透過 heatmap 顯示使用前後的差異, 可以發現有明顯的不同:
BOLT 基於 execution profile 結果來重排函式內的 code. 如此一來函式的主體能夠基於其執行的頻率來切分. 一旦完成後, 最後一步就是產生依照 call graph prifile 的熱區優化後的 layout.

使用 ARM NEON 指令於 Qualcomm Centriq 平台上優化 JPEG 轉換

值得一讀的優化過程文
優化的對象是 jpegtran — 一個用來無損轉換 JPEG 自一個格式到另一個的工具 (主要用於旋轉 N*90度, 或是作 progressive 與 baseline 互換)
由於使用 ARM server 的緣故, 發現 processing throughput 僅僅 Intel Xeon 的約 1/4, 主因在於一些軟體並沒有對 ARM 平台做優化, 除了對原有以 SSE 優化過的 loop 使用 NEON 改寫外, 也增加使用 vqtbl4q_u8 來做查表(個人撰文介紹過), 最終可以達到超越 Xeon 上的效能
而若再考量每瓦效能, ARM server (24 images/watt)則為 Xeon server (10 images/watt)的 2.4 倍
大部分的優化工作, 都沒有捷徑:
1. 分析 workload, 做有意義的量化
2. 尋找發生問題的瓶頸, 並評估是否為理論/物理瓶頸(無解)
3. 投入時間, 導入工具或方法, 實作優化項目
4. 成果驗證與評估增益

Go重構帶來 23X 效能的過程

非常值得一讀的效能優化文章, 沒有時間可以跳到最後面的 take away
 作者由於研讀了一篇關於 good code and bad code 的指引, 由於該篇文章文章提及了”效能”這件事, 作者便開始了驚人的優化過程, 最終達到 23X 的執行效能
看似簡單且輕描淡寫的過程, 其實可以看出功力深厚, 這些做法包含了大量的專業知識, 以下為其使用的技巧
1. 確認單執行緒效能 (不能為了平行而平行, 而忽略 overhead 嚴重性)
2. 使用 slice 避免記憶體配置 (能 in memory 處理的要盡可能這麼作, 記憶體配置成本高昂)
3. 以簡單的方式取代 regex (你需要的功能比你認為的少)
4. custom trim 實作
5. batching (批次處理能有效節省單次 overhead)
6. 手動指定 worker thread 數目 (依照系統調整執行緒數目那避免 thread racing 造成的 context switch)
7. custom lexical parser 實作(強大的工具伴隨複雜的行為)
這過程使用了涵蓋大量出於程式語言, 作業系統, I/O 行為, 效能分析, 計算架構等等諸多方面的知識, 效能優化由於需要分析諸多不同層面的問題, 因此是一門需要長期耕耘各種領域的專業

Go記憶體管理 - 一個簡單範例所引發的探索

今年六月初的文章, 作者為了 Vilnius Go Meetup 所準備的分享, 蠻早就看到了這篇, 會 queue 到現在的主因在於文章末段並沒有回答開頭提出的問題: 為何一個簡單的 Go http server 範例記憶體使用會高達 380MB? 原以為近期可能會寫續章, 可惜的是等了一個半月沒有下文. 然而若細看了昨天所分享的 “Getting to Go” , 這個問題就不難推敲可能的因素, 因此就分享呼應昨日分享的 Go Garbage Collection 相關文章.
這篇內容值得推荐的地方在於, 為了弄清楚相關問題,把從 CPU 方面的 page management, OS 如何管理記憶體, 一路到 Process 內 Heap 與 Stack 的使用都做了交代. 是不錯的從做中學的範例, 另外提供了對系統整體性與層次上的了解.
文章大概在中間開始才開始進入重點, 藉由介紹 Memory Allocators (TCMalloc, ptmalloc2) 切入, 觸及到 Go Memory Allocator 對於記憶體管理上的行為, 說明 Go 是如何處理 tiny object,以及透過 mcache, mheap 與 mspan 三者與 runtime 中負責 OS 層的 mcentral 做記憶體的配置與管理.
文章末段,透過觀察了 runtime 的消耗, C 語言程式的基本用量, 系統關於 process 的資訊 (/proc, /proc/PID/maps), 以及與更簡單的 Go 程式相較.
若有稍微閱讀昨日分享的 “Getting to Go” 可能會聯想到一件事 — stacks of goroutine.使用了 Go routine 一般會有 100K+ 的 stack 的數量, 若平均一個 stack 只要有 4KB 的配置, 就可以消耗 > 400MB, 那麼要確認的就是 Go package 中 http server 是否使用了 goroutine? 查詢 Go 官方文件可以看到:
Serve accepts incoming HTTP connections on the listener l, creating a new service goroutine for each
這說明了 go http 套件中 server 實作上使用了 goroutine 來處理相關的 request.

一起 Go 吧 (Getting to Go)

副標其實是原文的標題, 一語雙關蠻有意思的, “Getting to Go”意思是 “出發吧”, 但因為裏面的 “Go” 又同時就是 Go 語言的名稱. 這是個關於 Go 語言中以 Garbage Collection 角度討論 Go 歷程的官方部落格文章.
這篇文章紀錄了作者自 2014 至今對 Go 內部 GC 的改進與一路至今的歷程. 通篇的主軸在於從 Garbage Collection 的角度來分析看待 Go 語言. 相當值得一讀!
首先作者希望大家知道的是 Go 程式通常使用著 100,000+ 以上的 stack.


這些 stack 是被 Go scheduler 所管理著, 並且總是會被 GC 在安全點時搶佔. Go scheduler 將 Go routines 混合運行在期望能對應到每個 HW thread 的 OS threads 之上.
第二件重要的事情是 Go 是個 value-oriented 的程式語言, 這點與 C 類似而與一般其他需要執行環境 (runtime) 的 reference-oriented 程式語言不同. 如此的設計也帶來外部函式介面(foreign function interface, FFI) 的好處. Go 有著快速的 FFI. 由於不可能將全系統重新以 Go 語言打造, 因此 Go 必須透過 FFI 來與存取外部系統. FFI 這一點帶來許多驚人的事情, 這同時也是讓 Go 與其他同樣有著 GC 功能的語言有著重大差異的一點.
語言特性上需要注意, Go 允許 interior pointer (指向結構內特定欄位的 pointer), 此外 Go 本身為 ahead-of-time 編譯的語言, binary 即包含環境, 並不需要 runtime 以及 JIT.
大致上了解這些後, 作者帶大家回顧 Go 在 2014 在當時的挑戰:


在 2014 年 Go 飽受 GC latency 之苦. 可以這麼說, 若在 2014 年那時 Go 無法解決 GC 帶來的 latency 問題, Go 無法取得廣泛的成功. 而在當時許多其他的語言(像是 Rust)也同樣面臨著 latency 的問題, 但為何 latency 如此地重要?
因為數字是無情的, latency 是會不斷累積的. 一個 99%等級個別 GC 的服務品質目標(Service Level Objective), 像是 99% GC latency 小於 10ms, 並無法延伸. 重要的是整個期間的 latency 或全日中使用 App 的過程體驗. 因此在應用的情況下, 99% 這樣的等級可能結果是僅有 37% 的使用過程滿足 10ms 內延遲. 而期望達到 10ms 內延遲體驗的應用, 需要的是 4個9 或者是 99.99% 等級的目標. 作者稱其為4個9的暴政.


為了根本解決問題, 目標變成將 Garbage Collector 大幅轉為 concurrent 的形式, 而其中必須將用以處理 GC 處理的三色演算(tri-color algorithm), 必須轉為可平行化方式. 早期作者已在期刊提出並證明了 Dijkstra’s algorithm 能用以多執行緒運作. 對於 GC 常造成的系統停滯 (Stop The World, STW) 問題, 作者也證明了能有效處理.


在延遲性有方法處理後, 另一個棘手的問題在於 interior pointer. 而對此採取了尺寸隔離跨度 (size-segregated span) 的方式. GC必須有效地找出物件的起始位置, 若了解物件結構欄位的大小, 可以輕易的反推出物件的起始位置. 此外 size-segregated span 有著其他的優點:
  • 低碎片化 (Low fragmentation)
  • 內部結構 (Internal structures): 由於清楚知道物件分佈與結構, 如此可以得知低成本地分配方式.
  • 速度 (Speed)
另外是在配置時針對每個 Word 配置 2-bit Mask 一方面注記用途上是 scalar 或者是 pointer, 另外也提供了除錯方面的用途.
最後是 GC Pacer, 其目的在於負責標記(marking)與配置(allocation), 並藉由每一次的 GC 過程學習去決定何時啟動下一次的 GC 機制.
文章在後半以 Twitter Server 的實例的統計圖表來說明 Go v1.4 到 v1.9 中 latency 變化, 圖表展現了這些改進在實際應用上的增進幅度. 自 300+ ms 到 ~0.5ms, latency 大幅度改善了600X 非常驚人!
後段還有談到 Failure 以及記憶體方面的趨勢, 而詳細就留給大家細細閱讀了.

2018年7月17日 星期二

OSA 開放提供 3D 影像基礎教學

先 Bookmark,
在光學計算上先進的 OSA (Optical Society) 提供了可公開下載閱覽的“Fundamentals of 3D imaging and displays: a tutorial on integral imaging, light-field, and plenoptic systems
節錄說明: “本教程是針對有興趣了解積分成像和光場系統但不同學科的學生和研究人員, 由於可能沒有足夠的光學背景知識. 我們的目的是為這些對象提供教程, 教授基本原理以及進階概念, 用以理解, 分析和實作整體成像與光場型的拍攝以及顯示系統. ”, 內容主要是關於 3D 成像方面的基礎知識的教程, 內容主要在於: 積分成像, 光場和全光系統, 內容五十餘頁, 儘管寫著是基礎知識, 然而基本所需的數學曾度相當高.

2018年7月16日 星期一

將合入 Linux 4.19 的 Virtual Kernel-Mode-Setting

Virtual Kernel-Mode-Setting (VKMS) 事實上是個 Google Summer of Code 的專案, 其目的在於能在無顯示裝置的情況下依然能透過 GPU 運作 X 或者是 Wayland. 其運作方式類似於 VGEM, 某種方面類似 VIRTIO.
從 freedesktop mailing list 中可以看到 VKMS 將合入 Linux 4.19 的訊息, 稍微思考了一下 VKMS 的好處, 或許是在於透過 VKMS 的使用可以簡化 docker 的這一類 container 來使用 GPU 加速 headless GUI 程式應用的方式. (現有方式純軟太慢, 其他不是不安全, 就是麻煩有限制)

Intel's Vulkan 簡短教程 - 使用 Vulkan* APIs 來以平行方式繪製物件

由於小弟還沒花時間學 Vulkan, 因此對於此類文章抵抗力很差
這篇是 Intel 提供的 Vulkan 的簡短教學文, 文章透過說明 UI, load module 與 rendering 三個程式組成部分來說明如何使用 Vulkan 搭配 Qt 來撰寫 3D Graphics 應用程式, 文章重點的段落為:
  • MainWindow.cpp: 為 UI 主要部分,並負責 3D model 載入與流程控制
  • Renderer.cpp: 由於 Vulkan 的複雜程度,因此最大的挑戰即為針對 VulkanWindow 的邏輯來實作 Renderer 的部分 (文中的 renderer.cpp 與 vulkanwindow.cpp 連結失效, 到此下載範例原始程式)
  • Rendering Result May Vary: 由於 CPU,GPU與系統的其他因素以及物件檔的大小將決定 single thread 和 multi-threaded 繪圖的時間. 且結果可能會各有所不同.
  • Function 1: void Renderer::startNextFrame():如何使用 Vulkan API 來載入物件.
  • Function 2: Renderer::endFrame(): 指示 Vulkan 所有的 command buffersu 已準備好來繪圖.
  • Function 3: Renderer::drawObject(): 準備將 command buffers 傳送給 GPU

微軟公開其研究用開放資料 — Open Research Data

微軟於六月下旬發表其開放研究資料庫 Microsoft Research Open Data, 其目的在於促進全球研究社群間的協作. 以一個簡單且方便的雲端服務形式提供. 這些資料代表著微軟長年拓展與研究的投入. 這些資料都用在微軟發表的研究成果上.
資料以 Microsoft Research Data License Agreement 授權釋出, 面向上涵蓋了 生物, 電腦科學, 工程, 環境科學, 資訊科學, 數學, 物理與社會科學等等.

進入最後階段的 Huge-page Swapping 功能

來源: https://lwn.net/Articles/758677/
現今的系統有著大量的記憶體, 軟體對於記憶體的需求也愈來愈高, 以 4KB-page 為單位對於系統的 MMU 與 TLB 消耗了大量的 entries. 為了避免這方面系統平台的 overhead, 通常會採用 Huge-page (在 x86 上 Huge-page 為 4MB, arm64 為 2M/1G(4KB-page) or 512M(64KB-page)), 來提高系統的效能.
然而長久以來, 管理者都避免對 Huge-page 作 swapping 的動作, 主要在於 Huge-page swapout 在目前 Linux kernel 的實作 — Transparent Huge Page(THP), 會將 Huge-page 切分為 4K-page 再做 swapout, 一方面以往硬碟的寫出效能不佳, 二來需要一次寫出大量的資料, 最後是 swapin 後是以 4KB-page 的方式回復.
隨著非揮發性記憶體在軟硬體上的進展, 改變了相關的生態, 讓 swapping 這件事情再次地變得有趣. 而 THP 也從 2016 年開始帶來了相關的變革, 這些變動來大幅地增進了效能:
1. Linux 4.13 中 THP 延遲了 Huge-page 的拆分動作, 直到 swap entries 被配置之後, kernel 才會做拆分. (因此在 DRAM 中有短暫的時間消耗了 Page-Table Entries)
2. 緊接著在 Linux 4.14 中再次將拆分的動作延遲到寫入 swap device 之後, (因此在 DRAM 中都保持著 Huge-page)
3. 目前已經實作了 Huge-page 後續管理的部份, 主要在於攔截可能會讓 Huge-page 拆分的 kernel event, 並且在 swapin 時儘可能地維持住 Huge-page 的方式. 一旦系統有著餘裕提供 DRAM Huge-page, 那麼就可以享有其帶來的好處.
依照在 patch 提交時所附上的 benchmark 的數據顯示, 未修改的 kernel 僅有 4.5% 的資料最後以 huge-page 存放. 而套用 patch 後提升為 96.6%. Inter-process interrupt 降低了 96% 此外 spinlock 等待的時間自 13.9% 降為 0.1%. Swapping 的 I/O throughput 增加超過 1000%. 通常 kernel 工作者必須很努力才能取得 1% 規模的效能增益, 如此的增進幅度是罕見的.

Nouveau NIR 已預備支援 OpenCL

儘管 Linux 平台上 Nvidia 顯卡多半採用官方推出的私有 driver/software, 然而長久以來 Nouveau 專案期望提供開源的 Nvidia GPU driver/software, 近一年之內其相當重要的工作在於導入 NIR (New Intermediate Representation) 的支援, NIR 為 Intel 所提倡並實作並導入 Mesa 的 Intermediate Language, 而 Freedreno (Snapdragon Adreno), VC4 (Broadcom VideoCore), 與 RadeonSI (AMD Radeon) 開源 GPU driver 專案都選擇採用了 NIR.
目前 Nouveau 對於 NIR 的支援尚未完備, 但 Nouveau 主要貢獻者 Karol Herbst 在加入 Red Hat 後依然持續地對 Nouveau 專案貢獻, 並提交相關程式碼, 預計會透過 Mesa/NIR 方式來支援 OpenCL. 而除了 NIR 外 配合 SPIR-V compiler 的進展最終會推動 Nvidia GPU 對 OpenCL 的開源支援.

"Always Measure One Level Deeper"一文分享與摘要

從陳官辰大大那看到很棒這篇 “Always Measure One Level Deeper“ 關於效能優化方面的文章. (不太清楚為何手機可以直接閱讀而電腦需要權限, 建議用手機閱讀此篇)
以效能量測的角度來切入軟體效能與系統觀念, 以此來討論其所具有的專業程度, 軟體開發上的重要性, 常犯的錯誤, 以及應該抱有的原則. 個人也常犯下這些錯誤, 而且許多提醒與建議都相當實用.
常犯的錯誤:
1. 相信數字 (Trusting the numbers):效能量測的程式碼如同其他程式碼般容易有問題, 但這些錯誤不容易被察覺.
2. 猜測取代量測 (Guessing instead of measuring): 有經驗性的猜測通常是正確的, 並在效能估測指引方面發揮重要作用; 然而, 工程師對性能的直覺並不可靠.
3. 表面量測 (Superficial measurements): 許多工程師僅僅量測系統的最外層可見行為, 這是必要的但卻有所不足. 這會留下許多無法回答的問題.
4. 匆促 (Haste): 對於效能評估總是沒有留下足夠的時間. 正確評測所需的時間總是被低估. 採用快速的方法總是會產生許多(對效能問題上的)錯誤.
高品質效能分析的關鍵:
1. 充份的時間 (Allow lots of time): 效能分析是一個包含混亂, 發現和改進的漫長過程. 效能分析包含了數個階段, 而每個階段可能需要幾天到幾週的時間.
2. 不要輕信電腦產生的數字 (Never trust a number generated by a computer): 消除錯誤的唯一方法是不相信每一次測量得到的數據,直到經過了仔細的驗證. 在被證明是無誤之前, 應該對測量數字保有存疑.
3. 使用直覺提出問題而非提出回答 (Use your intuition to ask questions, not to answer them): 直覺是很棒的事情. 隨著在某個領域所累積的知識和經驗, 將開始對系統的行為以及如何處理某些問題產生強烈的感受. 但是,很容易變得過度自信並認為直覺是絕對正確的.
4. 總是更深一層的量測 (Always measure one level deeper): 如果想了解特定層級的系統性能, 不僅要測量該層級但也必須更深一級地測量. 也就是說, 測量底層在更高階影響效能的因素.
文章最後提出了幾個實務上作法的建議. 值得一讀.

Self-Supervised Tracking via Video Colorization 讀後感

2018年6月底時 Google 發表了這篇標題為 ”Self-Supervised Tracking via Video Colorization” 的文章
其大意為使用了一個 CNN 來對灰階影像著色. 其方式為自參考影像(reference image)複製顏色而來. 藉由這樣的方式, NN得以在沒有監督的情況下自動地以視覺方式追踪標的. 而值得一提的是該 NN 並沒有特別以追蹤方式來訓練, 但它能夠跟踪多個標的, 在遮蔽下追蹤, 以及在變形時保持穩定, 無需任何標記的訓練數據.
這樣的 NN 有很多種應用, 在文章中提到了動態偵測, 物件互動與視訊風格套用, 文章舉例了對單色的視訊基於初始著色影響做連續自動上色. 事實上這種高可靠性提供連續畫面變化資訊的 object tracking 系統相當實用, 像是 depth map 的變化處理(錄影上套用淺景深風格), 影像特徵點對應的更新(美顏美肌的五官位置), 變動範圍擷取(監控系統, ROI extraction, 減少耗時的處理), 甚至與視訊壓縮提供更好的 ROI, motion/reference hints, 而透過 segmentation / object detection 相關的實作(很多 segmentation NN 都提供了 Visualize 的結果, 可以作為初始化來使用)甚至可以提供對於影像變化的了解(像是車用行徑路況動態的了解), 而許多單張處理上的演算法, 也可以搭配這樣的方式作變化比對與修正.

2018年7月12日 星期四

接近完成階段的 Huge Page Swapping

來源: The final step for huge-page swapping

現今的系統有著大量的記憶體, 軟體對於記憶體的需求也愈來愈高, 以 4KB-page 為單位對於系統的 MMU 與 TLB 消耗了大量的 entries. 為了避免這方面系統平台的 overhead, 通常會採用 Huge-page (在 x86 上 Huge-page 為 4MB, arm64 為 2M/1G(4KB-page) or 512M(64KB-page)), 來提高系統的效能.

然而長久以來, 管理者都避免對 Huge-page 作 swapping 的動作, 主要在於 Huge-page swapout 在目前 Linux kernel 的實作 - Transparent Huge Page(THP), 會將 Huge-page 切分為 4K-page 再做 swapout, 一方面以往硬碟的寫出效能不佳, 二來需要一次寫出大量的資料, 最後是 swapin 後是以 4KB-page 的方式回復.

隨著非揮發性記憶體在軟硬體上的進展, 改變了相關的生態, 讓 swapping 這件事情再次地變得有趣. 而 THP 也從 2016 年開始帶來了相關的變革, 這些變動來大幅地增進了效能:
  1. Linux 4.13 中 THP 延遲了 Huge-page 的拆分動作, 直到 swap entries 被配置之後, kernel 才會做拆分. (因此在 DRAM 中有短暫的時間消耗了 Page-Table Entries)
  2. 緊接著在 Linux 4.14 中再次將拆分的動作延遲到寫入 swap device 之後, (因此在 DRAM 中都保持著 Huge-page)
  3. 目前已經實作了 Huge-page 後續管理的部份, 主要在於攔截可能會讓 Huge-page 拆分的 kernel event, 並且在 swapin 時儘可能地維持住 Huge-page 的方式. 一旦系統有著餘裕提供 DRAM Huge-page, 那麼就可以享有其帶來的好處.

依照在 patch 提交時所附上的 benchmark 的數據顯示, 未修改的 kernel 僅有 4.5% 的資料最後以 huge-page 存放. 而套用 patch 後提升為 96.6%. Inter-process interrupt 降低了 96% 此外 spinlock 等待的時間自 13.9% 降為 0.1%. Swapping 的 I/O throughput 效能增加超過 1000%. 通常 kernel 工作者必須很努力才能取得 1% 規模的效能增益, 如此的增進幅度是罕見的.

2018年7月11日 星期三

應用資料快取的演變: 自 RAM 到 SSD

在 Netflix 僅服務歐美地區的時候, 實作了基於 memcachedEVCache 來提供低延遲,高可靠性的服務.除了與 AWS與 EC2良好地整合外, Netflix 也將其成為在 Github 上的開源專案.
然而隨著 Netflix 的服務範圍的拓展, 需要存放在各地區的備份與快取資料量增加為三倍. 因此需要一個兼顧快速與經濟效益的全球性方案. 由於將資料全部存放在 RAM 上是成本高昂的. 而現今基於 SSD 的儲存技術提供了快速且相較 RAM 較低的成本. Netflix 觀察與實驗後發現使用 SSD 的 throughput 與 latency 依然可以維持其服務的 SLA (Service Level Agreement).
EVCache Moneta 為 Netflix 首次嘗試使用 SSD作為資料儲存媒介. 選擇了將所有資料存放在 SSD (RocksDB) 上並將熱區資料放在 RAM (Memcached) 中. 相較於僅存放在 RAM 中, 這個方式降低了多數基於 Moneta clusters 上超過 60% 的資料.然而這樣的作法會遭遇到 throught 與 latency 的問題.
正當 Netflix 嘗試解決這問題的同時, 採用不同方式使用 NVMe 儲存裝置的 Memcached External Storage (extstore)發表了. Memcached 提供了一個稱為 extstore 的外部儲存場 (external storage shim), 其支援了將資料存放在 SSD (I2) 與 NVMe (I3). extstore 在成本與儲存裝置有著高效率而無需犧牲速度與吞吐量. 所有的 metadata (key & other metadata) 存放在 RAM 上,而實際的資料存放在快閃記憶體中.
而 Netflix 還提供了相關的使用上的 throughput 與 latency 數據. 值得一讀

開源陣營對 ARM 的回應 - www.arm-basics.com

延續上一篇文章 ARM 對 RISC-V 發起的攻擊
朋友稍早傳來一個網址 - https://www.arm-basics.com/
裏面有著針對 ARM 提出的攻擊的回覆, 簡譯如下:
  • Cost
像是 ARM 這種專屬指令集架構有著 License fee 以及 royalty 模式可能有著高達數千萬美元的支出. 此外 ARM ISA License fee 至少佔了銷售額 1%. 此外, 一整年的 ARM architecture license fee 支出就足以負擔數個 RISC-V 設計團隊. 
  • Fragmentation Risk
ARM 就碎片化他們自己的 ISAs (ARM v6/7/8, Thumb 1, Thumb2, ThumbEE, Jazelle, ARM v8, v8-M, NVIC/VIC/GICv2/3/4, multiple hypervisor variants/…, DSP/NEON/VFP/SVE).
ARM 並不允許使用者去客製, 強迫他們去買第二顆核心, 或是更聰明地轉移到 RISC-V.
  • Improvements
ARM 指令集並不允許開源開發者貢獻. 這表示你受到原始供應商和可能有的後門所支配. 這限制使得人們更難以信任你的晶片並且阻止社群免費針對你的系統無償地帶來增進.
  • Design Assurance
處理器設計的 verification 與 validation 可能佔 75% 的總設計時間. 然而讓其開源表示自願者夠參與創建你的設計, 並無償地帶來他們獨到領域的專業. 如此減少了設計成本.
擴充是選擇性的, 你能夠購買已驗證後的核心. 事實上, 8x 較少的指令以及簡單的特權架構帶來簡單的驗證過程.
  • Large, Supportive Community
一個架構良好地被活躍的社群接受是重要的, 因此能夠幫助你移植一個更加多元的軟體圈, 服務以及設計到你的處理器架構. 這保證了市場選擇, 產品品質與一個最佳到市場的時間. 專有的生態系並沒有著這樣層次的信任與開放.
RISC-V 目前弱於 ARM 現在是事實, 但是它成長的速度更快.
  • Security
Cyberthreats 表示安全性不能是選擇性的. 專有產品能是嚴重不安全的, 因為他們無法受惠於開源開發者與業界專家多年的審查, Spectre 與 Meltdown 可能會對其發生. ARM 並不關心大眾期望的安全議題. 他們長時間忽略了 ret2usr for a very long period of time, 當百萬計的 ARM 使用者暴露在這個龐大漏洞,  直到一些安全特性 (domain, PXN) 加到了 ARMv7 中. 第一個 PXN 實作是 PaX/Grsecurity 所完成, 當第一個 implementation of domain 是 PaX's UDEREF 所完成. 至今 ARM 都沒有歸功於他們. RISC-V 將有機會在一開始做正確的事情, 而那是安全子團隊以及來自 RISC-V 基金會的 Security Standing Committee 從開始就已經在進行的.

==
ARM 已於先前撤下了 riscv-basics.com 網站, 以下為重點備份截圖



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

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