GF-Complete 是一个开源、综合性的伽罗瓦运算库,相应的文章发表在FAST13 中(见参考文献【1】)。作者是大名鼎鼎的Jim Plank 教授,作为开源纠错码库Jerasure 的开发者,在这个伽罗瓦运算库中创新地采用了SSE 指令集来加速纠错码运算的瓶颈---伽罗瓦运算中的乘法运算,并采用了其他运算方法,综合得到GF-Complete。该库可在Plank 主页中下载得到,下面就GF-Complete 库做简要分析,详细可参考文档【2】。
一、文件的组成
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) 通过手动输入参数进行初始化。
参考文献:
【1】FAST13’: Screaming Fast Galois Field Arithmetic Using Intel SIMD Instructions. PDF SLIDE
【2】GF-Complete: A Comprehensive Open Source Library for Galois Field Arithmetic (version 0.1). PDF
【3】Optimizing Galois Field arithmetic for diverse processor architectures and applications. PDF
【4】Efficient Software Implementations of Large Finite Fields GF(2n) for Secure Storage Applications PDF
【5】An XOR-Based Erasure-Resilient Coding Scheme. PDF