C++ 内存块 快速访问内存使用(二)


注:
    这些代码是去年就写好了,所以代码里注释的年份是13年,中间也修改过没有添加注释说明,这次和上次介绍的内存块都是申请固定长度的内存块,不能是任意长度,任意长度实现比较困难设计大量的算法,如果你有兴趣可以查查类似内存管理算法之类的关键字,我有个写日志的模块式用到了非固定长度的内存,我是这么用的,申请几个内存池(这个后面介绍自己实现的), MemPool<8> MemPool<16> MemPool<32> MemPool<64> MemPool<128> 然后根据字符串的长度来放入不同的内存池中,超多128的就分成两截,分成两截的也没那么复杂,以后有机会了也介绍下自己写的那个日志模块,因为那个模块有点缺陷我也不怎么用,不过效率绝对的高


    今天就说下在C++ 内存块 快速访问内存使用(一)中提到的第二种内存块的实现,和第一种内存块接口完全一致,主要考虑用来做内存池时可以无缝兼容。

    基本的原理就是申请一块内存,在内存的开头预留出一段来标示后面的内存是否分配出去了,开头这一段为了减少长度就使用了位的方式,用一个位来标记一块内存是否分配出去,后面可以分配出多少个node前面就预留出多少个位,由于内存最小的长度是char(8个位),所以最好元素的数量是8的整数倍,当然这个工作就在这个block内部做了,如果你的block想申请的个数为9,内部会自动对齐到16个,即8的整数倍,小于8用8代替

    下面就是新鲜的代码,里面的注释不是很规范,希望能解释的很清楚,还是那句话,希望牛人看完后给圈圈点点 微笑,自己造的轮子希望您也能用的上,驰骋沙场,挥鞭天下,下面有简单的测试结果

    还有一块差点忘记说了,就是这两种内存块我都附加了一些追踪信息,就是简单判断内存是否写超了,起不了关键作用,但有作用总比没作用要好的多,这段追踪代码可以通过宏来关闭,如果想做的细一些,可以考虑在内存块的每个node的末尾多多申请一些内存来追踪,就是通过一定得手段把NODE_SIZE变大一些,每个node多出的内存写入一些特定的标记为来检查。那个静态变量tracememflag并没有赋初始值,其实不用赋初始值,里面乱会更好,你认为呢,两种内存块都是非线程安全的,这一块我也打算在内存池里来做,要是独立使用内存块就要注意了


/* 
============================================================================================
	内存块2 非线程安全
	原理:
	在这块内存的开头使用位来标示后面的内存是否分配出去
	调用Malloc需要调用Free销毁  调用New需要调用Delete来销毁
	使用New或者Delete函数 存在一些类型构造和析构的回调 不要在析构的回调中调用无参的Delete
	在无参的Delete防止析构回调再次使用内存块分配和释放 做了一些处理
	使用TRACE_MEM_BLOCK可以开启内存块一些信息的追踪

	add 2013.11.22 by yuwf

	Copyright (c), ...	

=============================================================================================
*/

#ifndef _PALANTIR_MEM_BLOCK2_H
#define _PALANTIR_MEM_BLOCK2_H

#include "PLTTrace.h"

namespace Palantir
{

#ifdef PALANTIR_TRACE
#ifndef TRACE_MEM_BLOCK
#define TRACE_MEM_BLOCK
#endif
#endif

	// _NODE_COUNT_ 应该为8的整倍数 否则内部会自动扩充8的倍数
	// 内部有个位数组记录每个node是否分配
	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_ = 8>
	struct MemBlock2
	{
		enum
		{
			_NODE_NUM_			= _NODE_COUNT_ == 0 ? 8 : _NODE_COUNT_%8 == 0 ? _NODE_COUNT_ : _NODE_COUNT_+(8-_NODE_COUNT_%8),		// 调节node的个数应是8的倍数 主要是为了使portion位恰好和和char一致
			NODE_SIZE			= _NODE_SIZE_ == 0 ? 1 : _NODE_SIZE_,		// node的大小
			NODE_COUNT			= _NODE_NUM_,								// node的个数 
			PORTION_SIZE		= NODE_COUNT/8,								// 索引的大小
			BUFFER_SIZE			= NODE_COUNT*NODE_SIZE,						// buffer 的大小
			TRACE_SIZE			= NODE_SIZE*2,					
		};

		MemBlock2();
		~MemBlock2();

		void* Malloc();

		// p 必须是从这个block上malloc获取的 外部要做检查
		// 返回值表示是否成功删除
		bool Free( const void* p );

		// 把分配的全部回收到内存池中
		void Free();

		// 基于类型的函数
		template<class T>
		T* New()
		{
			return ::new(Malloc()) T();
		}

		template<class T, class T1>
		T* New( const T1& param1 )
		{
			return ::new(Malloc()) T( param1 );
		}

		template<class T, class T1, class T2, class T3>
		T* New( const T1& param1, const T2& param2, const T3& param3 )
		{
			return ::new(Malloc()) T( param1, param2, param3 );
		}

		template<class T, class T1, class T2, class T3, class T4>
		T* New( const T1& param1, const T2& param2, const T3& param3, const T4& param4 )
		{
			return ::new(Malloc()) T( param1, param2, param3, param4 );
		}

		template<class T, class T1, class T2, class T3, class T4, class T5>
		T* New( const T1& param1, const T2& param2, const T3& param3, const T4& param4, const T5& param5 )
		{
			return ::new(Malloc()) T( param1, param2, param3, param4, param5 );
		}

		template<class T, class T1, class T2, class T3, class T4, class T5, class T6>
		T* New( const T1& param1, const T2& param2, const T3& param3, const T4& param4, const T5& param5, const T6& param6 )
		{
			return ::new(Malloc()) T( param1, param2, param3, param4, param5, param6 );
		}

		template<class T, class T1, class T2, class T3, class T4, class T5, class T6, class T7>
		T* New( const T1& param1, const T2& param2, const T3& param3, const T4& param4, const T5& param5, const T6& param6, const T7& param7 )
		{
			return ::new(Malloc()) T( param1, param2, param3, param4, param5, param6, param7 );
		}

		template<class T, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8>
		T* New( const T1& param1, const T2& param2, const T3& param3, const T4& param4, const T5& param5, const T6& param6, const T7& param7, const T8& param8 )
		{
			return ::new(Malloc()) T( param1, param2, param3, param4, param5, param6, param7, param8 );
		}

		template<class T, class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9>
		T* New( const T1& param1, const T2& param2, const T3& param3, const T4& param4, const T5& param5, const T6& param6, const T7& param7, const T8& param8, const T9& param9 )
		{
			return ::new(Malloc()) T( param1, param2, param3, param4, param5, param6, param7, param8, param9 );
		}

		// p是T类型 会调用 p->~T()
		// 返回值表示是否成功删除
		template<class T>
		inline bool Delete( const void* p );

		// 把分配的全部回收到内存块中
		template<class T>
		inline void Delete();

		// 判断指针是否是这个block上的
		inline bool Is_From( const void* p ) const;

		// 已使用的数量
		inline unsigned int Used_Count() const { return NODE_COUNT - free_num; };

		// 未使用的数量
		inline unsigned int Free_Count() const { return free_num; };

		// 是否分配完毕
		inline bool Is_Full() const { return free_num == 0 ; }

		// 是否未使用
		inline bool Is_Not_Use() const { return free_num == NODE_COUNT; }
		
	protected:
		// 没必要支持拷贝
		MemBlock2( const MemBlock2& ) {};
		MemBlock2& operator = ( const MemBlock2& ) { return *this; };

		unsigned int free_num;					// 未分配的个数 用户快速判断是否还有空间
		unsigned char portion[PORTION_SIZE];	// 用位来标记data中的node是否分配出去 0:未分配 1:分配
		unsigned char buffer[BUFFER_SIZE];
#ifdef TRACE_MEM_BLOCK
		unsigned char tracemem[TRACE_SIZE];		// 用来追踪这个块是否写超了
		static unsigned char tracememflag[TRACE_SIZE];
#endif

	};


#ifdef TRACE_MEM_BLOCK
	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_>
	unsigned char MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::tracememflag[MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::TRACE_SIZE];
#endif

	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_>
	MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::MemBlock2() : free_num(NODE_COUNT)
	{
		memset( portion, 0, sizeof(portion) );
#ifdef TRACE_MEM_BLOCK
		memcpy( tracemem, tracememflag, sizeof(tracemem) );
#endif
	}

	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_>
	MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::~MemBlock2()
	{
#ifdef TRACE_MEM_BLOCK
		for ( int i = 0; i < PORTION_SIZE; ++i )
		{
			if ( portion[i] )
			{
				for ( int k = 7; k >= 0; --k )
				{
					if ( portion[i] & (1 << k) )
					{
						PLT_Trace( "%s 分配出去的内存没有收回 %p\n", __FUNCTION__, (&buffer[(i*8+(7-k))*NODE_SIZE] ) );
					}
				}
			}
		}

		if ( memcmp( tracemem, tracememflag, sizeof(tracemem) ) != 0 )
		{
			PLT_Trace( "%s 内存块写超了\n", __FUNCTION__ );
		}
#endif
	}

	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_>
	void* MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::Malloc()
	{
		if ( Is_Full() )
		{
			return nullptr;
		}
		for ( int i = 0; i < PORTION_SIZE; ++i )
		{
			// 按位异或 说明portion[i]还没有分配完
			if ( portion[i] ^ 0XFF )
			{
				// 先从portion[i]的最高位来分配 最高位在前面和buffer顺序对应
				for ( int k = 7; k >= 0; --k )
				{
					// 先判断是否已经分配出去了
					if ( portion[i] & (1 << k) )
					{
						continue;
					}
					--free_num;
					portion[i] |= 1 << k;	// 标记这个内存已分配出去了
					return &buffer[(i*8+(7-k))*NODE_SIZE];	// 返回对应的buffer地址
				}
			}
		}
		return nullptr;
	}

	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_>
	bool MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::Free( const void* p )
	{
		if ( free_num == NODE_COUNT )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 内存块没有往外分配任何元素\n", __FUNCTION__ );
#endif
			return false;
		}
		if ( (uintptr_t)p < (uintptr_t)&buffer[0] || (uintptr_t)p > (uintptr_t)&buffer[BUFFER_SIZE-1] )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 指针并不是block分配出去的 buffer.begin=%p, buffer.end=%p p=%p\n", __FUNCTION__, &buffer[0], &buffer[BUFFER_SIZE-1], p );
#endif
			return false;
		}

		if( ((uintptr_t)p - (uintptr_t)&buffer[0])%NODE_SIZE != 0 )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 指针存在这个block上,但不在一个分配的位置 buffer.begin=%p, buffer.end=%p p=%p\n", __FUNCTION__, &buffer[0], &buffer[BUFFER_SIZE-1], p );
#endif
			return false;
		}

		uintptr_t index = ((uintptr_t)p - (uintptr_t)&buffer[0])/NODE_SIZE;
		if ( !( portion[index/8] & (1 << (7-index%8)) ) )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 指针存在这个block上,但这个位置并没有分配出去 buffer.begin=%p, buffer.end=%p p=%p\n", __FUNCTION__, &buffer[0], &buffer[BUFFER_SIZE-1], p );
#endif
			return false;
		}
#ifdef TRACE_MEM_BLOCK
		if ( memcmp( tracemem, tracememflag, sizeof(tracemem) ) != 0 )
		{
			PLT_Trace( "%s 内存块写超了\n", __FUNCTION__ );
		}
#endif
		++free_num;
		portion[index/8] ^= (1 << (7-index%8));	// 还原已分配出去的标记
		return true;
	}

	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_>
	void MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::Free()
	{
		if ( free_num == NODE_COUNT )
		{
			return;
		}

#ifdef TRACE_MEM_BLOCK
		if ( memcmp( tracemem, tracememflag, sizeof(tracemem) ) != 0 )
		{
			PLT_Trace( "%s 内存块写超了\n", __FUNCTION__ );
		}
#endif
		free_num = NODE_COUNT;
		memset( &portion[0], 0, sizeof(portion) );
	}

	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_>
	template<class T>
	inline bool MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::Delete( const void* p )
	{
		if ( free_num == NODE_COUNT )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s block没有往外分配任何元素\n", __FUNCTION__ );
#endif
			return false;
		}
		if ( (uintptr_t)p < (uintptr_t)&buffer[0] || (uintptr_t)p > (uintptr_t)&buffer[BUFFER_SIZE-1] )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 指针并不是block分配出去的 buffer.begin=%p, buffer.end=%p p=%p\n", __FUNCTION__, &buffer[0], &buffer[BUFFER_SIZE-1], p );
#endif
			return false;
		}

		if( ((uintptr_t)p - (uintptr_t)&buffer[0])%NODE_SIZE != 0 )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 指针存在这个block上,但不在一个分配的位置 buffer.begin=%p, buffer.end=%p p=%p\n", __FUNCTION__, &buffer[0], &buffer[BUFFER_SIZE-1], p );
#endif
			return false;
		}

		uintptr_t index = ((uintptr_t)p - (uintptr_t)&buffer[0])/NODE_SIZE;
		if ( !( portion[index/8] & (1 << (7-index%8)) ) )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 指针存在这个block上,但这个位置并没有分配出去 buffer.begin=%p, buffer.end=%p p=%p\n", __FUNCTION__, &buffer[0], &buffer[BUFFER_SIZE-1], p );
#endif
			return false;
		}
#ifdef TRACE_MEM_BLOCK
		if ( memcmp( tracemem, tracememflag, sizeof(tracemem) ) != 0 )
		{
			PLT_Trace( "%s 内存块写超了\n", __FUNCTION__ );
		}
#endif

		T* pobj = (T*)p;
		pobj->~T();	// 这里相当于有回调 可能会存在在回调中调用同一个内存块创建和删除node 
					// 其实没关系 只要在这个回调中不重复删除自己(pobj)(若在析构函数中在删除自己 系统提供的delete也会崩掉) 就不会存在任何问题 
					// 因为本函数中只有下面的代码会修改这个内存块的变量

		if ( free_num == NODE_COUNT )	// 防止在析构回调中调用了那个无参的Delete
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 出现了不可预知的错误\n", __FUNCTION__ );
#endif
			return false;
		}

		++free_num;
		portion[index/8] ^= (1 << (7-index%8)); // 还原已分配出去的标记
		return true;
	}

	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_>
	template<class T>
	inline void MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::Delete()
	{
		if ( free_num == NODE_COUNT )
		{
			return;
		}
#ifdef TRACE_MEM_BLOCK
		if ( memcmp( tracemem, tracememflag, sizeof(tracemem) ) != 0 )
		{
			PLT_Trace( "%s 内存块写超了\n", __FUNCTION__ );
		}
#endif
		// 首先修改free_num 防止下面的析构函数再次使用内存块分配和释放node
		free_num = NODE_COUNT;
		unsigned char portion_temp[PORTION_SIZE];
		memcpy( portion_temp, portion, sizeof(portion_temp) );
		memset( &portion[0], 1, sizeof(portion) );
		
		for ( int i = 0; i < PORTION_SIZE; ++i )
		{
			if ( portion_temp[i] )
			{
				for ( int k = 7; k >= 0; --k )
				{
					if ( portion_temp[i] & (1 << k) )
					{
						T* pobj = (T*)(&buffer[(i*8+(7-k))*NODE_SIZE]);
						pobj->~T();
					}
				}
			}
		}
		memset( &portion[0], 0, sizeof(portion) );
		free_num = NODE_COUNT;
	}

	template<unsigned int _NODE_SIZE_, unsigned int _NODE_COUNT_>
	inline bool MemBlock2<_NODE_SIZE_,_NODE_COUNT_>::Is_From( const void* p ) const
	{
		if ( (uintptr_t)p < (uintptr_t)&buffer[0] || (uintptr_t)p > (uintptr_t)&buffer[BUFFER_SIZE-1] )
		{
			return false;
		}

		if( ((uintptr_t)p - (uintptr_t)&buffer[0])%NODE_SIZE != 0 )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 指针存在这个block上,但不在一个分配的位置 buffer.begin=%p, buffer.end=%p p=%p\n", __FUNCTION__, &buffer[0], buffer[BUFFER_SIZE-1], p );
#endif
			return false;
		}
		if ( free_num == NODE_COUNT )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 内存块没有往外分配任何元素\n", __FUNCTION__ );
#endif
			return false;
		}
		uintptr_t index = ((uintptr_t)p - (uintptr_t)&buffer[0])/NODE_SIZE;
		if ( !( portion[index/8] & (1 << (7-index%8)) ) )
		{
#ifdef TRACE_MEM_BLOCK
			PLT_Trace( "%s 指针存在这个block上,但这个位置并没有分配出去 buffer.begin=%p, buffer.end=%p p=%p\n", __FUNCTION__, &buffer[0], &buffer[BUFFER_SIZE-1], p );
#endif
			return false;
		}

		return true;
	}
}

#endif





简单测试下两种内存块及系统new的效率,申请内存块的元素为10000,循环1百万次,每次随机一个[0-10000)的索引,若此处申请过就销毁,若没申请就申请一个元素,测试条件是在Windows Release模式下,关闭掉内存块的追踪部分,测试代码如下:

int allnum = 0;
struct MyTest
{
	MyTest( int i )
		: num(i)
	{
		++allnum;
	}
	MyTest()
		: num(1)
	{
		++allnum;
	}
	~MyTest()
	{
		--allnum;
	}
	int num;
};



void Test()
{
 	MyTest* allp[10000] = {0};

	{
		PLT_Speed_TestM( 0, "new" );
		Palantir::MemBlock<sizeof(MyTest),10000> mem;
		for ( int i = 0; i < 1000000; ++i )
		{
			int index = rand()%10000;
			if ( index == 100 )
			{
				for ( int k = 0; k < 10000; ++k )
				{
					if ( allp[k] )
					{
						delete allp[k];
					}
				}
				memset( allp, 0, sizeof(allp) );
				continue;;
			}
			if ( allp[index] )
			{
				delete allp[index];
				allp[index] = nullptr;
			}
			else
			{
				allp[index] = new MyTest(index);
			}
		}
		for ( int k = 0; k < 10000; ++k )
		{
			if ( allp[k] )
			{
				delete allp[k];
			}
		}
		// 清空
		memset( allp, 0, sizeof(allp) );
	}

	{
		PLT_Speed_TestM( 0, "mem" );
		Palantir::MemBlock<sizeof(MyTest),10000> mem;
		for ( int i = 0; i < 10; ++i )
		{
			int index = rand()%10000;
			if ( index == 100 )
			{
				mem.Delete<MyTest>();
				memset( allp, 0, sizeof(allp) );
				continue;;
			}
			if ( allp[index] )
			{
				mem.Delete<MyTest>(allp[index]);
				allp[index] = nullptr;
			}
			else
			{
				allp[index] = mem.New<MyTest>(index);
			}
		}
		mem.Delete<MyTest>();
		// 清空
		memset( allp, 0, sizeof(allp) );
	}

	{
		PLT_Speed_TestM( 0, "mem2" );
		Palantir::MemBlock2<sizeof(MyTest),10000> mem2;
		for ( int i = 0; i < 1000000; ++i )
		{
			int index = rand()%10000;
			if ( index == 100 )
			{
				mem2.Delete<MyTest>();
				memset( allp, 0, sizeof(allp) );
				continue;;
			}
			if ( allp[index] )
			{
				mem2.Delete<MyTest>(allp[index]);
				allp[index] = nullptr;
			}
			else
			{
				allp[index] = mem2.New<MyTest>(index);
			}
		}
		mem2.Delete<MyTest>();
		// 清空
		memset( allp, 0, sizeof(allp) );
	}
}


测试结果如下:


SpeedTest usetime: 97(sec) 790(milli) 0(micro) Test: new
SpeedTest usetime: 26(sec) 695(milli) 0(micro) Test: mem
SpeedTest usetime: 0(sec) 215(milli) 0(micro) Test: mem2


    显然两种内存块都比系统的快,且mem2基本上比系统函数快400倍,mem比系统函数快4倍,mem就是慢在Delete函数里了,因为每次删除都要遍历下那些没有分配出去的node,但基本不会比系统多使用内存,但mem2基本上是每8000个元素就要多出1m的内存,空间换效率


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值