2019独角兽企业重金招聘Python工程师标准>>>
在论坛上有人提出,可以考虑用空间换取时间的方式来优化S盒置换,使用8位S盒,4字节置换需要进行4次查表,哪怕是S盒数组为了内存对齐而采用64位数据宽度,其本质仍旧是8位S盒数组,数据单元总数依旧是256个,对于本文所涉及的课题,理论上可以用16位甚至是32位S盒数组,前者进行4字节置换仅需2次查表操作,后者则一步到位,1次差表就完成了4字节置换,然而从实用角度而言,16位S盒数组是唯一可行的方案,原因很简单:32位S盒数组至少要占用 (2 ^ 32) x 4 = 16GB内存,这不但浪费内存而且会因为频繁大范围内存寻址造成CPU缓存波动。
首先是内存最少的方式,S盒数组下标范围16位,每个数据单元16位,总计占用 65536 x 2 = 128KB内存,完整代码如下:
#include
};static uint16_t s_sbox[65536];void sbox_init(void)
{uint64_t i;for(i &#61; 0; i <65536; i&#43;&#43;) {s_sbox[i] &#61; s_sbox8[i & 0xff] ^ (s_sbox8[i >> 8] <<8);}
}uint32_t sbox(uint32_t src)
{uint32_t dst;dst &#61; s_sbox[src & 0xffff] ^ (s_sbox[(src >> 16)] <<16);return(dst);
}
在原有的8位S盒数组的基础上&#xff0c;构造一个16位S盒数组&#xff0c;使用前须初始化&#xff0c;对应的测试程序代码变成&#xff1a;
#include
#include
#include
#include
#include
void sbox_init(void);int main(int argc, char *argv[])
{uint32_t i, data;sbox_init();data &#61; 0x00010203;for(i &#61; 0; i <100000000; i&#43;&#43;) {data &#61; sbox(data);}printf("data &#61; %08x\n", data);exit(EXIT_SUCCESS);
}
编译并运行&#xff0c;得到如下结果&#xff1a;
[root&#64;sxy-lenovo step4]# time ./test_sbox
data &#61; 9acd23e0real 0m0.460s
user 0m0.458s
sys 0m0.000s
相比8位S盒数组的版本&#xff0c;性能提升是明显的。
既然如此&#xff0c;可以考虑将16位S盒的数据单元大小从16位提升到64位&#xff0c;看看内存对齐对于16位S盒有何影响。只要将文件sbox.c的uint16_t数据类型改成uint64_t就行了&#xff1a;
#include
};static uint64_t s_sbox[65536];void sbox_init(void)
{uint64_t i;for(i &#61; 0; i <65536; i&#43;&#43;) {s_sbox[i] &#61; s_sbox8[i & 0xff] ^ (s_sbox8[i >> 8] <<8);}
}uint64_t sbox(uint64_t src)
{uint64_t dst;dst &#61; s_sbox[src & 0xffff] ^ (s_sbox[(src >> 16)] <<16);return(dst);
}
编译运行测试程序&#xff0c;结果是&#xff1a;
[root&#64;sxy-lenovo step5]# time ./test_sbox
data &#61; 9acd23e0real 0m0.840s
user 0m0.838s
sys 0m0.002s
这不是我想要的结果&#xff0c;这不科学&#xff01;&#xff01;&#xff01;64位数据单元可能过大了&#xff0c;改成32位再试试&#xff1a;
[root&#64;sxy-lenovo step6]# time ./test_sbox
data &#61; 9acd23e0real 0m0.530s
user 0m0.529s
sys 0m0.000s
这个结果比64位数据单元的版本强&#xff0c;依旧不如16位数据单元的版本&#xff0c;究竟是什么导致了性能劣化呢&#xff0c;答案是显而易见的&#xff1a;CPU缓存
[root&#64;sxy-lenovo step6]# cat /proc/cpuinfo
processor : 0
vendor_id : GenuineIntel
cpu family : 6
model : 37
model name : Intel(R) Core(TM) i3 CPU M 370 &#64; 2.40GHz
stepping : 5
microcode : 4
cpu MHz : 2399.000
cache size : 3072 KB
physical id : 0
siblings : 4
core id : 0
cpu cores : 2
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 11
wp : yes
运行程序的是一个老款笔记本电脑&#xff0c;其CPU的一级缓存为64KB&#xff0c;二级缓存为256KB&#xff0c;三级缓存为3MB&#xff0c;最初的8位S盒数组数据单元仅8位&#xff0c;占用了256字节&#xff0c;即便扩展到64位数据单元&#xff0c;也占用不过2KB内存&#xff0c;若使用16位S盒&#xff0c;当数据单元为16位时&#xff0c;占用128KB内存&#xff0c;当数据单元到达32位时&#xff0c;内存占用到了256KB&#xff0c;再依据测试消耗时间就可以得到初步结论&#xff1a;若S盒数组占用内存小于当前CPU的二级缓存大小时&#xff0c;性能将达到最优&#xff0c;若超出反而会变差。
到这里&#xff0c;可以明确地说&#xff1a;C语言的S盒置换优化之路走到了尽头&#xff0c;事态的发展真的是走投无路了吗&#xff1f;搞过单片机或是FPGA的都知道&#xff0c;通用CPU实施对称密码算法运算几乎无优势可言&#xff0c;反而是专用芯片无论是在成本还是在综合性能上都牢牢占据优势。造成这一现象的最主要的矛盾来自于CPU主频和内存芯片主频的巨大差异&#xff0c;大家都知道&#xff0c;限于PC平台的成本要求&#xff0c;与其采用高频内存芯片&#xff0c;还不如提高CPU缓存性能与大小更合算。解决S盒置换的终极方案之一早已在通用CPU中加入&#xff0c;以Intel为例&#xff0c;所谓的AESNI指令就是干这个的&#xff0c;使用AESNI指令进行AES算法运算能大幅度提高性能&#xff0c;里面的关键点就是抛弃了传统的S盒查表置换方式&#xff0c;直接用内置硬件计算AES算法专用的GF2^8有限域乘法和乘法逆&#xff0c;这使得AES算法在支持AESNI指令集的CPU上获得超乎想像的性能增益&#xff0c;而对于本文涉及的国密SMS4算法而言&#xff0c;AESNI是没有任何意义的&#xff0c;要想突破查表法的性能限制&#xff0c;就必须走另外的路。