一、文件的組成
GF-Complete 有以下文件:
1. gf_complete.h 是應用程序使用的的頭文件,所有的其他源程序將生成各自的 .o 文件,所有的 .o 文件將被打包爲gf_complete.a ,供應用程序使用。gf_complete.h 包含了通用的數據類型gf_t (GF(2w) 中所有值得類型)和其他運算接口;
2. gf_general.h 中包含了不同大小w 的伽羅瓦域基礎運算,盡力消除不同w 大小帶來不同數據類型運算的痛苦。文件gf_wgen.c / gf_w4.c / gf_w16.c / gf_w32.c / gf_w64.c / gf_w128.c 分別對應着w<32 / w=4,8,16,32,64,128 的基礎運算(加法,乘法,除法,乘2等);
3. gf_rand.h 和gf_rand.c 是一個隨機數生成器;
4. gf_int.h 定義了gf_w** 需要使用的一些初始化函數和其他類型;
下面幾個文件並不是該庫所必須的,主要用於測試和例子:
6. gf_mult.c / gf_div.c / gf_add.c 分別是域上的單個乘法/除法/加法工具;
7. whats_my_sse.c 是用於查看該機器支持哪些指令集,直接通過gcc 可編譯;
2. gf_method.h 和 gf_method.c 是用來幫助用戶知道自己可以使用的方法(不同處理器支持的指令集不同);
8. gf_time.c / gf_uint.c / gf_ploy.c 分別用於測試速度,單元測試和域中尋找本原多項式;
9. gf_example_1/2/3/4 分別對應着伽羅瓦域上的單個乘法和除法、一塊區域乘以一個常量、w=64 的常用運算,w=128 的常用運算測試;
二、gf_time
gf_time 是其中一個最重要的測試速度的工具,命令格式如下:
UNIX>gf_time w tests seed buffer-size iterations method
其中w 是位長,大於零小於等於32,或者爲64 和128;seed 是用於隨機數的種子;buffer-size 爲每次運算的區域的大小,iterations 爲循環次數(用於測速);最重要的兩個參數是tests 和 method,tests 指測試的選項,有以下幾種:
‘M’ 單個乘法計算
‘D’ 單個除法計算
‘I’ 單個元素轉置
‘G’ 一個隨機常量乘以一塊區域
‘0’ 零乘以一塊區域(等同於bzero())
‘1’ 一乘以一塊區域(等同於memcpy() 和 XOR)
‘2’ 二乘以一塊區域,這比一般的乘二運算要快(通過移位實現)
‘S’ 所有三種單個測試‘M’,‘D’,‘I’
‘R’ 所有四個區域測試‘G’,‘0’,‘1’,‘2’
‘A’ 所有七個測試
method 是伽羅瓦域上計算採用的技術(取決於機器支持的指令集,現有的伽羅瓦域運算方法和位長w ),通過gf_method 可以查詢到對應機器支持的方法,gf_method 有三部分組成:乘法方法,區域乘法方法和除法方法。
乘法對應有以下方法:
‘TABLE’ 乘法表
‘LOG’ 對數表
‘LOG_ZERO’ 類似於對數表
‘SHIFT’ 通過不可約多項式進行移位乘法,速度很慢,參考【3】
‘BYTWO_p’ 通過循環乘二並選擇性異或被乘數得到乘法結果,可以利用到Anvin 乘二的優化
‘BYTWO_b’ 同上
‘SPLIT’ 乘法裂表(比如LR表,或者利用SIMD 的表,詳見【1】)分別有wa 和wb 兩個參數
‘GROUP’ 使用左右組合表的方法(參考文獻【4】)
‘COMPOSITE’在一個組合域上進行運算,GF((2l)k) 在參考文獻【3】、【4】有介紹
區域乘法有以下方法:
‘-’ 使用默認方法
‘LAZY’ 區域乘法前生成查詢表
‘SSE’ 如果有SSE4 指令集,則使用
‘NOSSE’ 不使用SSE4 指令集
‘SINGLE’ 每次查詢一個元素運算的表
‘DOUBLE’每次查詢兩個元素運算的表
‘QUAD’ 每次查詢四個元素運算的表,‘SINGLE’是一般的查詢表,之所以有另外兩個表,是爲了加速較小w 的運算,達到內存使用和計算的均衡
‘STDMAP’使用標準字位對其,內存塊分割爲連續的字
‘ALTMAP’非標準字位對其,每個字分割在不同的內存塊中
‘CAUCHY’將內存分爲w 塊且僅進行Cauchy Reed-Solomon 中的異或運算【5】
除法有以下方法選擇:
‘-’ 使用默認方法
‘EUCLID’ 使用歐幾里得算法,方法慢,但允許使用乘法進行除法計算
‘MATRIX’ 將每個元素轉化爲w×w 大小的bit-matrix(像在Cauchy Reed-Solomon 中),然後轉置該矩陣得到逆元素
三、字長和選項
w = 4:
“BYTWO_b SSE -”是默認的區域運算的一半速度,幾乎和乘二一樣快,但單個運算慢;
“BYTWO_b NOSSE -”是不使用SSE4 指令最快的區域運算方法;
“TABLE QUAD -”是不使用SSE 指令,基於查詢表最快的方法;
w = 8:
默認下速度最快
w = 16:
“SPLIT 16 4 SSE,ALTMAP -”這是w = 16時最快的區域乘法,關於這部分算法可以參考【2】
“BYTWO_b SSE -” 這對於區域乘二是最快的選項,但其他乘法就慢了
w = 32:
“SPLIT 32 4 SSE,ALTMAP -” 和w = 16一樣,這個比默認方法要快
“SPLIT 8 8 - -” 單個乘法最好的選擇,但要預分配1.75MB 內存
“BYTWO_b SSE -” 乘二非常快
“SPLIT 32 8 - -” 不使用SSE4 指令集最快的區域乘法,不使用1.75MB 內存
“COMPOSITE 2 1 SPLIT 16 4 SSE,ALTMAP - ALTMAP -” 對於較大的w,組合(composite)操作是最好的方法,但對於單個乘法計算不友好
w = 64:
“SPLIT 64 4 SSE,ALTMAP -” 這是最快的區域運算,採用128個不同的查找表,單個運算慢,建議使用默認方法
“COMPOSITE 2 1 SPLIT 32 4 SSE,ALTMAP - ALTMAP -” 區域運算也很快
“BYTWO_b SSE -” 這一直是區域乘二的最快方法
w = 128:
“SPLIT 128 4 - -” 方法尚不完善,這是暫時最快的方法
“BYTWO_b - - ”最快的區域乘二
三、源碼分析
先看幾個 .h頭文件(gf_complete.h 包含於gf_general.h 和gf_int.h 中)定義了一些數據結構:
gf_general_t (gf_general.h)代表伽羅瓦域中元素類型,減少w 不同帶來不同數據類型運算的痛苦;
1: typedef union {
2: uint32_t w32;
3: uint64_t w64;
4: uint64_t w128[2];
5: } gf_general_t;
gf_internal_t (gf_int.h)代表伽羅瓦運算,包含了運算指定的類型和參數;
1: typedef struct {
2: int mult_type;
3: int region_type;
4: int divide_type;
5: int w;
6: uint64_t prim_poly;
7: int free_me;
8: int arg1;
9: int arg2;
10: gf_t *base_gf;
11: void *private;
12: } gf_internal_t;
gf_region_data (gf_int.h)代表一塊要進行區域乘法計算的內存塊;
1: typedef struct {
2: gf_t *gf;
3: void *src;
4: void *dest;
5: int bytes;
6: uint64_t val;
7: int xor;
8: int align;
9: void *s_start;
10: void *d_start;
11: void *s_top;
12: void *d_top;
13: } gf_region_data;
gf_val_32_t , gf_val_64_t , gf_val_128_t 分別作爲w = 32、64、128時伽羅瓦域中的元素類型,是uint32_t,uint64_t 和uint64_t * 的別名。
gf_func_a_b 是兩個元素乘法或除法的函數指針,對於不同w 有三種函數指針;
gf_func_a 是一個元素求逆的函數指針
gf_func_region 是一個元素乘以一個區域的函數指針
gf_t 是一個伽羅瓦域運算的結構體,某個gf_t的實例(且允許我這麼說)代表了一個伽羅瓦域的運算方法
1: typedef struct gf {
2: gf_func_a_b multiply;
3: gf_func_a_b divide;
4: gf_func_a inverse;
5: gf_region multiply_region;
6: gf_extract extract_word;
7: void *scratch;
8: } gf_t;
這些結構通過gf_free 釋放空間
但一個應用程序調用這些方法時,需要指定參數進行初始化,比如生成查找表等,這些可以通過gf_init_easy() 和gf_init_hard(),前者是後者的一個默認參數的選擇,如果手動選擇參數調用後者即可。也可以通過提供的create_gf_from_argv(&gf, w, argc, argv, 6) 通過手動輸入參數進行初始化。