git://www.github.com/aklomp/base64.git
git clone http://www.github.com/aklomp/base64
$ svn co --depth empty http://www.github.com/aklomp/base64
Checked out revision 1.
$ cd repo
$ svn up trunk
This ( AVX2,NEON,aarch64/NEON,SSSE3,SSE4.1, SSE4.2, AVX ) 中的base64流编码/解码库的实现,带有SIMD和 OpenMP的加速。 它还包含用于编码/解码简单长度分隔字符串的包装函数。 这个库的目标是:
在x86上,库执行运行时特征检测。 库第一次调用时,将为机器确定适当的编码/解码例程。 然后在程序的生命周期内记住它们。 if,SSSE3,SSE4.1,,library,library,library,library,library,library,library,library,library,library,library,library,library,library,library 。
NEON支持在编译时硬编码到打开或者关闭,因为在ARM上无法使用可移植运行时特征检测。
即使你的处理器不支持SIMD指令,这是一个非常快的库。 回退例程可以以在一轮内处理 32或者 64位输入,这取决于处理器宽度的单词。 在一些 64位 机器上,64位 例程甚至比SSSE3程序还要好。
对作者的知识,在原版本的时候,这是提供vxml加速的惟一Base64库。 作者写了一篇文章,解释了一种可能的SIMD方法,用于编码/解码 Base64. 本文可以帮助找出代码正在做什么,以及为什么。
显著的功能:
最初的AVX2.NEON和 aarch64/NEON编解码器慷慨地由 Inkymail 提供,他们在它的fork 中也实现了一些额外的功能。 他们的工作正在缓慢地进入这个项目。
他自己的代码在这里是 。
OpenMP实现是由 Exalon Delft的渡轮 Toth ( @htot ) 添加的。
lib
目录包含实际库的代码。 在顶级目录中键入 make
将生成 lib/libbase64.o
和 bin/base64
。 第一个是一个单独的包含对象文件,你可以链接到自己的项目中。 第二种是独立的测试二进制代码,它与 base64
系统实用程序类似。
使用这里库所需的匹配头文件在 include/libbase64.h
中。
要只编译不带SIMD编解码器的"普通"库,请键入:
make lib/libbase64.o
可以通过指定 AVX2_CFLAGS
,NEON32_CFLAGS
,NEON64_CFLAGS
,SSSE3_CFLAGS
,SSE41_CFLAGS
,SSE42_CFLAGS
和/或者 AVX_CFLAGS
环境变量 来包含可选的SIMD编解码器。 在x86上的典型生成调用如下所示:
AVX2_CFLAGS=-mavx2 SSSE3_CFLAGS=-mssse3 SSE41_CFLAGS=-msse4.1 SSE42_CFLAGS=-msse4.2 AVX_CFLAGS=-mavx make lib/libbase64.o
要构建并包含AVX2编解码器,将 AVX2_CFLAGS
环境变量设置为一个值,在编译器中打开AVX2支持,通常是 -mavx2
。 例如:
AVX2_CFLAGS=-mavx2 make
只有在运行时特征检测显示目标机器支持AVX2时,才会使用编解码器。
要构建并包含SSSE3编解码器,将 SSSE3_CFLAGS
环境变量设置为一个值,在编译器中打开SSSE3支持,通常是 -mssse3
。 例如:
SSSE3_CFLAGS=-mssse3 make
只有在运行时特征检测显示目标机器支持SSSE3时,才会使用编解码器。
这里库包含两个NEON编解码器: 一个用于常规 32位 手臂和一个 64位 AArch64的,它有两倍的vxml寄存器,并且可以以完全的64-byte 表查找。 这些编码解码器在 48-byte 块中编码,在大量的64-byte 块中解码,因此必须用 uint32/64编解码器进行扩充,以便在较小的输入范围 !
使用 llvm/clang编译霓虹灯编解码器。 compiling编译的代码生成至少包含 Bug,并且生成的汇编代码质量低。 NEON内部是GCC已知的弱区域。 Clang做得更好。
不幸的是,在运行时,NEON支持无法从用户的( 。mrc
指令具有特权) 中移植,因此使用NEON编码器的缺省值是在编译时确定的。 但是你可以做你自己的运行时检测。 你可以以包含霓虹编码器并使它的成为默认值,然后执行运行时检查,如果没有 BASE64_FORCE_PLAIN
。
以下是你的选项:
BASE64_FORCE_PLAIN
覆盖缺省对于选项 1,根本不指定任何特定于neon的编译器标志,如下所示:
CC=clang CFLAGS="-march=armv6" make
对于选项 2,保持 CFLAGS
plain,但将 NEON32_CFLAGS
环境变量设置为一个值,该值将生成NEON支持。 例如,下面的行将构建,级别的所有代码,除了霓虹编解码器以外,它构建在tmodel中。 它也将使霓虹灯编解码器成为默认。 对于ARMv6平台,在运行时使用 BASE64_FORCE_PLAIN
标志重写默认值。 没有 touched/neon代码。
CC=clang CFLAGS="-march=armv6" NEON32_CFLAGS="-march=armv7 -mfpu=neon" make
对于选项 3,将所有内容放入 CFLAGS
并使用存根,但 非空 。NEON32_CFLAGS
。 本示例适用于 树莓派 2B V1.1,,该支持 NEON:
CC=clang CFLAGS="-march=armv7 -mtune=cortex-a7" NEON32_CFLAGS="-mfpu=neon" make
要构建和包含NEON64编解码器,通常使用 CFLAGS
定义平台并将 NEON64_CFLAGS
设置为非空存根。 ( AArch64目标具有强制NEON64支持。) 示例:
CC=clang CFLAGS="--target=aarch64-linux-gnu -march=armv8-a" NEON64_CFLAGS="" make
要启用 GCC,你需要使用 -fopenmp
构建。 这可以通过将 OPENMP
环境变量设置为 1
来实现。
例如:
OPENMP=1 make
这将让编译器定义 _OPENMP
,它又将把OpenMP优化的lib_openmp.c
包含到 lib.c
中。
默认情况下,并行线程的数目等于处理器的内核数。 在quad内核上,将检测到hyperthreading内核,但超线程不会提高性能。
要获取有关OpenMP的详细信息,请使用 OMP_DISPLAY_ENV=VERBOSE
启动程序,例如
OMP_DISPLAY_ENV=VERBOSE test/benchmark
要限制线程数量,可以使用 OMP_THREAD_LIMIT=n
启动程序,例如
OMP_THREAD_LIMIT=2 test/benchmark
启用了使用 OpenMP,SSSE3和AVX2运行基准的示例:
make clean && OPENMP=1 SSSE3_CFLAGS=-mssse3 AVX2_CFLAGS=-mavx2 make && OPENMP=1 make -C test
字符串表示为指针和长度;它们不是零终止的。 这是一个有意识的设计决定。 由于输出可能包含合法零字节,所以在解码步骤中依赖零终止是没有意义的。 在编码步骤中,返回长度将在输出中节省调用 strlen()
的开销。 如果你坚持尾部零,你可以在给定的偏移处轻松地添加它。
一些API调用采用 flags
参数。 参数可以用于强制使用特定编解码器,即使该编解码器在当前构建中不是运算。 主要是为了测试目的,这在ARM上也有用,它的中唯一的方法来实现运行时霓虹检测是可以用的。 可以使用以下常量:
BASE64_FORCE_AVX2
BASE64_FORCE_NEON32
BASE64_FORCE_NEON64
BASE64_FORCE_PLAIN
BASE64_FORCE_SSSE3
BASE64_FORCE_SSE41
BASE64_FORCE_SSE42
BASE64_FORCE_AVX
对于默认行为,将 flags
设置为 0
,这是x86上运行时特征检测,ARM上的编译时间固定编解码器和其他平台的普通编解码器。
void base64_encode ( constchar *src , size_t srclen , char *out , size_t *outlen , int flags ) ;
对给定长度的普通字符串进行编码的包装函数。 输出被写入 out
,但没有尾随零。 输出长度以字节为单位写入 outlen
。 out
中的缓冲区已经由调用方分配,且至少为 4/3的输入大小。
void base64_stream_encode_init ( struct base64_state *state , int flags ) ;
调用 base64_stream_encode()
以初始化状态之前调用这里方法。
void base64_stream_encode ( struct base64_state *state , constchar *src , size_t srclen , char *out , size_t *outlen ) ;
在 src
中将给定长度的数据块编码到 out
中的缓冲区中。 调用方负责分配足够大的缓冲区;它必须至少为 4/3的大小。 将写入的新字节数放入 outlen
( 函数启动时设置为零) 中。 不终止或者终止输出。
void base64_stream_encode_final ( struct base64_state *state , char *out , size_t *outlen ) ;
完成以前对 base64_stream_encode()
调用的输出。 如果合适,添加所需的end-of-stream标记。 outlen
被修改并且将包含在 out
( 这通常是零) 中写入的新字节数。
int base64_decode ( constchar *src , size_t srclen , char *out , size_t *outlen , int flags ) ;
用于解码给定长度的普通字符串的包装函数。 输出被写入 out
,但没有尾随零。 输出长度以字节为单位写入 outlen
。 out
中的缓冲区已经由调用方分配,且至少为 3/4的输入大小。 成功返回 1
,如果由于无效输入而发生解码错误,则返回 0
。 如果当前版本中未包含所选编解码器,则返回 -1
。
void base64_stream_decode_init ( struct base64_state *state , int flags ) ;
调用 base64_stream_decode()
以初始化状态之前调用这里方法。
int base64_stream_decode ( struct base64_state *state , constchar *src , size_t srclen , char *out , size_t *outlen ) ;
在 src
中将给定长度的数据块解码为 out
处的缓冲区。 调用方负责分配足够大的缓冲区;它必须至少为 3/4的大小。 将写入的新字节数放入 outlen
( 函数启动时设置为零) 中。 不终止输出。 如果所有内容都是好的,则返回 1,如果发现解码错误,则返回 0,如无效字符。 如果当前版本中未包含选定的编解码器,则返回 -1. 测试工具用于检查编解码器是否可以用于测试。
将 static 字符串编码为base64并将输出输出到stdout的简单示例:
#include<stdio.h>/* fwrite */#include"libbase64.h"intmain () { char src[] = "hello world"; char out[20]; size_t srclen = sizeof(src) - 1; size_t outlen; base64_encode(src, srclen, out, &outlen, 0); fwrite(out, outlen, 1, stdout); return0; }
标准输出的流编码标准输入的一个简单示例( 无错误检查等):
#include<stdio.h>#include"libbase64.h"intmain () { size_t nread, nout; char buf[12000], out[16000]; struct base64_state state; // Initialize stream encoder:base64_stream_encode_init(&state, 0); // Read contents of stdin into buffer:while ((nread = fread(buf, 1, sizeof(buf), stdin))> 0) { // Encode buffer:base64_stream_encode(&state, buf, nread, out, &nout); // If there's output, print it to stdout:if (nout) { fwrite(out, nout, 1, stdout); } // If an error occurred, exit the loop:if (feof(stdin)) { break; } } // Finalize encoding:base64_stream_encode_final(&state, out, &nout); // If the finalizing resulted in extra output bytes, print them:if (nout) { fwrite(out, nout, 1, stdout); } return0; }
有关 base64
实用工具的简单实现,请参阅 bin/base64.c
。 文件或者标准输入通过编码器/解码器输入,输出被写入标准输出。
有关小型测试套件,请参见 tests/
。 测试自动化与 Travis CI:
基准测试可以使用内置基准程序运行,如下所示:
make -C test benchmark <buildflags>&& test/benchmark
它将为所有编译的编解码器运行一个编码和解码基准。
下面的表格在随机机器上包含一些结果。 以 10MB/秒为单位,以字节表示的所有数字,舍入到最接近的整数。
需要 *: 更新
x86处理器
处理器普通 enc Plain SSSE3 SSSE3i7-4771 @ 3.5 GHz | 833 | 1111* | 3333* | 4444* | TBD | TBD | 4999* | 6666* |
i7-4770 @ 3.4 GHz DDR1600 | 1790 | 3038 | 4899 | 4043* | 4796 | 5709* | 4681 | 6386* |
i7-4770 @ 3.4 GHz DDR1600 OPENMP 1线程 | 1784 | 3041 | 4945 | 4035* | 4776 | 5719* | 4661 | 6294* |
i7-4770 @ 3.4 GHz DDR1600 OPENMP 2线程 | 3401 | 5729 | 5489 | 7444* | 5003 | 8624* | 5105 | 8558* |
i7-4770 @ 3.4 GHz DDR1600 OPENMP 4线程 | 4884 | 7099 | 4917 | 7057* | 4799 | 7143* | 4902 | 7219* |
i7-4770 @ 3.4 GHz DDR1600 OPENMP 8线程 | 5212 | 8849 | 5284 | 9099* | 5289 | 9220* | 4849 | 9200* |
i7-4870HQ @ 2.5 GHz | 1471 | 3066 | 6721 | 6962 | 7015 | 8267 | 8328 | 11576 |
i5-4590S @ 3.0 GHz | 1721 | 3004 | 4155 | 5724 | 4190 | 5999 | 4149 | 6212 |
Xeon X5570 @ 2.93 GHz | 1097 | 1048* | 2077* | 2215* | - | - | - | - |
Pentium4 @ 3.4 GHz | 528 | 448* | - | - | - | - | - | - |
Atom N270 | 112 | 208 | 447 | 417 | - | - | - | - |
AMD E-450 | 370 | 332* | 405* | 366* | - | - | - | - |
英特尔 @ 500 MHz | 79 | 92* | 152* | 172* | - | - | - | - |
英特尔 @ 500 MHz OPENMP 2线程 | 158 | 184* | 300* | 343* | - | - | - | - |
英特尔 @ 500 MHz ( x86-64 ) | 97 | 146 | 197 | 207* | - | - | - | - |
英特尔 @ 500 MHz ( x86-64 ) 2线程 | 193 | 288 | 389 | 410* | - | - | - | - |
ARM处理器
处理器普通 enc NEON32 NEON32 NEON64 NEON64 dec树莓派 B+ V1.2 | 46 | 40* | - | - | - | - |
树莓派 2 B V1.1 | 104 | 88* | 188 | 116* | - | - |
Apple iPhone传感器 | 1056 | 895 | 2943 | 2618 | - | - |
苹果iPhone传感器 | 1061 | 1239 | - | - | 4098 | 3983 |
PowerPC处理器
普通处理器普通 decPowerPC E6500 @ 1.8 GHz | 270 | 265* |
i7-4770 @ 3.4 DDR1600,带有varrying缓冲区大小的基准:
Note高速缓存大小和cache的缓存大小 is Plain faster faster AVX encoding比普通的AVX编码/解码 leading,因为线程创建开销小于 100 kB,decoding的吞吐量。 为了防止发生这种情况,lib_openmp.c
定义了 OMP_THRESHOLD 20000
,需要至少 20000字节的缓冲区来启用多线程处理。
这个存储库是 BSD 2-clause 许可协议下的许可证。 查看许可证文件。