作者:张富春(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. Pytest.mark.parametrize()基本用法

    Pytest.mark.parametrize()基本用法 @pytest.mark.parametrize()基本用法 数据驱动:就是把我们测试用例的数据放到excel,yaml,csv,mysql ...

  2. 3 分钟看完 NVIDIA GPU 架构及演进

    近期随着 AI 市场的爆发式增长,作为 AI 背后技术的核心之一 GPU(图形处理器)的价格也水涨船高.GPU 在人工智能中发挥着巨大的重要,特别是在计算和数据处理方面.目前生产 GPU 主流厂商其实 ...

  3. 这应该是java最好用的orm之一了

    这应该是java最好用的orm之一了 说起orm大家肯定都不会陌生,作者是一个.net菜鸟.并且是在.net繁荣的orm圈子下成长的,所以这次给大家带来的是媲美efcore,freesql,sqlsu ...

  4. UVA540 Team Queue(双queue)

    题目大意 有一条长队,每个人均唯一属于一个组(有编号),执行给定操作序列,输出相应结果.操作如下: (假设长队q1) ENQUEUE x:标号为x的人入队,若q1中存在和x属于同一组的人,则将x插入长 ...

  5. 【每日一题】37. [JSOI2007]建筑抢修 (优先队列 + 贪心)

    补题链接:Here 算法涉及:优先队列 + 贪心 因为每一个都有截止时间,按照截止时间排序下来, 如果修复这个工程的时间+修复这个之前的总时间<=截止时间.那么就是可以在规定的时间内完成. 保证 ...

  6. <vue 基础知识 2、插值语法> v-once,v-html,v-text,v-pre,v-cloak

    代码结构 一.     Mustache 1.效果 展示如何将数据展示在页面上 2.代码 01-Mustache.html <!DOCTYPE html> <html lang=&q ...

  7. FrameWork使用TraeFik连接Grpc的坑

    背景介绍:因为公司最近使用TraeFik来代替nginx做代理服务器.导致一些老项目访问Grpc的时候直接Status(StatusCode=Unavailable, Detail="fai ...

  8. context 从入门到深入了解

    1. 前言 在 Go 语言中,上下文 context.Context 用来设置截止日期,同步信号,传递值的功能,它与 goroutine 关系密切,被用来解决 goroutine 之间 退出通知,元数 ...

  9. ElasticSearch 通过 Kibana 与 ElasticSearch-head 完成增删改查

    本文为博主原创,未经允许不得转载: 1.  安装并配置 elasticSearch ,kibana, elasticsearch-head docker 安装 ElasticSearch 和 Kiba ...

  10. Linux系列之文件和目录权限

    前言 我们知道,root用户基本上可以在系统中做任何事.其他用户有更多的限制,并且通常被收集到组中.你把有类似需求的用户放入一个被授予相关权限的组,每个成员都继承组的权限. 让我们看一下: 查看权限( ...