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) 通过手动输入参数进行初始化。
一、文件的组成
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) 通过手动输入参数进行初始化。