搜索
bottom↓
回复: 39

【请教】如何在C++环境下进行高效比特流处理

[复制链接]

出200入2554汤圆

发表于 2015-6-4 18:14:22 | 显示全部楼层 |阅读模式
问题是这样的:做了一个类似时序分析仪的上位机,需要在上位机进行比特流解析,如何可以较为高效?

现在下位机传上来的数据通过辅助线程已经读出,但是是按字节存储的(8bit/Byte,高比特位优先),
由于甲方需要对比特流进行拆包解析,因而现在需要对一个8位的内存区进行按位查找包头、包尾。
在某些恶劣情况下,包头、尾可能无法与字节边界对齐,此时要求仍能正常工作。

现在硬件设计的性能如下:
平均接收速率:        512B/s,连续传输
下位机FIFO尺寸:        1KB~2KB
包头、包尾标志:        16~32bit,上位机运行时指定

在兼顾软件运行效率、开发效率的前提下,想请教下各位大侠,用何种方式比较理想?
我计划转成字串(CString)再搞,可以借用 Find、Left、Mid 之类方法处理后再输出,不知可有不妥?

阿莫论坛20周年了!感谢大家的支持与爱护!!

你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。

出0入0汤圆

发表于 2015-6-4 19:28:56 | 显示全部楼层
转换成CString肯定没有效率了
可以看看
boost::dynamic_bitset
std::bitset

出0入0汤圆

发表于 2015-6-4 23:37:37 | 显示全部楼层
使用联合体和结构体混合,按位操作数据要简单的多。

出200入2554汤圆

 楼主| 发表于 2015-6-5 10:24:13 来自手机 | 显示全部楼层
Vincent2012 发表于 2015-6-4 23:37
使用联合体和结构体混合,按位操作数据要简单的多。

由于错位情况的随机性,固定位置结构定义都不好使啊:就连判据都在频繁改动

出200入2554汤圆

 楼主| 发表于 2015-6-5 10:25:29 来自手机 | 显示全部楼层
yj_yulin 发表于 2015-6-4 19:28
转换成CString肯定没有效率了
可以看看
boost::dynamic_bitset

看看这些去,如果能实现类似数组的功能,还是可以考虑的

出0入0汤圆

发表于 2015-6-5 10:38:39 | 显示全部楼层
状态机 检测比特流  你唯一的选择

出0入0汤圆

发表于 2015-6-5 11:16:39 | 显示全部楼层
确实好惨啊 帧头还会跨越字节 够麻烦的

出0入4汤圆

发表于 2015-6-5 11:25:19 | 显示全部楼层
这种活 下位机 干完了 再按照字节传上来比较靠谱,这种活就该下位机干,硬要跑到window下干

出200入2554汤圆

 楼主| 发表于 2015-6-5 11:39:24 | 显示全部楼层
gongxd 发表于 2015-6-5 10:38
状态机 检测比特流  你唯一的选择

下位机的哥们眼瞅就要毕业撂挑子不干了,找人接替本已是万般无奈了。

现在工作都堆过来了

出200入2554汤圆

 楼主| 发表于 2015-6-5 11:41:27 | 显示全部楼层
myxiaonia 发表于 2015-6-5 11:16
确实好惨啊 帧头还会跨越字节 够麻烦的

好在之前做过一个非 8N 比特位的帧头检测(我是不是太好说话了,总干下位机的活),用 CString 感觉良好

稍后我贴出来一段代码供大家评估

出200入2554汤圆

 楼主| 发表于 2015-6-5 11:42:00 | 显示全部楼层
acmilannast 发表于 2015-6-5 11:25
这种活 下位机 干完了 再按照字节传上来比较靠谱,这种活就该下位机干,硬要跑到window下干 ...

我也觉得,就是下位机那边略不给力,BOSS这边催的太过给力

出200入2554汤圆

 楼主| 发表于 2015-6-5 12:00:34 | 显示全部楼层
在 #2 楼的启迪下测试了 bitset 类(以前居然不知道,汗...),感觉执行效率和直接数组还是很接近的。

【测试方法】
1. 生成一个长比特串,使用字节数组存储,每个单元只用1位;
2. 向比特串的随机位置填入规定好的帧头(实际情况下,步骤1、2生成的数据被压缩在收到的字节中)
3. 分别使用 bitset、CString 方法查找帧头,累加步骤 3 的查找时间
4. 步骤 1-3 循环 10000 遍,输出结果

【测试环境】
PC:三星 S3440VC
CPU:i5-3210M @ 2.50GHz
RAM:4.00GB
OS:Win8.1 (64bit)
IDE:VS2005

【测试代码】
  1. // test.cpp : 定义控制台应用程序的入口点。
  2. //

  3. #include "stdafx.h"
  4. #include "test.h"
  5. #include "bitset"

  6. #ifdef _DEBUG
  7. #define new DEBUG_NEW
  8. #endif


  9. // 唯一的应用程序对象

  10. CWinApp theApp;

  11. using namespace std;


  12. #define USE_STATIC_MEM

  13. int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
  14. {
  15.         int nRetCode = 0;

  16.         // 初始化 MFC 并在失败时显示错误
  17.         if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
  18.         {
  19.                 // TODO: 更改错误代码以符合您的需要
  20.                 _tprintf(_T("错误: MFC 初始化失败\n"));
  21.                 return 1;
  22.         }


  23.         BYTE  bufData[8192];
  24.         const BYTE bufStart[24]=
  25.         {
  26.                 1,0,1,0,0,0,1,0,
  27.                 0,1,0,0,1,1,0,0,
  28.                 0,0,1,0,1,0,0,1,
  29.         };

  30.         LARGE_INTEGER liFreq, liSt, liEd;
  31.         ::QueryPerformanceFrequency(&liFreq);

  32.         // #1: CString
  33.         int nOk1= 0;                        // 标记成功个数
  34.         LONGLONG nTm1= 0;                // 记录累计时间
  35.         DWORD nSum1= 0;                        // 所有成功位置校验和
  36.         srand(0x5a78);
  37.         for(int i=0; i<10000; i++)
  38.         {
  39.                 // 生成原始数据(0/1)
  40.                 ZeroMemory(bufData, sizeof(bufData));
  41.                 int ibufStart= rand()%(_countof(bufData)-_countof(bufStart));
  42.                 memcpy(bufData+ibufStart, bufStart, sizeof(bufStart));

  43.                 ::QueryPerformanceCounter(&liSt);

  44.                 // 生成字串及模板
  45. #ifdef USE_STATIC_MEM
  46.                 static CString szCode, szStart;
  47. #else
  48.                 CString szCode, szStart;
  49. #endif
  50.                 TCHAR * pstr;
  51.                 pstr= szCode.GetBufferSetLength(_countof(bufData));
  52.                 for(int i=0; i<_countof(bufData); i++)
  53.                 {
  54.                         pstr[i]= bufData[i]? _T('1'):_T('0');
  55.                 }
  56.                 szCode.ReleaseBuffer();
  57.                 pstr= szStart.GetBufferSetLength(_countof(bufStart));
  58.                 for(int i=0; i<_countof(bufStart); i++)
  59.                 {
  60.                         pstr[i]= bufStart[i]? _T('1'):_T('0');
  61.                 }
  62.                 szStart.ReleaseBuffer();

  63.                 // 查找起始
  64.                 int rst= szCode.Find(szStart);
  65.                 if(rst>=0)
  66.                 {
  67.                         nOk1++;
  68.                         nSum1+= rst;
  69.                 }

  70.                 ::QueryPerformanceCounter(&liEd);
  71.                 nTm1+= liEd.QuadPart - liSt.QuadPart;
  72.         }

  73.         // #2: Bitset
  74.         int nOk2= 0;                        // 标记成功个数
  75.         LONGLONG nTm2= 0;                // 记录累计时间
  76.         DWORD nSum2= 0;                        // 所有成功位置校验和
  77.         srand(0x5a78);
  78.         for(int i=0; i<10000; i++)
  79.         {
  80.                 // 生成原始数据(0/1)
  81.                 ZeroMemory(bufData, sizeof(bufData));
  82.                 int ibufStart= rand()%(_countof(bufData)-_countof(bufStart));
  83.                 memcpy(bufData+ibufStart, bufStart, sizeof(bufStart));

  84.                 ::QueryPerformanceCounter(&liSt);

  85.                 // 生成操作类模板
  86. #ifdef USE_STATIC_MEM
  87.                 static bitset<_countof(bufData)> bsCode;
  88. #else
  89.                 bitset<_countof(bufData)> bsCode;
  90. #endif
  91.                 for(int i=0; i<_countof(bufData); i++)
  92.                 {
  93.                         bsCode[i]= bufData[i]? TRUE:FALSE;
  94.                 }

  95.                 // 查找起始
  96.                 int rst, sch;
  97.                 for(rst=0; rst<_countof(bufData); rst++)
  98.                 {
  99.                         for(sch=0; sch<_countof(bufStart); sch++)
  100.                         {
  101.                                 if(rst+sch>=_countof(bufData))
  102.                                 {
  103.                                         break;
  104.                                 }
  105.                                 if(bsCode[rst+sch] != (bufStart[sch]?TRUE:FALSE))
  106.                                 {
  107.                                         break;
  108.                                 }
  109.                         }
  110.                         if(sch>=_countof(bufStart))
  111.                         {
  112.                                 break;
  113.                         }
  114.                 }
  115.                 if(rst<_countof(bufData))
  116.                 {
  117.                         nOk2++;
  118.                         nSum2+= rst;
  119.                 }

  120.                 ::QueryPerformanceCounter(&liEd);
  121.                 nTm2+= liEd.QuadPart - liSt.QuadPart;
  122.         }

  123. #ifdef USE_STATIC_MEM
  124.         printf("(static memory mode)\n\n");
  125. #else
  126.         printf("(non-static memory mode)\n\n");
  127. #endif

  128.         printf("Method #1(CString):\n");
  129.         printf("Chksum= %08X, Time=%lf ms\n", nSum1, nTm1/(liFreq.QuadPart/1e3));
  130.         printf("\n");

  131.         printf("Method #2(bitset):\n");
  132.         printf("Chksum= %08X, Time=%lf ms\n", nSum2, nTm2/(liFreq.QuadPart/1e3));
  133.         printf("\n");

  134.         system("pause");

  135.         return nRetCode;
  136. }
复制代码


附上测试项目包:


【测试结果】
分别使用了非静态局部、静态局部的核心工作类,结果截图如下:






【测试结论】
bitset、CString 执行速度相近,基本上只要不频繁申请内存,速度都差不多。
由此也大致能推测出 bitset 使用了类似数组的 CPU 寻址结构,相较于直接移位操作应该有所优化。

但由于未评估内存消耗的数量,故此留作后日慢慢测试。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2015-6-5 12:13:54 | 显示全部楼层
由于标志最大为32bit,所以最省事儿的方法是用64Bit做一个滑动窗。
数据从一端进来,按Bit移位,然后Mask/Match就行了。

约略是这么样的伪代码。

  1. uint64_t window = 0;
  2. loop {
  3.     window = window | load_uint8_t(income_data);
  4.     loop(8 times) {
  5.         window = window << 1;
  6.         if((windows & mask) == magic) {
  7.             found_flag.
  8.         }
  9.     }
  10. }
复制代码

出0入0汤圆

发表于 2015-6-5 13:09:30 来自手机 | 显示全部楼层
本帖最后由 chxaitz 于 2015-6-5 14:22 编辑

LZ 由于存在帧头跨字节的情况,推荐LZ尝试,下位机传输上来的每个细节,上位机用2个字节来存储,并将数据放在双字节的高位,在移位的时候,以双字节内部自移,然后将后一字节低位第七位的字符或到前一双字节的高位,实现位的偏移,然后再以字节为单位,比较,移位,直至匹配。
核心概念就是实现一个数组缓冲区,然后让接收到的数据在数组中不断地左移。
速度提升未知,内存减少75%。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出200入2554汤圆

 楼主| 发表于 2015-6-5 13:17:53 | 显示全部楼层
dr2001 发表于 2015-6-5 12:13
由于标志最大为32bit,所以最省事儿的方法是用64Bit做一个滑动窗。
数据从一端进来,按Bit移位,然后Mask/M ...

用 64bit 滑动窗的方法确实不错,可以借鉴之
稍后我测试下这种滑动窗的效率如何

出200入2554汤圆

 楼主| 发表于 2015-6-5 13:28:01 | 显示全部楼层
chxaitz 发表于 2015-6-5 13:09
LZ 由于存在帧头跨字节的情况,推荐LZ尝试,下位机传输上来的每个细节,上位机用2个字节来存储,并将数据放 ...

能用直接移位比较是最好的,可是我一直担心为了这效率的提升,带来代码复杂度的爆炸,进而稳定性、测试问题都来了。

其实还有更差的情况:帧头夸包!

上位机读出的这包数据尾部发现部分帧头,然后就被拆到下包数据去了....
赶脚用移位的话,再扯上夸包拼接,就是无数的全局变量扑面而来,又得记录这包数据读到哪比特了云云

出200入2554汤圆

 楼主| 发表于 2015-6-5 13:31:03 | 显示全部楼层
myxiaonia 发表于 2015-6-5 11:16
确实好惨啊 帧头还会跨越字节 够麻烦的

其实还有夸包情况......

出0入0汤圆

发表于 2015-6-5 13:38:45 | 显示全部楼层
同意 13 楼的思路

出0入0汤圆

发表于 2015-6-5 13:58:50 | 显示全部楼层
t3486784401 发表于 2015-6-5 13:28
能用直接移位比较是最好的,可是我一直担心为了这效率的提升,带来代码复杂度的爆炸,进而稳定性、测试问 ...

其实我说的和滑动窗口是一个意思,就是滑动接收,但是我的更优越,因为可以在滑动的同时接收整个包;既然跨包,那就更麻烦了,不知道一个包里面是否只有一帧,是的话,可以分级处理,底层没有包的概念,它只负责维护一个不断左移的缓冲区,然后用来识别帧头等信息,上层从这个缓冲区里面读取帧数据。

出200入2554汤圆

 楼主| 发表于 2015-6-5 14:26:36 | 显示全部楼层
chxaitz 发表于 2015-6-5 13:58
其实我说的和滑动窗口是一个意思,就是滑动接收,但是我的更优越,因为可以在滑动的同时接收整个包;既然 ...

稍后也一并尝试下吧,对于移位方法的话也可以优化:最多移位7次,完成字节错序的纠正。
但这么做的话又不能实现低层简单维护移位那么简单了。

直接移位的话,内存拷贝那是相当频繁啊,而且大多代码要自己写。
现在整个项目代码已经超过 3W 行了,这么一个解析功能就消耗太多代码,真心感觉苦逼。

总感觉这就是个蛋疼的问题,可能花了很大精力也没个好的解决方案。

出0入0汤圆

发表于 2015-6-5 16:12:50 | 显示全部楼层
本帖最后由 myxiaonia 于 2015-6-5 16:16 编辑
t3486784401 发表于 2015-6-5 13:28
能用直接移位比较是最好的,可是我一直担心为了这效率的提升,带来代码复杂度的爆炸,进而稳定性、测试问 ...


你说的这个其实还是跨越字节的问题   我之前做过在tcp流里搜索帧头。。。。那里是只考虑字节间问题,用到内存查找算法——sunny  可惜在你这里好像不适用

我在那里字节间查找就相当麻烦了  你这里还要跨字节   软件做这事性能绝对是瓶颈

包头包尾长度多大,这个也很重要,我的情况是4字节包头,导致对字节流必须按字节滑动匹配

出0入0汤圆

发表于 2015-6-5 16:40:25 | 显示全部楼层
本帖最后由 dr2001 于 2015-6-5 16:44 编辑
t3486784401 发表于 2015-6-5 13:28
能用直接移位比较是最好的,可是我一直担心为了这效率的提升,带来代码复杂度的爆炸,进而稳定性、测试问 ...


没什么复杂的。
这块的需求,实际上就是实现一个报文重组成流,包头检测,重新进行报文分割的独立模块;进来的可以是流也可以是报文,出来的就是报文或者带有Type/Length标识的无差错流。

出200入2554汤圆

 楼主| 发表于 2015-6-5 17:06:29 来自手机 | 显示全部楼层
myxiaonia 发表于 2015-6-5 16:12
你说的这个其实还是跨越字节的问题   我之前做过在tcp流里搜索帧头。。。。那里是只考虑字节间问题,用到 ...

所以才想升级到数组再处理,字串的话还可以有现成方法

至于效率,就牺牲些cpu了

出200入2554汤圆

 楼主| 发表于 2015-6-5 17:15:14 来自手机 | 显示全部楼层
dr2001 发表于 2015-6-5 16:40
没什么复杂的。
这块的需求,实际上就是实现一个报文重组成流,包头检测,重新进行报文分割的独立模块; ...

看看测试代码就知道了,单单一个查找,CString只用了一行,而其他(bitset)十几行。

可读性和维护成本牺牲不少啊。以前排序总是自己写,现在都是qsort了,感觉还是要借用下前人的。

现在测试平台(数据源)都搭好了,过阵子就可以实地跑效率了

出0入42汤圆

发表于 2015-6-5 17:59:47 | 显示全部楼层
要求有点略高。比特流问题还真没研究过。学习

出0入0汤圆

发表于 2015-6-5 19:39:27 | 显示全部楼层
一般可以参考TS流parser代码。match包头之后,后面的数字都是8bit对齐的。
没有硬件加速的情况下,效率也就那样了。

出0入0汤圆

发表于 2015-6-5 20:29:05 | 显示全部楼层
myxiaonia 发表于 2015-6-5 16:12
你说的这个其实还是跨越字节的问题   我之前做过在tcp流里搜索帧头。。。。那里是只考虑字节间问题,用到 ...

你说的是sunday算法吧?检索时的御用算法~

出0入0汤圆

发表于 2015-6-5 20:30:19 | 显示全部楼层
myxiaonia 发表于 2015-6-5 16:12
你说的这个其实还是跨越字节的问题   我之前做过在tcp流里搜索帧头。。。。那里是只考虑字节间问题,用到 ...

你说的是sunday算法吧?检索时的御用算法~

出0入0汤圆

发表于 2015-6-5 21:19:04 | 显示全部楼层
你的这些需求,处理速度不是问题,平均接收速率:512B/s,连续传输,这PC随意处理,不需要考虑运行效率

出200入2554汤圆

 楼主| 发表于 2015-6-5 23:12:06 来自手机 | 显示全部楼层
mangocity 发表于 2015-6-5 19:39
一般可以参考TS流parser代码。match包头之后,后面的数字都是8bit对齐的。
没有硬件加速的情况下,效率也就 ...

受教了,明日查查看
多谢指点啊

出200入2554汤圆

 楼主| 发表于 2015-6-5 23:13:05 来自手机 | 显示全部楼层
chxaitz 发表于 2015-6-5 20:29
你说的是sunday算法吧?检索时的御用算法~

来请教一下还是很有收获的,多谢啊

出200入2554汤圆

 楼主| 发表于 2015-6-5 23:17:13 来自手机 | 显示全部楼层
usecool 发表于 2015-6-5 21:19
你的这些需求,处理速度不是问题,平均接收速率:512B/s,连续传输,这PC随意处理,不需要考虑运行效率 ...

一语点醒,哈哈

出0入0汤圆

发表于 2015-6-6 07:48:57 | 显示全部楼层
chxaitz 发表于 2015-6-5 20:29
你说的是sunday算法吧?检索时的御用算法~

呵呵 是的  写错了 是sunnday算法

出200入2554汤圆

 楼主| 发表于 2015-6-6 11:58:08 | 显示全部楼层
本帖最后由 t3486784401 于 2015-6-6 12:00 编辑
chxaitz 发表于 2015-6-5 13:09
LZ 由于存在帧头跨字节的情况,推荐LZ尝试,下位机传输上来的每个细节,上位机用2个字节来存储,并将数据放 ...


为了不辜负 14楼 一片好心,决定测试全局移位的工作效率。

思路大致为:仍采取前续 CString、bitset 测试用例,这次转换为 WORD 数组后再次实验查找效率。

算法描述为:最多移位8次,每次移位后在整个缓冲区内按字节匹配,找到匹配后即发现帧头。
(如果不加 “最多移位8次” 限定,即只在固定位置进行匹配检测,等待帧头移动到此,也能工作,但效率奇低)

由于每种方式所需存储结构略有差别,因而转换存储结构可能消耗一定时间。这次测试分别评估了包含与不包含转换的结果。

【测试结果】

包含转换时间的测试截图:


不包含转换时间的测试截图:


另外测试过程中发现如下现象:
1. 全局移位非常耗时,14L方式如果不加 “最多8次移位” 限定优化,工作时间超过 2min(相较于现在的~200ms);
2. 转换过程中的移位也非常耗时,如果注释掉这些代码,运行时间可以显著压缩(当然结果校验也是错误的)。

【测试结论】

1. 全局移位的方式可以按字节存储,因而在比特流较长情况下有着一定的压缩性能,不过这只有 KB 级的数据是否值得压缩有待商榷;
2. 全局移位在优化后,性能上类似比特流扩展为数组后再查找(纯运行时间在相近量级),但连续移位不可取;
3. 各种方式的编码转换时间较长(消耗在移位运算上),因而应尽可能避免此类转换。

【测试代码】

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2015-6-6 23:28:09 | 显示全部楼层
本帖最后由 chxaitz 于 2015-6-7 02:31 编辑
t3486784401 发表于 2015-6-6 11:58
为了不辜负 14楼 一片好心,决定测试全局移位的工作效率。

思路大致为:仍采取前续 CString、bitset 测 ...


我下了一个VC6,发现LZ应该是VS的。下边是我的代码,定义了一个8000bit的比特流,定义了最极端的条件下,最后8个单位,即其中有64个bit是要匹配的数据,采用不断移位的方法。
电脑用的是I5 3337基频1.8GHz,但具体频率无法参考,LZ可以自行测试。在这里测试出来平均的时间是16ms至46ms之间,应该主要是memcmp()函数的问题。但仍远远小于LZ所说的2min。。。,下面是代码和图片。
  1. #include "stdio.h"
  2. #include "string.h"
  3. #include "windows.h"

  4. typedef union{
  5.         unsigned char  u8[2];        //0:u8_l 1:u8_h
  6.         unsigned short u16;
  7.         } data_t;
  8.        
  9. data_t  x_data_buf[1000];
  10. data_t  x_data_cmp[8]={0};
  11. size_t bit_len=0;        //缓冲区中bit流的长度,而不是Byte的长度

  12. //一个输入数据的中断程序,将新的数据流插入当前缓冲区的后面
  13. void data_add_irq( char *data, size_t len )
  14. {
  15.         int i;
  16.         data_t  x_data_tmp[100];
  17.         size_t byte_seek;
  18.         size_t bit_offset;
  19.         byte_seek = (bit_len+7)/8;
  20.         bit_offset = bit_len%8;
  21.         for( i=0; i<len; i++ )
  22.         {
  23.                 x_data_tmp[i].u8[0] = data[i];
  24.                 x_data_tmp[i].u16 <<= bit_offset;        //右移
  25.                 x_data_tmp[i+1].u8[0] |= x_data_tmp[i].u8[1];//原数据先自相或(相当于统一右移了)
  26.         }
  27.         x_data_buf[byte_seek].u8[1] |= x_data_tmp[0].u8[0];//第一个相或存放
  28.         for( i=1; i<len; i++ )
  29.                 x_data_buf[byte_seek+i].u8[1] = x_data_tmp[i].u8[0];        //其余直接拷贝

  30.         bit_len += 8*len;
  31. }

  32. void main()
  33. {
  34.         bool b_finded = false;
  35.         int i;
  36.         size_t data_byte_len;
  37.         size_t bit_search_count;
  38.         size_t bit_cmp_count;                //尝试匹配的次数
  39.         long t_start,t_end;
  40.         x_data_buf[0].u16=0x55aa;
  41.         /* 先初始化一些测试数据*/
  42.         bit_len = 8 * 1000;                //定义1个8000b的比特流
  43.         x_data_buf[992].u8[1] = 0xff;
  44.         x_data_buf[993].u8[1] = 0x02;
  45.         x_data_buf[994].u8[1] = 0x03;
  46.         x_data_buf[995].u8[1] = 0x04;
  47.         x_data_buf[996].u8[1] = 0x05;
  48.         x_data_buf[997].u8[1] = 0x06;
  49.         x_data_buf[998].u8[1] = 0x07;
  50.         x_data_buf[999].u8[1] = 0x08;

  51.         x_data_cmp[0].u8[1] = 0xff;
  52.         x_data_cmp[1].u8[1] = 0x02;
  53.         x_data_cmp[2].u8[1] = 0x03;
  54.         x_data_cmp[3].u8[1] = 0x04;
  55.         x_data_cmp[4].u8[1] = 0x05;
  56.         x_data_cmp[5].u8[1] = 0x06;
  57.         x_data_cmp[6].u8[1] = 0x07;
  58.         x_data_cmp[7].u8[1] = 0x08;

  59.         /* 其它位置生成随机数 */
  60. //        for( i=0; i<992; i++ )
  61. //                x_data_buf[i].u8[1] = rand()%0xffff;
  62.         for( i=0; i<992; i++ )
  63.                 x_data_buf[i].u8[1] = 0xff;
  64.         /* 下面开始测试*/
  65.         bit_search_count = 0;
  66.         bit_cmp_count    = 0;
  67.         t_start = GetTickCount();
  68.         while( bit_len )
  69.         {

  70.                 //先开始匹配(先自己匹配第一个,符合条件调用函数匹配)
  71.                 if( x_data_buf[0].u16 == x_data_cmp[0].u16 )
  72.                 {
  73.                         bit_cmp_count++;
  74.                         if( 0 == memcmp( x_data_buf, x_data_cmp, 8*sizeof(data_t) ) )
  75.                         {
  76.                                 /* 匹配完成的相关标记 */
  77.                                 t_end = GetTickCount();
  78.                                 printf("共用时%dms,共尝试匹配%d次,找到位置在%d。\r\n",t_end-t_start,bit_cmp_count,bit_search_count);
  79.                                 b_finded = true;
  80.                                 break;
  81.                         }
  82.                 }

  83.                 //匹配不到就移位
  84.                 bit_search_count++;                //累加移位次数
  85.                 data_byte_len = (bit_len+7)/8;
  86.                 //向左移位
  87.                 for( i=0; i<data_byte_len; i++ )
  88.                         x_data_buf[i].u16 = x_data_buf[i].u16>>1;        //相当于全部除2
  89.                 //高地址的地位与低地址的高位相或
  90.                 for( i=0; i<data_byte_len-1; i++ )
  91.                 {
  92.                         x_data_buf[i].u8[1] |= x_data_buf[i+1].u8[0];        //相或
  93.                         x_data_buf[i].u8[0] = 0;                                                                                        //低位再清零
  94.                 }
  95.                 x_data_buf[data_byte_len].u8[0] = 0;                                        //最后一个有效双字节低位再清零
  96.                

  97.                 bit_len--;
  98.         }
  99.         if( b_finded == false )
  100.         {
  101.                 t_end = GetTickCount();
  102.                 printf("共用时%dms,共尝试匹配%d次,但是没找到。\r\n",t_end-t_start,bit_cmp_count);
  103.         }
  104.         getchar();
  105. }
复制代码


补充:
LZ,后面我又做了最最极端的测试,代码已更新,就是将前面所有随机数换成0xff,将待匹配的第一个字符也换成0xff,这样即每次移动,就要匹配一次,结果如下图,但是发现时间还是不稳定,只是相对差距更大了,应该还是和主频有关,还顺便测量了,第一次,也就是1000个单位各移1次时的总共耗时,但是定时器精度太低,无法测量。
x_data_buf[992].u8[1] = 0xff;
x_data_cmp[0].u8[1] = 0xff;
        /* 其它位置生成随机数 */
//        for( i=0; i<992; i++ )
//                x_data_buf.u8[1] = rand()%0xffff;
        for( i=0; i<992; i++ )
                x_data_buf.u8[1] = 0xff;

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2015-6-7 00:07:29 来自手机 | 显示全部楼层
本帖最后由 chxaitz 于 2015-6-7 00:20 编辑
t3486784401 发表于 2015-6-6 11:58
为了不辜负 14楼 一片好心,决定测试全局移位的工作效率。

思路大致为:仍采取前续 CString、bitset 测 ...


回答一下LZ的总结。
1、经LZ你提醒,如果不在意空间的话,也可以通过自己构造一个足够大的环形缓冲区来解决,每一个字节表示0或1,数据的添加和移除通过入栈和出栈来解决,这样效率会更高,因为它没有实际的移位操作,只有指针的移动。
2、这个其实可以不存在类型的重新转换,因为可以用联合共用体,LZ看我的代码就可以了。

出200入2554汤圆

 楼主| 发表于 2015-6-7 19:42:37 来自手机 | 显示全部楼层
chxaitz 发表于 2015-6-6 23:28
我下了一个VC6,发现LZ应该是VS的。下边是我的代码,定义了一个8000bit的比特流,定义了最极端的条件下, ...

过了下代码,忘提醒了,我所有时间都是重复10000次查找的时间总和。
所以如果单次几十毫秒的话,10000次就几分钟了。
那些前边那些算法平均一次才几十微秒

出200入2554汤圆

 楼主| 发表于 2015-6-7 19:44:30 来自手机 | 显示全部楼层
chxaitz 发表于 2015-6-7 00:07
回答一下LZ的总结。
1、经LZ你提醒,如果不在意空间的话,也可以通过自己构造一个足够大的环形缓冲区来解 ...

另外计时用QueryPerformanceCounter/Frequency
吧,比那个毫秒的时钟准多了

出0入0汤圆

发表于 2015-6-7 21:23:12 | 显示全部楼层
本帖最后由 chxaitz 于 2015-6-7 21:38 编辑
t3486784401 发表于 2015-6-7 19:44
另外计时用QueryPerformanceCounter/Frequency
吧,比那个毫秒的时钟准多了


好吧,因为没有看你的代码,我也在纳闷,为什么会需要那么长时间,怕是你实现的有问题,所以自己实现了一下,那既然这样,那就没什么问题了。
不过我还是应该说一下,你说类型转换会耗费大量时间这个事情,因为它不存在类型转换,如果你看了我的代码,你就知道,所主要的时间还是花在移位上,“ArrSft[j] |= (ArrSft[j+1]>>8);”,右移应该也不是它耗费太多时间的原因,这样看,确实,就像你说的,还是最多移动8次,每次移动之后,就从前向后进行匹配,来寻找帧头这样的方式比较好。
现在感觉你用的那个dynamic_bitset挺好的,我都是用的C语言,我给你说的是我现在在单片机上用的数组整体移位的方法,现在看来,C++里这个二进制类库的性能应该更好~o(∩_∩)o 哈哈
并且你用的这个计算时间的方式,这果然是得到精确时间的好东西,我也是学习了,并且既然是循环10000遍,那这样的性能,我自己感觉是很满意了,o(∩_∩)o 哈哈,愿LZ顺利。

出200入2554汤圆

 楼主| 发表于 2015-6-8 09:28:12 | 显示全部楼层
chxaitz 发表于 2015-6-7 21:23
好吧,因为没有看你的代码,我也在纳闷,为什么会需要那么长时间,怕是你实现的有问题,所以自己实现了一 ...

所以说有好物果断拿来分享,感谢各位捧场啊 :)
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-10-3 02:12

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表