浅析MiniGUI v1.62小内存自管理函数所在文件Fixstr.c
文章来源:http://gliethttp.cublog.cn
MiniGUI对内存的使用遵循如下法则: 1.malloc用在只有1次性分配,或者释放和分配频率不高,或者超出小内存管理大小,或者相应小内存没有了的情况下 2.当释放和分配频率很高时,MiniGUI使用FixStrAlloc()和FreeFixStr()两个加速函数自己管理小块内存 的分配和释放,但是MiniGUI的小内存管理代码有些地方似乎效率可以提高, 比如*bitmap |= (0x80 >> (stroff%8));就没有必要使用余取运算,因为%运算要使用到/除法运算, 很耗时,因此直接stroff&0x7就可以;另一个地方就是[0]数组位置存放的是大数据,致使每次长度和索引 关联计算时,总是需要进行一次翻转运算(当然可以使用static数组,直接读取value[i]中length值) bufflen = 1 << (NR_HEAP + 1 - i),所以应该在初始化时,做一些运算,让[0]对应低数值,这样避开 翻转,最后直接bufflen = 1 << i就搞定,总之,感觉minigui在内存管理速度方面仍需要做改进; 另外MiniGUI的所管理的小块内存区域不能动态生长和释放,只能是根据系统设计之处的最大值, 固化之前指定好,所以对于控件动态增量不固定的应用系统,可能随着系统运行时间的增长,因为控件的不断增加, 而不能在heap区域找到空闲的数组,进而使用malloc申请的那些控件的速度开始下降,当然这样的应用系统, 目前为止我还没有见到过,因为系统动态性都很小,所以一般我会把内存指定足够大来由GUI系统管理, 不过确实存在这样一种局限性. //----------------------------------------------------------- 以下为Fixstr.c文件源程序: static struct FIXSTR { BYTE bitmap[LEN_BITMAP]; int offset[NR_HEAP]; char* heap[NR_HEAP + 1]; #ifndef _LITE_VERSION pthread_mutex_t lock; #endif } FixStrHeap;
BOOL InitFixStr (void) { int i, j, offset; BYTE* bitmap; //2007-10-11 gliethttp //首先申请一块足够大的用来自管理小内存的连续内存区 //之所以乘8是因为,FixStrAlloc()函数中for(j = 0; j < 8; j++)运算 //这样FixStrAlloc()可以直接对常量---8进行运算,不用担心bits&0x07是否还有数据 //进而提高运算速度 if (!(FixStrHeap.heap [0] = malloc (MAX_LEN_FIXSTR * 8 * NR_HEAP))) return FALSE;
for (i = 1; i <= NR_HEAP; i++) { FixStrHeap.heap[i] = FixStrHeap.heap[0] + MAX_LEN_FIXSTR * 8 * i; } //heap[NR_HEAP]为自管理连续内存区的结尾地址,也就是free区域有效开始地址 bitmap = FixStrHeap.bitmap; offset = 0; for (i = 0; i < NR_HEAP; i++) { for (j = 0; j < (1<<i); j ++) bitmap[j] |= 0xFF; //2007-10-11 gliethttp //bitmap[0]管理 1个8位组---每个位组2k ---共16k //bitmap[1]管理 2个8位组---每个位组1k ---共16k //bitmap[2]管理 4个8位组---每个位组512---共16k //bitmap[3]管理 8个8位组---每个位组256---共16k //bitmap[4]管理 16个8位组---每个位组128---共16k //bitmap[5]管理 32个8位组---每个位组64 ---共16k //bitmap[6]管理 64个8位组---每个位组32 ---共16k //bitmap[7]管理128个8位组---每个位组16 ---共16k //bitmap[8]管理256个8位组---每个位组8 ---共16k //bitmap[9]管理512个8位组---每个位组4 ---共16k //bitmap总大小为1+2+4+8+16+32+64+128+256+512个字节 bitmap += 1<<i; //2007-10-11 gliethttp //offset和bitmap是绑定在一起使用的 //即:bitmap+offset[i]开始的map是管理heap[i]这组内存块的 FixStrHeap.offset[i] = offset; offset += 1<<i; } #ifndef _LITE_VERSION //对于非lite的threads配置等,需要锁机制来保护内存申请部分 //如果系统对优先级翻转并不关心,可以直接使用semaphore作为锁 //因为mutex的效率多少都要比semaphore机制低 pthread_mutex_init (&FixStrHeap.lock, NULL); #endif return TRUE; }
void TerminateFixStr (void) { #ifndef _LITE_VERSION pthread_mutex_destroy (&FixStrHeap.lock); #endif //minigui退出,调用free释放由malloc申请的大块管理内存 free (FixStrHeap.heap[0]); } static char zero_string [] = {'\0'};
char* GUIAPI FixStrAlloc (int len) { UINT ulen = (UINT)len; int i, j, btlen, bufflen; char* heap; BYTE* bitmap;
if (len < 0) return NULL; if (len == 0) return zero_string;//仅对<0数据为非法申请,长度0由zero_string对应 if (len >= MAX_LEN_FIXSTR) //超过常用小数据块的大小,那么malloc重新申请 //使用malloc比较耗时一些 return (char*)malloc (len + 1); i = 0; while (ulen) { ulen = ulen >> 1; //求取2的幂数值 i++; } //2007-10-11 gliethttp //bitmap[0]管理 1个8位组---每个位组2k ---共16k //bitmap[1]管理 2个8位组---每个位组1k ---共16k //bitmap[2]管理 4个8位组---每个位组512---共16k //bitmap[3]管理 8个8位组---每个位组256---共16k //bitmap[4]管理 16个8位组---每个位组128---共16k //bitmap[5]管理 32个8位组---每个位组64 ---共16k //bitmap[6]管理 64个8位组---每个位组32 ---共16k //bitmap[7]管理128个8位组---每个位组16 ---共16k //bitmap[8]管理256个8位组---每个位组8 ---共16k //bitmap[9]管理512个8位组---每个位组4 ---共16k //bitmap总大小为1+2+4+8+16+32+64+128+256+512个字节 //2007-10-11 gliethttp //i=2,保证4字节对齐 if (i == 1) i = 2; bufflen = 1 << i; //#define MAX_LEN_FIXSTR 2048 //#define NR_HEAP 10 //#define LEN_BITMAP (1+2+4+8+16+32+64+128+256+512) i = NR_HEAP + 1 - i; //2007-10-11 gliethttp //重新计算后的i就是下面的heap索引值 //heap[0]管理 8个 2k内存块 //heap[1]管理 16个 1k内存块 //heap[2]管理 32个512内存块 //heap[3]管理 64个256内存块 //heap[4]管理128个128内存块 //heap[5]管理256个 64内存块 //heap[6]管理512个 32内存块 //heap[7]管理 1k个 16内存块 //heap[8]管理 2k个 8内存块 //heap[9]管理 4k个 4内存块 #ifndef _LITE_VERSION //操作内存管理全局量,申请持有全局量修改权的锁 pthread_mutex_lock (&FixStrHeap.lock); #endif heap = FixStrHeap.heap[i]; bitmap = FixStrHeap.bitmap + FixStrHeap.offset[i]; btlen = 1 << i; //btlen为8位组个数 for (i = 0; i < btlen; i++) { //2007-10-11 gliethttp //数据块的个数肯定是8的倍数,这在InitFixStr()中已经故意申请成8的倍数了 //为了提高效率可以,加入一个判断,即: //if(*bitmap != 0xff)这样再执行for(j = 0; j < 8; j++) //这在系统运行一段时间之后能够很好提高效率,进而让高效不随时间的增加而降低 for(j = 0; j < 8; j++) { if (*bitmap & (0x80 >> j)) { *bitmap &= (~(0x80 >> j)); #ifndef _LITE_VERSION pthread_mutex_unlock (&FixStrHeap.lock); #endif #if 0 printf ("FixStrAlloc, len: %d, heap: %p.\n", len, heap); #endif return heap;//我们找到了一个4,8...字节的小数据区 } //heap执行8位组中的第0~7个组的下一个组 //我觉得可以直接在return heap;之前加入一句:heap += j*bufflen; //然后在下面bitmap++的前面加上,heap += 8*bufflen;这样效率会高一些, //尤其当运行一段时间heap使用了一部分之后,效率提高的比较明显 heap += bufflen; } //指向下1个8位组对应的bitmap bitmap++; }
#ifndef _LITE_VERSION pthread_mutex_unlock (&FixStrHeap.lock);//释放持有权
#endif //管理的相应小内存块已经全部用完,只能使用malloc //如:heap[3]管理 64个256内存块可能全部耗尽,但是heap[0]的2k内存块 //可能1块都没有用呢,但是minigui不管,还是使用下面的malloc申请 //这也突显了minigui的小内存管理单元的局限性,它仅仅在一定程度上提高了部分 //控件的内存申请、释放效率 return (char*)malloc (len + 1); }
void GUIAPI FreeFixStr (char* str) { char* heap; BYTE* bitmap; int i; int bufflen; int stroff; if (str [0] == '\0') return; if (str >= FixStrHeap.heap [NR_HEAP] || str < FixStrHeap.heap [0]) { //释放的内存区域不是由FixStrAlloc管理的,而是由free管理 free (str); return; } for (i = 1; i <= NR_HEAP; i++) { if (str < FixStrHeap.heap[i]) //str在当前heap的前一个heap中 break; } i--; bufflen = 1 << (NR_HEAP + 1 - i);
#ifndef _LITE_VERSION pthread_mutex_lock (&FixStrHeap.lock); #endif //重新计算后的i就是下面的heap索引值 //heap[0]管理 8个 2k内存块 //heap[1]管理 16个 1k内存块 //heap[2]管理 32个512内存块 //heap[3]管理 64个256内存块 //heap[4]管理128个128内存块 //heap[5]管理256个 64内存块 //heap[6]管理512个 32内存块 //heap[7]管理 1k个 16内存块 //heap[8]管理 2k个 8内存块 //heap[9]管理 4k个 4内存块 heap = FixStrHeap.heap[i]; bitmap = FixStrHeap.bitmap + FixStrHeap.offset[i];
stroff = 0; //2007-10-11 gliethttp //这里minigui是很肯定的假设str一定能够在 //该heap中有以相应字节数目为对齐值的项,如果没有那可就惨了,可能就此down机了 //当然加入过多判断,难免会增加内存申请释放的时间花费 //这也是假设完全安全的一个让人信服的,无话可说的理由之一 while (str != heap) { heap += bufflen; stroff ++; } #if 0 printf ("FreeFixStr, len: %d, str: %p: heap: %p.\n", strlen (str), str, heap); #endif //该str位于本heap的第stroff个bufflen字节内存块 bitmap = bitmap + (stroff>>3); *bitmap |= (0x80 >> (stroff%8));//可以使用stroff&0x07替换stroff%8进而提高效率,毕竟余取运算涉及到除法 #ifndef _LITE_VERSION pthread_mutex_unlock (&FixStrHeap.lock); #endif } 总之,MiniGUI v1.62的小内存自管理机制,确实有很大的局限性,当然它也确实在小范围内,提高了MiniGUI的效率.
|