JSON学习笔记

您所在的位置:网站首页 sidd是什么意思 JSON学习笔记

JSON学习笔记

2023-12-14 22:14| 来源: 网络整理| 查看: 265

背景:一些JSON含有大量的空白字符(whitspace),在解析JSON时需要跳过这些空白字符。

那么如果我们只是简单的进行处理,当遇在输入流中到这四种空白字符( ,\t,\n,\r,)时,直接跳过,直至流里的字符为非空白字符,虽然很容易理解,但是会带来很多分支且每次只能处理一个字符。

为此,我们采用SSE4.2指令集当中的_mm_cmpistrm()指令对其进行优化。它可以一次对一组16个字符与另一组字符作比较,也就是说一个指令可以作最多16×16=256次比较 (补充:该函数为SIMD的应用,而SIMD是一种并行运算,一次可以执行多条指令,这也是为什么同样是比较而该函数由于逐个比较的原因)

我们用此指令将16个输入流里面的字符与四个空白字符进行比较来替代64次比较及或运算以达到优化的目的。

for (;; p += 16) { const __m128i s = _mm_load_si128((const __m128i*)p); const int r = _mm_cmpistrm(w, s,_SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); //如果r!=16,意味着存在非0字符,将指针移到相应位置返回。 if (r != 16) { return p + r; }

: _mm_cmpistrm(a,b,c): 将a与b进行对比。 c为一个常量,包含以下:指示字符是字节还是单词,要做的比较的类型,以及返回值的格式。 : _SIDD_UBYTE_OPS: 操作单位是无号字节,即16个 unsigned char。 _SIDD_CMP_EQUAL_ANY: 每次比较 s 里的字符,是否和 w 中的任意字符相等。 **SIDD_LEAST_SIGNIFICANT:**返回设置为1的最右边位的索引 _SIDD_NEGATIVE_POLARITY: 把结果反转。这里指返回值的1代表非空白字符。

即将16字节字符串 s 与空白字符串 w 进行对比,非空白字符为1,空白字符为0。返回最右边位(第一位)非空白字符位置,将指针移动到此位置并返回。

这时我们遇到了两个问题: (1)字符未对齐,若末尾调用函数而该缓冲区的分配空间小于16字节,则函数将读取其拥有的内存之外的内容,导致崩溃。 ---->解决办法: 先用普通代码处理未对齐地址,再进行读取。

const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15)& static_cast(~15));

(2)当遇到只需要解析中间代码片段然而全为空白字符时,上述解析代码for循环完毕,p指针并没有移动到末尾。 ---->解决办法: 首先我们需要一个使指针移动的函数:

inline const char* SkipWhitespace(const char* p, const char* end) { while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) ++p; return p; }

再对解析函数进行简单的变化:

inline const char* SkipWhitespace_SIMD(const char* p, const char* end) { /......相关操作....../ for (; p // 快速返回(第一个即为非空白字符,) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; /....后续检测操作../ }

以下为完整代码: (1)整体检测:

inline const char* SkipWhitespace_SIMD(const char* p) { // 快速返回(第一个即为非空白字符,) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; // 对齐处理(16字节),c const char* nextAligned = reinterpret_cast((reinterpret_cast(p) + 15)& static_cast(~15)); while (p != nextAligned) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; static const char whitespace[16] = " \n\r\t"; const __m128i w = _mm_load_si128((const __m128i*) & whitespace[0]);// _mm_load_si128()加载128位值(16字节) for (;; p += 16) { const __m128i s = _mm_load_si128((const __m128i*)p); const int r = _mm_cmpistrm(w, s,_SIDD_UBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_LEAST_SIGNIFICANT | _SIDD_NEGATIVE_POLARITY); //如果r!=16,意味着存在非0字符,将指针移到相应位置返回。 if (r != 16) { return p + r; }

(2)部分检测:

inline const char* SkipWhitespace_SIMD(const char* p, const char* end) { // 快速返回(第一个即为非空白字符,) if (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t') ++p; else return p; //中间片段处理因为有界所以无需进行对齐处理 //处理同上 static const char whitespace[16] = " \n\r\t"; const __m128i w = _mm_load_si128((const __m128i*) & whitespace[0]); for (; p while (p != end && (*p == ' ' || *p == '\n' || *p == '\r' || *p == '\t')) ++p; return p; }

总结: 我们使用指令_mm_cmpistrm()替代了将一个字符与四个空白字符逐一比对的过程,达到了优化的目的; 考虑到第一个字符即为非空白字符,所以将第一个字符单独进行检测,达到节省时间,快速返回的目的; 检测分为两种情况,整体检测(提供起始指针)和中间部分字符串检测(提供起始指针及结束指针)。整体检测需要将字符对齐后再进行检测,以免造成崩溃,部分检测需要注意全部为空白指针导致起始指针不移动的问题,需要额外调用函数。

: 本文是基于Rapidjson中解析JSON的部分代码学习总结,详细代码请参考:RapidJSON: 首页 http://miloyip.github.io/rapidjson/zh-cn/

SSE4.2常用指令介绍:SSE2的一些常用指令集介绍_人工智能_小白_努力-CSDN博客 https://blog.csdn.net/laobai1015/article/details/50886282

_mm_cmpistrm()指令解释以及决定输入字符的类型、要运行的比较以及返回值的格式说明:_mm_cmpestri | Microsoft Docs https://docs.microsoft.com/zh-cn/previous-versions/visualstudio/visual-studio-2010/bb531465(v=vs.100)?redirectedfrom=MSDN



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3