把大神的帖子中一部分摘抄出来,结合自己写的位运算代码和循环代码(数组遍历)进行性能测试分析并给出结果。 摘自: https://www.gameres.com/827195.html

本文适用于所有脏标记遍历功能,提升性能几倍,本文以游戏中玩家的属性同步作为例子进行介绍。

先说性能分析结果:一般玩家属性列表也就120个够用了,其中常用的攻击,防御,血蓝等,不超过20个。每0.5秒同步一次,那么变化的常用属性更少。

当变化10个属性,位运算性能提升6倍,20个时4倍,如图所示:

总结:位运算效率高,并且把常用属性定义为低位效率更高。 其次是数组(原因是遍历),再其次是set(影响原因是hash函数、hash扩容、二叉树节点转换),最差是stl之bitset(原因:本质也是数组)

优化前的循环算法(数组):

 const int N = ;
class BitSet
{
public:
BitSet()
{
memset(m_szBits,,sizeof(m_szBits));
m_bBitsDirty = false;
}
int Set(size_t i)
{
if(i >= && i < N)
{
++m_szBits[i]; m_bBitsDirty = true; }
return ;
}
int Get(size_t i) const
{
if(i >= && i < N)
{
return m_szBits[i] > ;
}
return ;
}
void Clean()
{
memset(m_szBits,,sizeof(m_szBits));
m_bBitsDirty = false;
}
bool IsDirty() const
{
return m_bBitsDirty;
}
private:
char * m_szBits[N];
bool m_bBitsDirty;
};

优化后的位运算:

 const int nLLLeng = ;
class NewBitSet
{
public:
NewBitSet():llLow(0ull),llHight(0ull)
{
}
void Set(size_t i)
{
if(i > && i < nLLLeng)
{
llLow |= 1ull << i;
}
else if (i < nLLLeng * )
{
llHight |= 1ull << (i & 0x3F); // 0x3F 是64的16进制,这样做是相当于减64
}
}
void GetAllDirty(int * arrDirty, int & nMaxCount) const
{
unsigned long long nllLowTemp = llLow;
unsigned long long nllHightTemp = llHight;
nMaxCount = -;
while (nllLowTemp)
{
arrDirty[++nMaxCount] = __builtin_ffsll(nllLowTemp) - ;
nllLowTemp &= (nllLowTemp - );
}
while (nllHightTemp)
{
arrDirty[++nMaxCount] = __builtin_ffsll(nllHightTemp) - + nLLLeng;
nllHightTemp &= (nllHightTemp - );
}
}
void Clean()
{
llLow = llHight = 0ull;
} bool IsDirty() const
{
return llLow || llHight;
}
private:
unsigned long long int llLow;
unsigned long long int llHight;
};

测试用例:

 #include <iostream>
#include <sys/time.h>
#include<stdlib.h>
#include <algorithm>
#include <math.h>
#include <stdio.h>
#include<vector>
#include<map>
#include <string.h>
using namespace std;
const int N = ;
class BitSet
{
public:
BitSet()
{
memset(m_szBits,,sizeof(m_szBits));
m_bBitsDirty = false;
}
int Set(size_t i)
{
if(i >= && i < N)
{
++m_szBits[i]; m_bBitsDirty = true; }
return ;
}
int Get(size_t i) const
{
if(i >= && i < N)
{
return m_szBits[i] > ;
}
return ;
}
void Clean()
{
memset(m_szBits,,sizeof(m_szBits));
m_bBitsDirty = false;
}
bool IsDirty() const
{
return m_bBitsDirty;
}
private:
char * m_szBits[N];
bool m_bBitsDirty;
}; const int nLLLeng = ;
class NewBitSet
{
public:
NewBitSet():llLow(0ull),llHight(0ull)
{
}
void Set(size_t i)
{
if(i > && i < nLLLeng)
{
llLow |= 1ull << i;
}
else if (i < nLLLeng * )
{
llHight |= 1ull << (i & 0x3F); // 0x3F 是64的16进制,这样做是相当于减64
}
}
void GetAllDirty(int * arrDirty, int & nMaxCount) const
{
unsigned long long nllLowTemp = llLow;
unsigned long long nllHightTemp = llHight;
nMaxCount = -;
while (nllLowTemp)
{
arrDirty[++nMaxCount] = __builtin_ffsll(nllLowTemp) - ;
nllLowTemp &= (nllLowTemp - );
}
while (nllHightTemp)
{
arrDirty[++nMaxCount] = __builtin_ffsll(nllHightTemp) - + nLLLeng;
nllHightTemp &= (nllHightTemp - );
}
}
void Clean()
{
llLow = llHight = 0ull;
} bool IsDirty() const
{
return llLow || llHight;
}
private:
unsigned long long int llLow;
unsigned long long int llHight;
}; int nPrintCount = ; void DirtyBit(int nRandCount)
{
NewBitSet tDirty;
int nRand = ;
//srand((unsigned)time(0));
for (int i = ; i < nRandCount;i++)
{
// nRand = rand()%126 + 1;
tDirty.Set(i+);
}
if (!tDirty.IsDirty())
{
return;
}
int arrDirty[nLLLeng * ] = {};
//同步自己执行次数
int nMaxCount = ;
tDirty.GetAllDirty(arrDirty, nMaxCount);
nMaxCount = ;
//同步WS执行次数
tDirty.GetAllDirty(arrDirty, nMaxCount); int nNotPrint = ;
for (int i = ; i< nMaxCount; i++)
{
nNotPrint = arrDirty[i];
}
tDirty.Clean();
} void DirtyArr(int nRandCount)
{
BitSet tDirty;
int nRand = ;
// srand((unsigned)time(0));
for (int i = ; i < nRandCount;i++)
{
// nRand = rand()%126 + 1;
tDirty.Set(i+);
}
if (tDirty.IsDirty())
{
int arrDirty[N] = {};
int nMaxCount = -;
//同步自己执行次数
for (int i = ; i< N; ++i)
{
if (tDirty.Get(i))
{
arrDirty[++nMaxCount] = i;
}
}
//同步WS执行次数
nMaxCount = -;
for (int i = ; i< N; ++i)
{
if (tDirty.Get(i))
{
arrDirty[++nMaxCount] = i;
}
}
int nNotPrint = ;
for (int i = ; i< nMaxCount + ; i++)
{
nNotPrint = arrDirty[i];
}
} tDirty.Clean();
} int main()
{
float time_use=;
struct timeval start;
struct timeval end;
int nTestCount = ;
gettimeofday(&start,NULL); nPrintCount = ;
//处理过程--开始
for (int i = ; i< ; ++i)
{
DirtyBit(nTestCount);
}
//处理过程--结束 cout<<endl;
gettimeofday(&end,NULL);
time_use=(end.tv_sec-start.tv_sec)*+(end.tv_usec-start.tv_usec);//微秒
printf("time_use is %.10f\n",time_use); nPrintCount = ;
gettimeofday(&start,NULL);
//处理过程--开始
for (int i = ; i< ; ++i)
{
DirtyArr(nTestCount);
}
//处理过程--结束 cout<<endl;
gettimeofday(&end,NULL); time_use=(end.tv_sec-start.tv_sec)*+(end.tv_usec-start.tv_usec);//微秒
printf("time_use is %.10f\n",time_use);
return ;
}

玩家属性同步优化-脏数据标记(位运算、数组、stl之bitset)的更多相关文章

  1. C++基础-位运算

    昨天笔试遇到一道题,让实现乘法的计算方法,设计方案并优化,后来总结位运算相关知识如下: 在计算机中,数据是以1010的二进制形式存储的,1bytes = 8 bits,bit就是位,所以位运算就是对每 ...

  2. C/C++中的位运算

    位运算     位运算的运算分量只能是整型或字符型数据,位运算把运算对象看作是由二进位组成的位串信息,按位完成指定的运算,得到位串信息的结果. 位运算符有:     &(按位与).|(按位或) ...

  3. P3613 睡觉困难综合征 LCT+贪心+位运算

    \(\color{#0066ff}{ 题目描述 }\) 由乃这个问题越想越迷糊,已经达到了废寝忘食的地步.结果她发现--晚上睡不着了!只能把自己的一个神经元(我们可以抽象成一个树形结构)拿出来,交给D ...

  4. STL+位运算的文件

    1.queue 队列 queue的头文件是<queue>. 定义queue对象的示例代码如: queue<int>q;  队列内存放的是int类型的数 queue<dou ...

  5. 一文了解有趣的位运算(&、|、^、~、>>、<<)

    1.位运算概述 从现代计算机中所有的数据二进制的形式存储在设备中.即0.1两种状态,计算机对二进制数据进行的运算(+.-.*./)都是叫位运算,即将符号位共同参与运算的运算. 口说无凭,举一个简单的例 ...

  6. 位运算(&、|、^、~、>>、<<)

    1.位运算概述 从现代计算机中所有的数据二进制的形式存储在设备中.即0.1两种状态,计算机对二进制数据进行的运算(+.-.*./)都是叫位运算,即将符号位共同参与运算的运算. 口说无凭,举一个简单的例 ...

  7. 原生Redis跨数据中心双向同步优化实践

    一.背景 公司基于业务发展以及战略部署,需要实现在多个数据中心单元化部署,一方面可以实现多数据中心容灾,另外可以提升用户请求访问速度.需要保证多数据中心容灾或者实现用户就近访问的话,需要各个数据中心拥 ...

  8. Angular 1 深度解析:脏数据检查与 angular 性能优化

    TL;DR 脏检查是一种模型到视图的数据映射机制,由 $apply 或 $digest 触发. 脏检查的范围是整个页面,不受区域或组件划分影响 使用尽量简单的绑定表达式提升脏检查执行速度 尽量减少页面 ...

  9. 关于位图数据和标记位-P3

    文章目录 1 背景 1.1 问题 2 问题1探究 2.1 没有区的情况 2.2 一个区的情况 2.3 两个区的情况 2.4 三个区的情况 2.5 四个区的情况 2.6 五个区的情况 3 问题2探究 3 ...

随机推荐

  1. java基础 String

    标准格式:数据类型[] 数组名称 = new 数据类型[] {元素1,元素2,...};省略格式:数据类型[] 数组名称 = {元素1,元素2,...}; Scanner类实现的功能,可以实现键盘输入 ...

  2. java中异常的抛出:throw throws

    java中异常的抛出:throw throws Java中的异常抛出 语法: public class ExceptionTest{ public void 方法名(参数列表) throws 异常列表 ...

  3. Linux基础-7.Linux网络基础设置

    装好Linux,Linux一般会默认使用NetworkManager来辅助管理网络配置.对于配置Linux服务器来说,我们不需要NetworkManager来配置网络,所以要关闭它,不然它会影响手动配 ...

  4. php正则表达式中preg_match_all函数的详解

    php正则表达式中的函数我们之前为大家结果一个preg_match函数,相信大伙对此有所了解,那么php正则表达式中preg_match_all函数的具体使用是如何的呢?今天我们就带大家了解php正则 ...

  5. js预编译环节 变量声明提升 函数声明整体提升

    预编译四部曲 1.创建AO对象 2.找形参和变量声明,将变量和形参名作为AO属性名,值为undefined 3.将实参和形参统一 4.在函数体里面找函数声明,值赋予函数体 function fn(a) ...

  6. Java的基本知识之线程池篇

    1.基本概念 1.共享资源 多个线程对同一份资源进行访问(读写操作),该资源被称为共享资源.如何保证多个线程访问到的数据是一致的,则被称为数据同步或资源同步. 2.线程通信 线程通信,又叫进程内通信, ...

  7. Mysql慢查询日志以及优化

    慢查询日志设置 当语句执行时间较长时,通过日志的方式进行记录,这种方式就是慢查询的日志. 1.临时开启慢查询日志(如果需要长时间开启,则需要更改mysql配置文件) set global slow_q ...

  8. C#对MongDB取数据的常用代码

    1.使用聚合取最新的实时数据(每一个测站有多条数据,取日期最新的数据.也就是每个测站取最新的值) var group = new BsonDocument { {"_id",new ...

  9. 不依赖Python第三方库实现梯度下降

    认识 梯度的本意是一个向量(矢量),表示某一函数在该点处的方向导数沿着该方向取得最大值,即函数在该点处沿着该方向(此梯度的方向)变化最快,变化率最大(为该梯度的模), 我感觉, 其实就是偏导数向量方向 ...

  10. Linux 计划任务管理

    实际的生产环境中,总会做一些定期的任务,比如数据备份,我们不可能总等到那个时间去手动执行,这时计划任务就派上用场了. 一次性计划任务 at 一次性计划任务 at [HH:MM] [YYYY-MM-DD ...