photosfeatured2

當在Facebook移動端上瀏覽某個人的使用者資料或頁面時,首先看到的往往是圖片。這些圖片是構成Facebook體驗不可缺少的一部分,但有時候,圖片的下載與展示非常慢,在低速或行動網路中尤其如此。而在像印度這樣的發展中國家市場上,許多Facebook新使用者主要是使用2G網路。近日,Facebook工程師Brian K Cabral和Edward Kandrot撰文描述了Facebook解決這一問題的過程。

封面照片是螢幕上最顯眼的部分,但它也是載入最慢的部分之一。這主要有兩個原因:一是封面照片的大小常常達到100KB,而2G連線的傳輸速度可能只有32KB/秒;二是應用程式需要傳送兩個網路請求才能顯示封面照片。它首先向GraphQL伺服器傳送請求,獲得照片URL,然後傳送第二個網路請求,使用該URL從CDN獲取照片。第二個網路請求的延遲相當長,比第一個長許多。

為了解決上述問題,他們希望能夠由原照片生成一張200位元組大小的效果圖,然後將其作為GraphQL響應的一部分在第一次請求應答中直接返回,這樣可以省掉第二次請求,極大地縮短使用者資料和頁首的顯示延遲。當然,他們最終還是要從CDN下載完整照片並進行展示,但這可以在後臺進行。至此,問題變成如何將照片壓縮成200位元組。

他們希望照片的效果圖有一種磨砂玻璃的效果。這既有趣,又能與原始照片保持一致。磨砂玻璃效果採用高斯濾波器比較容易實現,而且圖片越模糊,解析度就越低,圖片的尺寸就越小。不過,為了提供良好的使用者體驗,解析度也不能太低。通過多次嘗試,他們得出,42×42的圖片可以達到他們想要的效果,而解析度再高一些並不會帶來更好的效果。但是,即使只顯示圖片的DC分量,每個畫素仍然需要3個位元組,那麼42x42x3就是5292位元組,遠遠超出200位元組的目標。

他們開始評估標準的壓縮技術,試圖找出一種最好的方法,將資料壓縮至200位元組。遺憾的是,只是對圖片進行熵編碼(比如zlib)的話,只能將圖片壓縮一半,仍然太大。他們還評估了其它若干非標準技術,但最終,他們決定試一下JPEG影像編碼。遺憾的是,JPEG頭本身就有幾百個位元組的大小。不過,去掉JPEG頭,編碼的資料有效負荷接近200位元組。

於是,他們開始探索,JPEG圖片是否有可能使用一個固定的頭,那樣就可以將其儲存在客戶端,而不需要傳輸。JPEG頭包含多個表。在Q值一定的情況下,量化表是不變的。通過試驗,他們發現,Q20生成的圖片可以滿足他們的要求。雖然他們的圖片不是固定尺寸,但基本上都限制在42×42以下。他們還仔細檢視了JPEG頭中的其它內容,發現只有Huffman表會隨著圖片的不同發生變化。Q值、圖片資料及圖片尺寸決定著Huffman表中的頻率值,每一項變化都會導致不同的壓縮比和有效負荷位元組數。他們在一組圖片上進行了試驗,並最終找出了一個可以作為標準的Huffman表。

雖然他們處理了大量的圖片,但總有一些該方案不適用的情況。為此,他們增加了一個版本號。如果發現任何極端情況,或者未來發現了更好的Huffman表,那麼他們就可以更新相關圖片的版本號,並將新表傳送給客戶端。最終的格式包含一個位元組的版本號、一個位元組的寬度、一個位元組的高度和大約200位元組的有效負荷。伺服器只將這一格式作為GraphQL響應的一部分傳送,然後由客戶端將JPEG體附加到預定義的JPEG頭上,生成一個普通的JPEG圖片。經過標準的JPEG解碼後,客戶端可以執行預定的高斯模糊,並拉伸其尺寸以適應視窗大小。

最終,他們獲得一種可以滿足需求的格式。在網速緩慢的情況下,這幫助他們將使用者資料和頁面載入時間縮短了30%。而在網速非常快的情況下,這可以確保使用者立即看到封面照片預覽,提升了整體體驗。