前言 openssl的性能测试主要就是测试在固定时间内、不同长度数据进行算法操作的次数。最后统计每1000秒钟处理的数据量或是每秒钟进行运算的次数,可以通过执行md5/rsa512/aes-128-cbc算法的性能测试看一下输出的结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 root@keep-VirtualBox:~# /usr/bin/openssl speed md5 Doing md5 for 3s on 16 size blocks: 23848303 md5's in 2.56s Doing md5 for 3s on 64 size blocks: 13073221 md5's in 2.50s Doing md5 for 3s on 256 size blocks: 5745354 md5's in 3.00s Doing md5 for 3s on 1024 size blocks: 1927877 md5's in 3.01s Doing md5 for 3s on 8192 size blocks: 259000 md5's in 3.01s Doing md5 for 3s on 16384 size blocks: 171425 md5's in 3.98s OpenSSL 1.1.1f 31 Mar 2020 built on: Tue May 3 17:49:36 2022 UTC options:bn(64,64) rc4(8x,int) des(int) aes(partial) blowfish(ptr) compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-7zx7z2/openssl-1.1.1f=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAESNI_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2 The 'numbers' are in 1000s of bytes per second processed. type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes md5 149051.89k 334674.46k 490270.21k 655862.47k 704893.02k 705685.23k root@keep-VirtualBox:~# root@keep-VirtualBox:~# /usr/bin/openssl speed aes-128-cbc Doing aes-128 cbc for 3s on 16 size blocks: 32626951 aes-128 cbc's in 3.00s Doing aes-128 cbc for 3s on 64 size blocks: 8663154 aes-128 cbc's in 3.00s Doing aes-128 cbc for 3s on 256 size blocks: 2921912 aes-128 cbc's in 3.01s Doing aes-128 cbc for 3s on 1024 size blocks: 727405 aes-128 cbc's in 3.00s Doing aes-128 cbc for 3s on 8192 size blocks: 91505 aes-128 cbc's in 3.00s Doing aes-128 cbc for 3s on 16384 size blocks: 46708 aes-128 cbc's in 3.00s OpenSSL 1.1.1f 31 Mar 2020 built on: Tue May 3 17:49:36 2022 UTC options:bn(64,64) rc4(8x,int) des(int) aes(partial) blowfish(ptr) compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-7zx7z2/openssl-1.1.1f=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAESNI_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2 The 'numbers' are in 1000s of bytes per second processed. type 16 bytes 64 bytes 256 bytes 1024 bytes 8192 bytes 16384 bytes aes-128 cbc 174010.41k 184813.95k 248508.13k 248287.57k 249869.65k 255087.96k root@keep-VirtualBox:~# root@keep-VirtualBox:~# /usr/bin/openssl speed rsa512 Doing 512 bits private rsa's for 10s: 196101 512 bits private RSA's in 9.99s Doing 512 bits public rsa's for 10s: 3471653 512 bits public RSA's in 10.00s OpenSSL 1.1.1f 31 Mar 2020 built on: Tue May 3 17:49:36 2022 UTC options:bn(64,64) rc4(8x,int) des(int) aes(partial) blowfish(ptr) compiler: gcc -fPIC -pthread -m64 -Wa,--noexecstack -Wall -Wa,--noexecstack -g -O2 -fdebug-prefix-map=/build/openssl-7zx7z2/openssl-1.1.1f=. -fstack-protector-strong -Wformat -Werror=format-security -DOPENSSL_TLS_SECURITY_LEVEL=2 -DOPENSSL_USE_NODELETE -DL_ENDIAN -DOPENSSL_PIC -DOPENSSL_CPUID_OBJ -DOPENSSL_IA32_SSE2 -DOPENSSL_BN_ASM_MONT -DOPENSSL_BN_ASM_MONT5 -DOPENSSL_BN_ASM_GF2m -DSHA1_ASM -DSHA256_ASM -DSHA512_ASM -DKECCAK1600_ASM -DRC4_ASM -DMD5_ASM -DAESNI_ASM -DVPAES_ASM -DGHASH_ASM -DECP_NISTZ256_ASM -DX25519_ASM -DPOLY1305_ASM -DNDEBUG -Wdate-time -D_FORTIFY_SOURCE=2 sign verify sign/s verify/s rsa 512 bits 0.000051s 0.000003s 19629.7 347165.3
我们可以看到对于对称算法和摘要算法,主要关心的是每秒钟处理数据的量,对于非对称算法,主要关心的是每秒钟执行操作的次数。
代码详解 性能测试的入口函数是speed_main。
首先,初始化了所有算法用到的不同长度的key,原始的明文数据,还有一些相关的参数(如类型, 模式等)。
然后,就是解析命令行参数,设定不同的参数。比如:运行测试的时长, 并发运行数、是加密还是解密等, 最后解析要测试的算法类型。
如果是设置了并发运行数,那么就会fork多个子进程,父进程与子进程间使用管道获取数据。子进程执行完之后会把结果通过管道传递给父进程, 父进程读取所有结果再平均计算最终结果。这里每个子进程都执行一样的测试逻辑。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #ifndef NO_FORK if (multi && do_multi(multi, size_num)) goto show_res; #endif static int do_multi (int multi, int size_num) { int n; int fd[2 ]; int *fds; static char sep[] = ":" ; fds = app_malloc(sizeof (*fds) * multi, "fd buffer for do_multi" ); for (n = 0 ; n < multi; ++n) { if (pipe(fd) == -1 ) { BIO_printf(bio_err, "pipe failure\n" ); exit (1 ); } fflush(stdout ); (void )BIO_flush(bio_err); if (fork()) { close(fd[1 ]); fds[n] = fd[0 ]; } else { close(fd[0 ]); close(1 ); # then lowest-numbered unused file des is 1 if (dup(fd[1 ]) == -1 ) { # use 1 for new des BIO_printf(bio_err, "dup failed\n" ); exit (1 ); } close(fd[1 ]); mr = 1 ; usertime = 0 ; free (fds); return 0 ; } printf ("Forked child %d\n" , n); } for (n = 0 ; n < multi; ++n) { FILE *f; char buf[1024 ]; char *p; f = fdopen(fds[n], "r" ); while (fgets(buf, sizeof (buf), f)) { ... } fclose(f); } free (fds); return 1 ; }
接着后面就是循环执行算法了。
1 2 3 4 5 6 7 8 9 10 if (doit[D_MD5]) { for (testnum = 0; testnum < size_num; testnum++) { print_message(names[D_MD5], c[D_MD5][testnum], lengths[testnum], seconds.sym); Time_F(START); count = run_benchmark(async_jobs, MD5_loop, loopargs); d = Time_F(STOP); print_result(D_MD5, testnum, count, d); } }
doit数组保存的是该算法是否是要测试的,size_num表示多少种块大小的数据。在print_message中,会开启一个设置一个固定时长的信号,在一定时长之后会出发一个alarm信号。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static void print_message (const char *s, long num, int length, int tm) { #ifdef SIGALRM BIO_printf(bio_err, mr ? "+DT:%s:%d:%d\n" : "Doing %s for %ds on %d size blocks: " , s, tm, length); (void )BIO_flush(bio_err); alarm(tm); #else BIO_printf(bio_err, mr ? "+DN:%s:%ld:%d\n" : "Doing %s %ld times on %d size blocks: " , s, num, length); (void )BIO_flush(bio_err); #endif }
在之前注册了信号处理函数,设置了一个全局变量run为0
1 2 3 4 5 6 7 8 9 10 11 signal(SIGALRM, alarmed); static void alarmed (int sig) { signal(SIGALRM, alarmed); run = 0 ; }
再之后调用Time_F(START); 记录当前时间, 紧接着就开始了run_benchmark,传入md5算法操作函数MD5_loop。
1 2 3 4 5 6 7 8 static int run_benchmark (int async_jobs, int (*loop_function) (void *), loopargs_t * loopargs) { ... run = 1 ;
在run_benchmark里面设置run=1, 然后开始调用md5的算法操作。
1 2 3 4 5 6 7 8 9 10 11 12 static int MD5_loop (void *args) { loopargs_t *tempargs = *(loopargs_t **) args; unsigned char *buf = tempargs->buf; unsigned char md5[MD5_DIGEST_LENGTH]; int count; for (count = 0 ; COND(c[D_MD5][testnum]); count++) MD5(buf, lengths[testnum], md5); return count; } # define COND(unused_cond) (run && count<0x7fffffff)
MD5_loop里面就是一个循环一直执行同样的动作。直到run=0 或者count达到0x7fffffff
然后调用Time_F(STOP),获取执行的时长。
最后再汇总输出结果,这里要注意的是,如果设置了multi,那么结果是多个进程处理数据的总和,而不是平均值。
小结 openssl这个性能测试的输入数据都是固定的,然后通过在一定时长内运行算法操作,统计出运行的次数,综合对应的数据长度,计算出每秒钟运行的次数或者处理的数据量。这个测试适合不同机器间横向的对比,没有做单机纵向的对比,缺少随机数据的比较。
了解这个openssl的性能测试逻辑,主要就是为了借鉴到项目中来做性能测试。当然,后面性能也要跟openssl进行对比。所以在数据层面上,也是需要跟openssl一样的。所有的执行逻辑和最终结果的展示,最好跟openssl相差不大,可以方便的进行对比。
行动,才不会被动!
欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。
博客地址: https://fishmwei.github.io/
掘金主页: https://juejin.cn/user/2084329776486919