iphone 6 面世,其記憶體容量維持了 1gb 的大小。據一些分析,更大的記憶體帶來了能耗增加,是蘋果不願貿然使用大記憶體的乙個原因。如今,不僅嵌入式裝置對能耗格外關注,資料中心也開始重視能耗問題,例如嘗試基於 arm 的伺服器來降低能耗。資料中心通常也裝備許多記憶體,然而部署大量記憶體不僅**不菲,更消耗大量能源。
某次在企業儲存工程師的職位描述中看到 ssdalloc,細查了下,ssdalloc 是用 ssd 來擴充套件記憶體的一種方法,典型應用場景為類 memcache 的資料緩衝應用。採用 ssd 來擴充套件記憶體,不僅能用較低成本獲得巨大容量,更有益於減少能耗。
說到這不禁要問,直接用 ssd 做 swap 不就行了,為啥還要整乙個 ssdalloc ?答案是 ssdalloc 效能要好太多。作 swap 時,內容的讀取和改動是以頁為單位 —— 讀寫頁內一點點資料,要整頁讀取,整頁寫入 —— 不僅消耗了 ssd 讀寫頻寬,寫操作更會影響其使用壽命。
以下兩圖來自
ssdalloc 的幻燈。
圖1:列出了一些常見緩衝應用,遷移到 ssdalloc 所需代價及所獲的收益。可以看到與 swap 相比,效能提公升幅度在5倍至10倍左右。
圖2:不同型號 ssd 的吞吐量測試,可以看到與 swap 相比的效能巨大增幅。
ssdalloc 的核心是圍繞著 oop(object per page) 模型來展開的,即每個記憶體頁最多放乙個物件。對於小於頁尺寸的物件,將其置於記憶體頁頭部,其餘部分留空。大於頁尺寸的物件類似,只是佔據多個頁。通過 opp 可以借助記憶體保護機制,區分出對單個物件的讀寫,從而減少 ssd 層面的讀和寫。
使用 ssdalloc 時,僅需將 malloc 等記憶體分配/釋放函式呼叫替換成 ssdalloc 的記憶體分配/釋放函式即可。另乙個需要修改的地方與陣列有關:對於 opp 陣列,其中元素的起始位址間隔為一頁大小。
另一方面,採用 opp 模型對記憶體使用似乎極為浪費。這裡所說的浪費,分為兩方面。一是虛擬位址空間的浪費,隨著64位計算的進一步普及,這點浪費不算什麼。另一方面是物理記憶體的浪費,為解決這個問題,引入兩元件:
在實測中 page buffer 大小設定在 25mb 以下就有非常好的效能。當 page buffer 滿了以後,按照先入先出序,老的 opp 中的物件被趕出,頁中的有效資料被塞入 ram object cache。ram object cache也在記憶體中,但其物件排列是緊緻的。
從某個角度而言,ram object cache 像是「壓縮」的資料,而 page buffer 則為「解開」的資料。「解開」是為了處理資料,這裡所謂的「處理」乙個主要方面是偵測物件的讀和寫。
下圖為 ssdalloc 的乙個總覽,圖來自
ssdalloc 的幻燈。
圖3:ssdalloc 結構總覽
上圖可見,ssdalloc 分成若干層:
以下按照 ssdalloc 使用中各個環節來進一步細述。
ssdalloc 有兩類主要的記憶體管理器,一類是「池」方式的,另一類則是「合併」(coalescing)方式的。「合併」方式主要用來分配 opp 陣列,是一種 ptmalloc 風格(c 庫 malloc 也是)的記憶體管理器。大概的意思是指釋放時,和附近挨著的空閒空間合併成較大的塊頭。同時,每個物理記憶體頁頭部會放入一些元資料(例如鍊錶指標之類的),有效資料緊隨其後。
以下重點來看最為常用的「池」方式的記憶體管理器,如下圖:
圖4:ssdalloc 的「池」方式記憶體管理器示意
分配物件所需空間時,按照分配申請的大小,從對應的「池」中(freelist)分配。例如分配 0.6kb 尺寸的物件時,從對應 0.5-1kb 的「池」中取。
關於 object table:
當試圖訪問某物件發生缺頁錯誤,即該物件不在 page buffer 中,則試圖從 ram object cache 中查詢並填充對應頁。查詢過程為乙個雜湊表查詢,查詢鍵為虛擬位址。如下圖所示:
圖5:ssdalloc 處理缺頁時,首先試圖從 ram object cache 中滿足需求。圖同時還說明 page buffer 採用先入先出序來驅逐「舊」資料到 ram object cache 中。該圖據《
ssdalloc hybrid ssd/ram memory management made easy
》文中配圖微調。
當請求頁面不在 ram object cache 中時,需要從 ssd 上讀取,即定位其在 ssd 上扇區號(offset)並讀入,過程如下:
查詢 atm(address translation module)。atm 是乙個在記憶體中的平衡二叉樹,查詢發生缺頁的虛擬位址所在虛擬位址範圍(key),從而找到 otid(value)。
通過發生缺頁的虛擬位址,獲得對應物件的起始虛擬位址。再通過該位址相對虛擬位址範圍的偏移,可得對應的 oto(object table offset)。
通過 otid 得到 object table(陣列),進而通過 oto 來獲得陣列中對應項,項中內容為扇區號。
每個扇頭兩位元組存了首個在本扇區開頭的物件的偏移。由於每個物件大小、所屬 記錄到元資料中,於是進一步遍歷可以定位物件。
圖6:ssdalloc 處理缺頁時,頁面不在 ram object cache 中,此時進一步從 ssd 層取得。該圖據《
ssdalloc hybrid ssd/ram memory management made easy
》文中配圖微調。
當首次寫乙個物件時,還會將物件標記為臟,髒物件被趕出 ram object caches時,需要刷到 ssd 上。刷的過程:
至此,我們粗略看了下 ssdalloc 全貌。
幾個說明:
最後來 yy 的一點,源自對安裝 linux 時決定 swap 分割槽大小的厭惡。ssdalloc 同樣需要乙個 ssd 儲存區域來支撐,這個區域,推測起來,應該不是檔案,而是分割槽的塊裝置。這樣,又回到了這個該死的問題 —— 預留多少呢?
另一方面,ssdalloc 在塊層使用日誌結構來減少寫次數,這實際上是和 flash 友好的檔案系統(例如現在很火的 f2fs)是類似的思路。那麼,是否可以將 ssdalloc 的 ssd 相關部分結合到檔案系統中?
如此,除了**復用以外,我們再也不用擔心類似 swap 分多少合適的問題了。同時,塊的寫操作在整個檔案系統迴圈,有助於更好地平均各塊寫壽命。
用C 擴充套件PHP
前端時間寫過乙個repl的php extension,記得當時在國內的中文 上,相關的資料真的很少,今天就拋磚引玉寫一些,希望對後來者有所幫助,那些基本的東西我就不贅述了,主要談談,php exitension和 php 之間的引數傳遞問題 先說說我寫的那個php extension,因為repl ...
Flask擴充套件 Flask Migrate用法
引用官方文件的話,flask migrate是使用alembic遷移flask應用程式的sqlalchemy資料庫的擴充套件,可以通過flask命令列或者flask script擴充套件對資料庫進行操作。該擴充套件主要用於遷移資料庫。傳統操作flask資料庫一般使用sqlalchemy,表的初始化使...
用python擴充套件snmp
這段時間在做伺服器狀態監控,是借助snmp協議來實現的,這裡把snmp的安裝配置和python擴充套件snmp記錄一下,也方便我以後查閱。環境 centos 6.3 64 1.1 通過yum查詢snmp完整名稱 1.2 安裝net snmp yum install net snmp y 安裝成功,版...