作者:张富春(ahfuzhang),转载时请注明作者和引用链接,谢谢!


关于凯撒加密,具体请看:https://en.wikipedia.org/wiki/Caesar_cipher

总而言之就是玩点没什么用的小心眼,把字母的顺序变化一下。

第一版:根据业务逻辑直接实现:

void caesarEncodeV0(uint8_t* out, uint8_t* in, int len, int rot){
rot = rot % 26;
uint8_t* end = in + len;
uint32_t* line = table.Table[rot];
for (;in<end; in++, out++){
if (*in>='a' && *in<='z'){
*out = (*in - 'a' + rot)%26 + 'a';
} else if (*in>='A' && *in<='Z'){
*out = (*in - 'A' + rot)%26 + 'A';
} else {
*out = *in;
}
}
} void testCaesar(){
const char* s = "QAULi2jah2eqSD1zQAULhuG0Qs9mhOF9TDGtFAGtFqB=";
int len = strlen(s);
uint8_t* out = malloc(len+1);
int rot = 4;
caesarEncodeV0(out, s, len, rot);
out[len] = '\0';
printf("in :%s\n", s);
printf("out:%s | caesarEncodeV0\n", out);
}

第二版:使用查表法

很明显,字符间的替换,可以预先放在一个数组里,然后查表就行了。

typedef struct{
uint32_t Table[26][256]; // uint32_t 要比 uint8_t 更好,猜测是因为字节对齐的原因
} __attribute__((packed)) CaesarTable; // 预先计算替换规则后的结果
void initTable(uint32_t *table[26][256]){
for (int i=0; i<26; i++){
for (int j=0; j<256; j++){
if (j>='a' && j<='z'){
table[i][j] = (uint8_t)((j-'a'+i)%26 + 'a');
} else if (j>='A' && j<='Z'){
table[i][j] = (uint8_t)((j-'A'+i)%26 + 'A');
} else {
table[i][j] = j;
}
}
}
} void caesarEncodeV1(uint8_t* out, uint8_t* in, int len, int rot, uint32_t *table[26][256]){
rot = rot % 26;
uint8_t* end = in + len;
uint32_t* line = table[rot];
for (;in<end; in++, out++){
*out = (uint8_t)line[*in]; // 直接查表得到结果
}
} CaesarTable table; void testCaesar(){
initTable(&table.Table);
//
const char* s = "QAULi2jah2eqSD1zQAULhuG0Qs9mhOF9TDGtFAGtFqB=";
int len = strlen(s);
uint8_t* out = malloc(len+1);
int rot = 4;
caesarEncodeV1(out, s, len, rot, &table.Table);
out[len] = '\0';
printf("in :%s\n", s);
printf("out:%s | caesarEncodeV1\n", out);
}

第三版:使用 simd,一个周期内查表多次

查看文档发现,只有 avx512 指令集才能很好的支持查表操作。

void caesarEncodeSIMDV2(uint8_t* out, uint8_t* in, int len, int rot, uint32_t *table[26][256]){
rot = rot % 26;
uint32_t* line = table[rot];
const batchSize = 16;
uint8_t* end = in + len - (len&0x0f); // 每个批次处理 16 字节,不够 16 字节的尾部要单独处理
uint8_t* start = in;
for (; start<end; start += batchSize, out += batchSize){
_mm_storeu_epi8( // step5: 把 16 个 int8 存储到目的地址
out, _mm512_cvtepi32_epi8( // step4: 把 16 个 int32 的查表结果,转换成 16 个 int8
_mm512_i32gather_epi32( // step3: 把 16 个 int32 当成偏移量,在 table 开始的地址里面查询. 最后一个参数 4,表示查表中每个元素的偏移量是 4 字节
_mm512_cvtepu8_epi32( // step2: 把 16 个 int8 转换成 16 个 int32
_mm_loadu_si128(start) // step1: 以非对齐的方式,从源地址加载 16 字节
), line, 4))
);
}
end = in + len;
for (; start<end; start++, out++){
*out = (uint8_t)line[*start];
}
}

编译命令行为:

gcc -o caesar caesar.c -g -w -mavx -mavx2 -mavx512f -mavx512vl -mavx512bw -O2

最后测试的结果为:

  • 逐个字符查表:67.041 ns/op
  • avx512 查表:36.371 ns/op

【代码分享】使用 avx512 + 查表法,优化凯撒加密的更多相关文章

  1. 经典算法,yuv与rgb互转,查表法,让你的软件飞起来

    代码的运算速度取决于以下几个方面 1. 算法本身的复杂度,比如MPEG比JPEG复杂,JPEG比BMP图片的编码复杂. 2. CPU自身的速度和设计架构 3. CPU的总线带宽 4. 您自己代码的写法 ...

  2. YUV420查表法高效、无失真的转换为RGB32格式

    YUV格式有两大类:planar和packed.planar的YUV格式,先连续存储所有像素点的Y,紧接着存储所有像素点的U,随后是所有像素点的V,这里所讲述的就是这中存储格式的:packed的YUV ...

  3. C#,Java,C -循环冗余检验:CRC-16-CCITT查表法

    C#代码 using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ...

  4. 查表法计算CRC16校验值

    CRC16是单片机程序中常用的一种校验算法.依据所采用多项式的不同,得到的结果也不相同.常用的多项式有CRC-16/IBM和CRC-16/CCITT等.本文代码采用的多项式为CRC-16/IBM: X ...

  5. 【C语言学习笔记】空间换时间,查表法的经典例子!知识就是这么学到的~

    我们怎么衡量一个函数/代码块/算法的优劣呢?这需要从多个角度看待.本篇笔记我们先不考虑代码可读性.规范性.可移植性那些角度. 在我们嵌入式中,我们需要根据实际资源的情况来设计我们的代码.比如当我们能用 ...

  6. C语言:十进制进制转换为其他进制(思想:查表法)

    // //  main.c //  Hex conversion // //  Created by ma c on 15/7/22. //  Copyright (c) 2015年 bjsxt. A ...

  7. 最简单的CRC32源码-查表法

    这个算法是在逐BYTE法的基础上进行修改的,在上一篇文章里我们说过,如果不查表的话,逐BYTE法和逐BIT法没什么区别,现在我们就把这个算法写出来,注意在调用CRC校验函数前需要先调用表生成函数: u ...

  8. 嵌入式C语言查表法的项目应用

    嵌入式C实战项目开发技巧:如果对一个有规律的数组表进行位移操作 就像下面的这个表 之前写过上面这个标题的一篇文章,讲的是以位移的方式去遍历表中的数据,效率非常高,但是,如果要实现一个乱序的流水灯或者跑 ...

  9. 嵌入式C语言查表法

    转自:https://blog.csdn.net/morixinguan/article/details/51799668    作者:Engineer-Bruce_Yang 就像下面的这个表 之前写 ...

  10. RGB2GRAY 各种算法速度比较,整形乘法比查表法快!

    1.  查表法,外循环用 这种格式 :  //for(int j = 0; j != h; ++j)// for(int i = 0; i!=w;++i)//. for(int j = 0; j != ...

随机推荐

  1. Linux CentOS 8 安装DHCP服务

    DHCP 如果虚拟机没有 /etc/dhcp/dhcpd.conf 文件,这可能是因为 DHCP 服务器软件包尚未安装,或者安装后配置文件未创建. 要创建 DHCP 服务器配置文件 dhcpd.con ...

  2. Codeforces 1326A Bad Ugly Numbers (思维)

    Codeforces 1326A Bad Ugly Numbers 看完题目,第一直觉,质数肯定满足题意,再看数据范畴,\(1≤n≤10^5\), 质数线性筛仅能做到 n=7 的情况,即处理到1000 ...

  3. 【每日一题】11.黑白树 (树上DFS)

    补题链接:Here 题目描述 一棵 \(n\) 个点的有根树,\(1\) 号点为根,相邻的两个节点之间的距离为 \(1\) .树上每个节点 \(i\)对应一个值\(k[i]\).每个点都有一个颜色,初 ...

  4. 【每日一题】1. tokitsukaze and Soldier (优先队列 + 排序)

    题目链接:Here 思路:这道题很容易看出来是考察 优先队列(priority_queue) 和 sort . 对于容忍人数越高的人来说,团队人数低也更能做到: for i = 0 to n - 1: ...

  5. vue学习笔记 十二、通过计算属性获取定义的状态数据

    系列导航 vue学习笔记 一.环境搭建 vue学习笔记 二.环境搭建+项目创建 vue学习笔记 三.文件和目录结构 vue学习笔记 四.定义组件(组件基本结构) vue学习笔记 五.创建子组件实例 v ...

  6. windows不安装虚拟机如何使用Linux系统作为开发工具?

    哈喽,大家好,我是仲一.作为嵌入式开发程序员,常常需要在Linux环境下编译一些代码.安装虚拟机比较方便,但是,太占用内存了.性能不好的电脑开了一台虚拟机后,可能就干不了其他事情了.安装双系统也比较麻 ...

  7. 彻底解决 gcr、quay、DockerHub 镜像下载难题

    在使用 Docker 和 Kubernetes 时,我们经常需要访问 gcr.io 和 quay.io 镜像仓库,由于众所周知的原因,这些镜像仓库在中国都无法访问,唯一能访问的是 Docker Hub ...

  8. python进阶(8)--测试函数与类

    一.测试函数(unittest) 1.源文件方法(name_function.py): def get_formatted_name(first,last): """生成 ...

  9. 图的遍历(DFS和BFS)

    声明:图片及内容基于https://www.bilibili.com/video/BV1rp4y1Q72r?from=articleDetail 图的遍历 深度优先遍历(DFS) DFS核心是递归和栈 ...

  10. SD Host控制器微架构设计-02

    SD_clk 测试模式下,选择hclk,将扫描链中的时钟保持一致 clk_en表示可以通过软硬件关闭时钟 sd_if模块 模块中设置一些寄存器,我们可以对寄存器进行读写或者对于寄存器中的某些域段进行读 ...