Index Compression(壓縮比vs解壓效率)
主要是對倒排索引(inverted index)中的倒排列表(postings list)進行編碼壓縮。
編碼方法:
1.D-gaps:對有序編號(如docid)進行差值(d-gaps)編碼。(處理小資料需要小代碼量,處理時間短)編碼並沒有定義儲存資料的位元模式,所以他自身不節省任何空間。
2.Elias-γ Code結合了一元編碼和二進位編碼。編碼數字k需要計算兩個值:
3. Elias-δCode通過改變kd的編碼方式,將kd分解為
Kdd使用一元編碼,kdr用二進位編碼, kr仍然用二進位編碼
4. Variable Byte Code:每個位元組的低7位是位元,高位是一個決定位。編碼的最後一個位元組高位位置為1,位置為0。處理器一般是以位元組為處理單位,所以Variable ByteCode速度快,但是處理大資料壓縮比不高。
5. Golomb編碼中,整數x用兩部分來表示,商和餘數。商的計算公式為 ,餘數的計算公式為r =( q*k )-1,在這裡 k是 Golomb編碼演算法的基礎,
如果 r< p,整 數可以用 ⌊log2k⌋位來儲存 , 否則它將需要 ,在這裡P是分界點,計算方法為p=2⌊log2k⌋+1-k。
當 r< p時,Golomb編 碼用q個0,1個1,還有r的二進位表示。否則表示方法為 q個0,1個 1,以及 r + p的二進位表示。這樣,整數 9可用 k = 3編碼為00,1,11。
參數k的選擇是至關重要的。如果選擇得不好,編碼後的整數會變得非常大 ,需要很長時間來解壓 。Witten et al.(1994)認為假定倒排表 中的整數符合 Bernoulli模型, 則一列整數a的k值用 k≈0.69x平均值(a)來計算。
Williams 和Zobel描述了對Golomb編碼實施最佳化的方法,並且認為常規的對整數的Golomb編碼比Elias gamma編碼和Elias delta編碼解碼更快並且更加節省空間的。
6. BinaryInterpolative Coding二進位插入編碼用相鄰數的資訊來編碼一個單調遞增的整數數列。
如果在整數數列X1中,對於任一給定的整數xi ,前一個數xi-1和後一個數xi-2是已知的,xi的大小是在(xi-1+1, xi-2-1) 的範圍內,所需的最大位元為 log2(xi-1-xi-2-2) 。解碼時需要xi-1和xi-2的資訊,所以數列 X2是從原先的X1得到的,也就是說每個從表 X1 得到的整數都在X2中, 這樣也就可以遞迴地進行編碼
。
二進位插入編碼(BIC)利用相鄰兩個數的資訊,對單調遞增的倒排列表進行緊湊的遞迴編碼,不僅壓縮率高,而且解碼速度快;該演算法還考慮了文檔中詞出現的頻率分布,以聚類( clustering)的方式對倒排列表的壓縮效能進行最佳化,有效地提高了索引的空間效率.
7.other compression:
InvertedIndex Compression Using Word-Aligned Binary Codes -Anh V, Moffat A.2005 (基於字行的二進位編碼方式)
Performanceof Compressed Inverted List Caching In Search Engines -Zhang Jiang-Gong,LoneXiao Hui,Suel T.2008
Invertedindex compression and query processing with optimized document ordering -Yan H, Ding S, Suel T.2009
基於字行的二進位編碼方式(Word-AlignedBinary Code,WABC)對索引進行壓縮;該編碼具有位元組操作的優點,其緊湊的二進位特性不僅保證了索引的壓縮效能,而且提高了查詢時倒排列表的解碼速度.
Performance ofCompressed Inverted List Caching In Search Engines中同時考慮了搜尋引擎中的索引壓縮與索引緩衝機制,並對變位元編碼等幾種壓縮演算法的效能進行了比較分析.為了進一步提高檢索效能.
Inverted indexcompression and query processing with optimized document ordering 對倒排列表文檔標識(Identity,ID)整數集的升序特徵進行了研究,通過更加緊湊的表示方法、快速求交集演算法以及對文檔ID順序進行最佳化,提高查詢效率。
"Index Compression Using 64-Bit Words", Anh, Moffat(最近才發現的好東東,對以往的壓縮方法的概括整合。待瞭解。開源地址:http://ww2.cs.mu.oz.au/~alistair/coders-64bit/)
以及作者Alistair Moffat的相關研究:http://ww2.cs.mu.oz.au/~alistair/abstracts/
參考《Compression of inverted Indexes For FastQuery Evaluation》論文源碼地址:http://www.seg.rmit.edu.au/projects.html)
對於posting 三元組<Docid, Frequence, Position>:
1. 首先對Docid,Position經過d-gaps編碼處理。
2. 然後對posting中的3個元素採用不同的編碼方法組合。如Dvbyte-FGol-PGamma
如果選擇Elias-γ Code,Elias-δCode,VariableByte Code, Golomb進行實驗對比,則共有24種組合方案。
這裡只介紹了倒排索引中的對整型資料的編碼壓縮方法,相關的其他壓縮方法參考wikipedia:http://en.wikipedia.org/wiki/Data_compression
另外參考:1.google
group varint 無損壓縮解壓演算法的高效實現
2.Doclist壓縮方法簡介
附lemur中RVLCompress的壓縮方法代碼:(可變位元組編碼壓縮方法v-bytes coding)
/********************************************************@editor:weedge E-mail:weege@126.com@date:2011/08/30@comment:1.對int32進行壓縮,返回壓縮後的長度 2.對壓縮byte型資料進行解壓,返回解縮後的長度*******************************************************/#include "RVLCompress.hpp"#include <stdlib.h>#include <stdio.h>#define pow2_7 128#define pow2_14 16384#define pow2_21 2097152#define pow2_28 268435456#define pow2_31 2147483648U#define RVL_COMPRESS_MASK ((1<<7)-1)//0111 1111#define RVL_COMPRESS_TERMINATE_BIT (1<<7)//左移7位,1000 0000#define RVL_COMPRESS_BYTE( d, in, b ) d[b] = (char) ((in >> 7*b) & ((1<<7)-1))//取高7*N位#define RVL_COMPRESS_TERMINATE( d, in, b ) d[b] = (char) ((in >> 7*b) | (1<<7))//取低7*N位,共置高位為1///return number of bytes in resultstatic int compress_ints (int *data_ptr, unsigned char *out_ptr, int size);/// returns number of ints decompressedstatic int decompress_ints(unsigned char *data_ptr, int *out_ptr, int num_bytes);/***************************************對壓縮byte型資料進行解壓,返回解縮後的長度@editor:weedge@parameter:data_ptr:待解壓的byte型資料out_ptr: 解壓後的int32型資料num_bytes:待解壓的資料大小**********************************/int decompress_ints (unsigned char *data_ptr,int *out_ptr,int num_bytes){ unsigned char *data_end_ptr = data_ptr + num_bytes;//指向數組末尾 unsigned char *data_curr_ptr; int *out_ptr_end = out_ptr; for (data_curr_ptr=data_ptr; data_curr_ptr<data_end_ptr; out_ptr_end++) //遍曆數組中的每個char { if (*data_curr_ptr & 128) //和1000 0000進行與操作,判斷最高位是否為1{ *out_ptr_end = 127 & *data_curr_ptr; data_curr_ptr ++; } else if (*(data_curr_ptr+1) & 128) { *out_ptr_end = *data_curr_ptr | ((*(data_curr_ptr + 1) & 127) << 7); data_curr_ptr += 2; } else if (*(data_curr_ptr+2) & 128) { *out_ptr_end = *data_curr_ptr | (*(data_curr_ptr + 1) << 7) | ((*(data_curr_ptr + 2) & 127) << 14); data_curr_ptr += 3; } else if (*(data_curr_ptr+3) & 128) { *out_ptr_end = *data_curr_ptr | (*(data_curr_ptr + 1) << 7) | (*(data_curr_ptr + 2) << 14) | ((*(data_curr_ptr + 3) & 127) << 21); data_curr_ptr += 4; } else { *out_ptr_end = *data_curr_ptr | (*(data_curr_ptr + 1) << 7) | (*(data_curr_ptr + 2) << 14) | (*(data_curr_ptr + 3) << 21) | ((*(data_curr_ptr + 4) & 127) << 28); data_curr_ptr += 5; } } // for return (out_ptr_end - out_ptr);}/**********************************************************注意:該int32資料在壓縮前,進行d-gaps編碼操作,節省壓縮時間。對int32進行壓縮,返回壓縮後的長度 @parameter:data_ptr:待壓縮的int資料out_ptr: 壓縮後的byte資料size:待壓縮的資料大小**********************************/int compress_ints (int *data_ptr,unsigned char *out_ptr,int size){ int *data_end_ptr = data_ptr + size;//指向數組末尾 int *data_curr_ptr; unsigned int n; unsigned char *out_ptr_end = out_ptr; for (data_curr_ptr=data_ptr; data_curr_ptr<data_end_ptr; data_curr_ptr++) //遍曆數組中的每個int { n = (unsigned int)*data_curr_ptr; if (n < pow2_7)//小於128 *out_ptr_end++ = 128 | n;//加128,即把n的位元值的首位置1 else if (n < pow2_14) //128<= n < 16384(2(14)){ *out_ptr_end = 127 & n;//和0111 1111進行與操作,取n的低7位 *(out_ptr_end + 1) = 128 | (n >> 7);//右移7位,取n的高7位,共置高位(8位)為1 out_ptr_end += 2; } else if (n < pow2_21) { *out_ptr_end = 127 & n;//和0111 1111進行與操作,取n的低7位 *(out_ptr_end + 1) = 127 & (n >> 7);//右移7位,取n的高7位,共置高位(8位)為1 *(out_ptr_end + 2) = 128 | (n >> 14); out_ptr_end += 3; } else if (n < pow2_28) { *out_ptr_end = 127 & n; *(out_ptr_end + 1) = 127 & (n >> 7); *(out_ptr_end + 2) = 127 & (n >> 14); *(out_ptr_end + 3) = 128 | (n >> 21); out_ptr_end += 4; } else { *out_ptr_end = 127 & n; *(out_ptr_end + 1) = 127 & (n >> 7); *(out_ptr_end + 2) = 127 & (n >> 14); *(out_ptr_end + 3) = 127 & (n >> 21); *(out_ptr_end + 4) = 128 | (n >> 28); out_ptr_end += 5;#if 0 if (n >= pow2_31) { cerr << "WARNING: value exceeded int limit in compression" << endl; }#endif } } // for return (out_ptr_end - out_ptr);}int main(){/*test compress_ints*//*test decompress_ints*/return 0;}