当前位置: 首页 > 编程日记 > 正文

Memcached内存池分析

针对Memcacged1.4.15代码

1.完整slabs内存池图

这是我画的memcached的slabs内存池对象关系图:


2.内存池数据结构

typedef struct {unsigned int size;      /* 每个item的大小 */unsigned int perslab;   /* 每个page中包含多少个item */void *slots;           //空闲的item列表,指向最后一个空闲的chunk的地址,如果perslab=1那么slots和slab_list[0]指向的地址是同一个unsigned int sl_curr;   //当前空闲的item位置(也就是实际空闲item个数),从后往前的unsigned int slabs;     //已分配chunk数目void **slab_list;       //所有的page指针,指向page的地址,也是第一个chunk的地址,可以用(item *)((&slabclass[N])->slab_list[0])获取第一个itemunsigned int list_size; //每个数组trunk数目,默认是16unsigned int killing;  /* index+1 of dying slab, or zero if none */size_t requested; //已分配总内存大小
} slabclass_t;//slots:指向free chunk块的指针数组,slots的指针指向一个void //*的数组,该数组中的每一个元素的内容均指向一个空闲的chunk块,而且相同slabclass上的所有slab中的free chunk块均挂接到这个链表上;
//slabs:当前slabclass中分配的页内存个数;
//slab_list:当前slabclass所分配的页内存(slab)的指针数组,每一个数组元素的内容均是一个指向页内存地址的指针;
//        size_t new_size =  (p->list_size != 0) ? p->list_size * 2 : 16;
//        void *new_list = realloc(p->slab_list, new_size * sizeof(void *));
//        if (new_list == 0) return 0;
//        p->list_size = new_size;
//        p->slab_list = new_list;static slabclass_t slabclass[MAX_NUMBER_OF_SLAB_CLASSES];
static size_t mem_limit = 0;//内存限制大小,如果默认64M   slabs_init的时候
static size_t mem_malloced = 0;//已分配大小
static int power_largest;//数组最大个数,默认是42static void *mem_base = NULL;
static void *mem_current = NULL;//内存使用当前地址
static size_t mem_avail = 0;//剩余内存/*** slab 线程锁*/
static pthread_mutex_t slabs_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t slabs_rebalance_lock = PTHREAD_MUTEX_INITIALIZER;

3.初始化slabs_init

/* slab初始化*/
/* limit:内存大小(字节);factor:增长因子;prealloc:是否一次性分配内存*/
void slabs_init(const size_t limit, const double factor, const bool prealloc) {int i = POWER_SMALLEST - 1;//0unsigned int size = sizeof(item) + settings.chunk_size;//chunk_size 最小分配空间mem_limit = limit;//内存限制大小if (prealloc) {//一次分配所有设置的内存/* Allocate everything in a big chunk with malloc */mem_base = malloc(mem_limit);if (mem_base != NULL) {mem_current = mem_base;mem_avail = mem_limit;} else {fprintf(stderr, "Warning: Failed to allocate requested memory in one large chunk.\nWill allocate in smaller chunks\n");}}memset(slabclass, 0, sizeof(slabclass));
//settings.item_size_max = 1024 * 1024=1M; /* The famous 1MB upper limit. */
//settings.item_size_max / factor    1048576/1.25=838860.8 这就是单page最大的chunk大小 字节
//slabclass[41]    {size=717184 perslab=1 slots=0x00000000 ...}    slabclass_t
//所以到了42就跳出循环了
//slabclass[42]    {size=1048576 perslab=1 slots=0x00000000 ...}    slabclass_t
//43就不分配了
//slabclass[43]    {size=0 perslab=0 slots=0x00000000 ...}    slabclass_twhile (++i < POWER_LARGEST && size <= settings.item_size_max / factor) {/* Make sure items are always n-byte aligned */if (size % CHUNK_ALIGN_BYTES)//字节数为8的倍数size += CHUNK_ALIGN_BYTES - (size % CHUNK_ALIGN_BYTES);slabclass[i].size = size;//item大小slabclass[i].perslab = settings.item_size_max / slabclass[i].size;//item数目size *= factor;//乘以增长因子if (settings.verbose > 1) {fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",i, slabclass[i].size, slabclass[i].perslab);}}power_largest = i;//默认=42slabclass[power_largest].size = settings.item_size_max;slabclass[power_largest].perslab = 1;//最大的只能存储一个itemif (settings.verbose > 1) {fprintf(stderr, "slab class %3d: chunk size %9u perslab %7u\n",i, slabclass[i].size, slabclass[i].perslab);}/* for the test suite:  faking of how much we've already malloc'd */{char *t_initial_malloc = getenv("T_MEMD_INITIAL_MALLOC");if (t_initial_malloc) {mem_malloced = (size_t)atol(t_initial_malloc);}}#ifndef DONT_PREALLOC_SLABS{char *pre_alloc = getenv("T_MEMD_SLABS_ALLOC");if (pre_alloc == NULL || atoi(pre_alloc) != 0) {slabs_preallocate(power_largest);}}
#endif
}

4.第一次分配slab

最初是在do_item_alloc中调用do_slabs_alloc

#0  do_item_alloc (key=0x7ffff0013754 "key", nkey=3, flags=0, exptime=0, nbytes=5, cur_hv=0) at items.c:190
#1  0x0000000000415706 in item_alloc (key=0x7ffff0013754 "key", nkey=3, flags=0, exptime=0, nbytes=5) at thread.c:486
#2  0x000000000040a38e in process_update_command (c=0x7ffff0013550, tokens=0x7ffff7ae4b00, ntokens=6, comm=2, handle_cas=false) at memcached.c:2917
#3  0x000000000040b43d in process_command (c=0x7ffff0013550, command=0x7ffff0013750 "set") at memcached.c:3258
#4  0x000000000040bfa1 in try_read_command (c=0x7ffff0013550) at memcached.c:3504
#5  0x000000000040cc25 in drive_machine (c=0x7ffff0013550) at memcached.c:3824
#6  0x000000000040d81f in event_handler (fd=37, which=2, arg=0x7ffff0013550) at memcached.c:4065
#7  0x00007ffff7dc9e0c in event_process_active_single_queue (base=0x635bb0, flags=0) at event.c:1350
#8  event_process_active (base=0x635bb0, flags=0) at event.c:1420
#9  event_base_loop (base=0x635bb0, flags=0) at    event.c:1621
#10 0x0000000000415416 in worker_libevent (arg=0x628d60) at thread.c:384
#11 0x0000003441607851 in start_thread () from /lib64/libpthread.so.0
#12 0x00000034412e890d in clone    () from /lib64/libc.so.6


客户端申请存储key value会调用到do_item_alloc

/*@null@*/
item *do_item_alloc(char *key, const size_t nkey, const int flags,const rel_time_t exptime, const int nbytes,const uint32_t cur_hv) {uint8_t nsuffix;item *it = NULL;char suffix[40];size_t ntotal = item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);if (settings.use_cas) {ntotal += sizeof(uint64_t);//算出需要分配的内存的大小,原始长度+结构体自身大小}unsigned int id = slabs_clsid(ntotal);if (id == 0)return 0;mutex_lock(&cache_lock);/* do a quick check if we have any expired items in the tail.. */int tries = 5;int tried_alloc = 0;item *search;void *hold_lock = NULL;rel_time_t oldest_live = settings.oldest_live;search = tails[id];/* We walk up *only* for locked items. Never searching for expired.* Waste of CPU for almost all deployments */for (; tries > 0 && search != NULL; tries--, search=search->prev) {uint32_t hv = hash(ITEM_key(search), search->nkey, 0);/* Attempt to hash item lock the "search" item. If locked, no* other callers can incr the refcount*//* FIXME: I think we need to mask the hv here for comparison? */if (hv != cur_hv && (hold_lock = item_trylock(hv)) == NULL)continue;/* Now see if the item is refcount locked */if (refcount_incr(&search->refcount) != 2) {refcount_decr(&search->refcount);/* Old rare bug could cause a refcount leak. We haven't seen* it in years, but we leave this code in to prevent failures* just in case */if (search->time + TAIL_REPAIR_TIME < current_time) {itemstats[id].tailrepairs++;search->refcount = 1;do_item_unlink_nolock(search, hv);}if (hold_lock)item_trylock_unlock(hold_lock);continue;}/* Expired or flushed */if ((search->exptime != 0 && search->exptime < current_time)|| (search->time <= oldest_live && oldest_live <= current_time)) {itemstats[id].reclaimed++;if ((search->it_flags & ITEM_FETCHED) == 0) {itemstats[id].expired_unfetched++;}it = search;slabs_adjust_mem_requested(it->slabs_clsid, ITEM_ntotal(it), ntotal);do_item_unlink_nolock(it, hv);/* Initialize the item block: */it->slabs_clsid = 0;} else if ((it = slabs_alloc(ntotal, id)) == NULL) {


调用slabs_clsid()

//寻找适合给定大小的item存储的slab
unsigned int slabs_clsid(const size_t size) {int res = POWER_SMALLEST;if (size == 0)return 0;while (size > slabclass[res].size)//找到第一个比item size大的slabif (res++ == power_largest)return 0;return res;
}

调用do_slabs_alloc返回slots指向的item,并使slots指向下一个item

/*存储item*/
static void *do_slabs_alloc(const size_t size, unsigned int id) {slabclass_t *p;void *ret = NULL;item *it = NULL;if (id < POWER_SMALLEST || id > power_largest) {MEMCACHED_SLABS_ALLOCATE_FAILED(size, 0);return NULL;}p = &slabclass[id];assert(p->sl_curr == 0 || ((item *)p->slots)->slabs_clsid == 0);/* fail unless we have space at the end of a recently allocated page,we have something on our freelist, or we could allocate a new page *///p->sl_curr != 0 说明还有空闲就不要调用do_slabs_newslab重新分配下一个pageif (! (p->sl_curr != 0 || do_slabs_newslab(id) != 0)) {/* We don't have more memory available */ret = NULL;} else if (p->sl_curr != 0) {/* return off our freelist */it = (item *)p->slots;//将当前p空闲的slots其实也就是第一个,实际上是链表最后一个chunk分配给itemp->slots = it->next;//修改p的空闲的slots,为倒数第二个,也就是it的前一个if (it->next) it->next->prev = 0;//因为it的前一个再前一个已经给item分配了,自然没了p->sl_curr--;//-1,虽然perslab在分割好之后和sl_curr一样大,但是sl_curr是要递减的,而perslab是永远不变的存储的是chunk个数ret = (void *)it;}if (ret) {p->requested += size;MEMCACHED_SLABS_ALLOCATE(size, id, p->size, ret);} else {MEMCACHED_SLABS_ALLOCATE_FAILED(size, id);}return ret;
}

下面是gdb下调试的函数调用关系:

#0  do_slabs_alloc (size=71, id=1) at slabs.c:241
#1  0x000000000041161d in slabs_alloc (size=71, id=1) at slabs.c:404
#2  0x0000000000412ae6 in do_item_alloc (key=0x7ffff0013754 "key", nkey=3, flags=0, exptime=0, nbytes=5, cur_hv=0) at items.c:188
#3  0x0000000000415706 in item_alloc (key=0x7ffff0013754 "key", nkey=3, flags=0, exptime=0, nbytes=5) at thread.c:486
#4  0x000000000040a38e in process_update_command (c=0x7ffff0013550, tokens=0x7ffff7ae4b00, ntokens=6, comm=2, handle_cas=false) at memcached.c:2917
#5  0x000000000040b43d in process_command (c=0x7ffff0013550, command=0x7ffff0013750 "set") at memcached.c:3258
#6  0x000000000040bfa1 in try_read_command (c=0x7ffff0013550) at memcached.c:3504
#7  0x000000000040cc25 in drive_machine (c=0x7ffff0013550) at memcached.c:3824
#8  0x000000000040d81f in event_handler (fd=37,    which=2, arg=0x7ffff0013550) at memcached.c:4065
#9  0x00007ffff7dc9e0c in event_process_active_single_queue (base=0x635bb0, flags=0) at event.c:1350
#10 event_process_active (base=0x635bb0, flags=0) at event.c:1420
#11 event_base_loop (base=0x635bb0, flags=0) at event.c:1621
#12 0x0000000000415416 in worker_libevent (arg=0x628d60) at thread.c:384
#13 0x0000003441607851 in start_thread () from /lib64/libpthread.so.0
#14 0x00000034412e890d in clone () from /lib64/libc.so.6

5.第一次slab_list

初始化slabs,分配trunk

static int do_slabs_newslab(const unsigned int id) {slabclass_t *p = &slabclass[id];
//settings.item_size_max = 1024 * 1024; /* The famous 1MB upper limit. */int len = settings.slab_reassign ? settings.item_size_max: p->size * p->perslab;char *ptr;if ((mem_limit && mem_malloced + len > mem_limit && p->slabs > 0) ||(grow_slab_list(id) == 0) ||((ptr = memory_allocate((size_t)len)) == 0)) {MEMCACHED_SLABS_SLABCLASS_ALLOCATE_FAILED(id);return 0;}memset(ptr, 0, (size_t)len);split_slab_page_into_freelist(ptr, id);
//这里很巧妙,如果是第一次do_slabs_newslab,那么p->slabs=0,++之后就=1,
//第二次来自然就是2,这次会把第2次申请的page也就是ptr串在p->slab_list[1]上p->slab_list[p->slabs++] = ptr;mem_malloced += len;MEMCACHED_SLABS_SLABCLASS_ALLOCATE(id);return 1;
}


p->slab_list第一次是存16*8个字节

//扩充trunk数目,重新分配slabs个数,默认是分配16个页,后续按照2倍增加
static int grow_slab_list (const unsigned int id) {slabclass_t *p = &slabclass[id];if (p->slabs == p->list_size) {//默认new_zise=16size_t new_size =  (p->list_size != 0) ? p->list_size * 2 : 16;//因为以后会扩容,所以这里用realloc//p->slab_list一开始=0,64位sizeof(void *)=8,32位sizeof(void *)=4void *new_list = realloc(p->slab_list, new_size * sizeof(void *));if (new_list == 0) return 0;p->list_size = new_size;p->slab_list = new_list;}return 1;
}

调用split_slab_page_into_freelist分割ptr


5.分割ptr

split_slab_page_into_freelist:

static void split_slab_page_into_freelist(char *ptr, const unsigned int id) {slabclass_t *p = &slabclass[id];int x;for (x = 0; x < p->perslab; x++) {do_slabs_free(ptr, 0, id);ptr += p->size;//ptr偏移一个size,第一次是96字节,如果是默认配置的话}
}

这个函数很短,就是循环去调用do_slabs_free函数

//分割item内存
static void do_slabs_free(void *ptr, const size_t size, unsigned int id) {slabclass_t *p;item *it;//首先声明一个item对象assert(((item *)ptr)->slabs_clsid == 0);assert(id >= POWER_SMALLEST && id <= power_largest);if (id < POWER_SMALLEST || id > power_largest)return;MEMCACHED_SLABS_FREE(size, id, ptr);p = &slabclass[id];//获取第id个slabclass_t并声明为pit = (item *)ptr;//将需要切割的内存空间实例化为item *it->it_flags |= ITEM_SLABBED;it->prev = 0;//it一开始无头it->next = p->slots;//it的新卡一个是p的当前的slots,也就是ptr上一次切割的item,也可以理解为是当前item的上一个itemif (it->next) it->next->prev = it;//如果当前的it的下一个item也就是也就是ptr上一次切割的item存在(第2次切割以后才会有)//前一个item的后一个item指向当前,其实也就是正常的双链表指向操作p->slots = it;//p的slots永远指向当前的item,每次如果执行do_slabs_free就会划走一块item,并串前后的指针p->sl_curr++;//p的当前空闲的item个数+1p->requested -= size;return;
}

第一次:
(gdb) p    ptr
$5 = 0x7ffff51e1010 ""

vs2012+        ptr    0x0000000000230070 ""    char *

>    split_slab_page_into_freelist(ptr, id);

第二次:
(gdb) p    ptr
$25 = (void *) 0x7ffff51e1070
0x70-0x10=0x60=96  正好是一个chunk大小

第三次
(gdb) p ptr
$50 = (void *) 0x7ffff51e10d0
(gdb) p *(it-2)
$46 = {next = 0x7ffff51e1010, prev = 0x7ffff51e10d0, h_next = 0x0, time = 0, exptime = 0, nbytes = 0, refcount = 0, nsuffix = 0 '\000', it_flags = 4 '\004
', slabs_clsid = 0 '\000', nkey = 0 '\000', data = 0x7ffff51e1070}
(gdb) p *(it-4)
$54 = {next = 0x0, prev = 0x7ffff51e1070, h_next = 0x0, time = 0, exptime = 0, nbytes = 0, refcount = 0, nsuffix = 0 '\000', it_flags = 4 '\004', slabs_cl
sid = 0 '\000', nkey = 0 '\000', data = 0x7ffff51e1010}

为什么是it-2才是上一个呢?
因为
(gdb) p sizeof(it)
$57 = 8
(gdb) p sizeof(item)
$58 = 48
it是指针自然是8字节,而item结构是48字节,96正好是48的2倍,其实这里是巧合,求上一个item不应该这么求!
static void split_slab_page_into_freelist(char *ptr, const unsigned int id) {
    slabclass_t *p = &slabclass[id];
    int x;
    for (x = 0; x < p->perslab; x++) {
        do_slabs_free(ptr, 0, id);
        ptr += p->size;
    }
}
因为每次是Ptr增加p->size的长度。
所以直接用next指针就可以了
(gdb) p it->next
$67 = (struct _stritem *) 0x7ffff51e1070
(gdb) p *it->next
$68 = {next = 0x7ffff51e1010, prev = 0x7ffff51e10d0, h_next = 0x0, time = 0, exptime = 0, nbytes = 0, refcount = 0, nsuffix = 0 '\000', it_flags = 4 '\004
', slabs_clsid = 0 '\000', nkey = 0 '\000', data = 0x7ffff51e1070}



下面是在vs2012下的调试数据,一看就清楚了


7.分割完ptr之后

回到do_slabs_newslab中修改了p->slab_list[p->slabs++] = ptr;mem_malloced += len;


然后又回到do_slabs_alloc函数中:

it = (item *)p->slots;//将当前p空闲的slots其实也就是第一个,实际上是链表最后一个chunk分配给itemp->slots = it->next;//修改p的空闲的slots,为倒数第二个,也就是it的前一个if (it->next) it->next->prev = 0;//因为it的前一个再前一个已经给item分配了,自然没了p->sl_curr--;//-1,虽然perslab在分割好之后和sl_curr一样大,但是sl_curr是要递减的,而perslab是永远不变的存储的是chunk个数ret = (void *)it;
(gdb) p    *p
$72 = {size = 96, perslab = 10922, slots = 0x7ffff52e0f70, sl_curr = 10922, slabs = 1, slab_list = 0x7ffff00169e0, list_size = 16, killing = 0, requested
= 0}
(gdb) n
(gdb) p *p
$73 = {size = 96, perslab = 10922, slots = 0x7ffff52e0f10, sl_curr = 10922, slabs = 1, slab_list = 0x7ffff00169e0, list_size = 16, killing = 0, requested

= 0}

p->sl_curr--;之后的调试数据,可以看到sl_curry已经减1 ,而perslab是不变的。

(gdb) p *p
$75 = {size = 96, perslab = 10922, slots = 0x7ffff52e0f10, sl_curr = 10921, slabs = 1, slab_list = 0x7ffff00169e0, list_size = 16, killing = 0, requested
= 0}

下面是vs2012中的结果,一样的:


8.分配好item之后

回到do_item_alloc,已经得到分配的item

(gdb) p *it
$79 = {next = 0x7ffff52e0f10, prev = 0x0, h_next = 0x0, time = 0, exptime = 0, nbytes = 0, refcount = 0, nsuffix = 0 '\000', it_flags = 4 '\004', slabs_cl
sid = 0 '\000', nkey = 0 '\000', data = 0x7ffff52e0f70}


9.模拟memcached调用slabs


3次分配一样大小的


2.故意设一个比默认chunk=96大的需求设置100,从后面的调试信息可以看出id2已经被算出应该是第2个slabs上:

int main()
{item *it1=NULL;item *it2 = NULL;item *it3 = NULL;item *it4 = NULL;int preallocate = 0;size_t ntotal =63;// item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);size_t ntotal2 =100;unsigned int id =0;	unsigned int id2 =0;settings_init();slabs_init(settings.maxbytes, settings.factor, preallocate);if (settings.use_cas) {ntotal += sizeof(uint64_t);}id=slabs_clsid(ntotal);it1=slabs_alloc(ntotal, id);it2=slabs_alloc(ntotal, id);it3=slabs_alloc(ntotal, id);if (settings.use_cas) {ntotal2 += sizeof(uint64_t);}id2=slabs_clsid(ntotal2);it4=slabs_alloc(ntotal2, id2);
return 0;
}


制造perslab=1的情景

分配超过settings.item_size_max / factor=717184的size

这样就会命中id=41的slabs[41],同时我们设置2个需要分配717184的:

从调试结果可以看出来slabclass[41]已经分配了2个slab_list,由于这2个slab_list指向的page都只有一个chunk,所以这个chunk中存储的item的next和prev都是0,

因为第一次分割do_slabs_free的时候:

it->prev = 0;it->next = p->slots;
由于p->slots一开始=0,因为后面不会再切割chunk了,所以这个item的next和prev都是0。

	item *it1=NULL;item *it2 = NULL;item *it3 = NULL;item *it4 = NULL;item *it5 = NULL;int preallocate = 0;size_t ntotal =63;// item_make_header(nkey + 1, flags, nbytes, suffix, &nsuffix);size_t ntotal2 =717184;unsigned int id =0;	unsigned int id2 =0;settings_init();slabs_init(settings.maxbytes, settings.factor, preallocate);if (settings.use_cas) {ntotal += sizeof(uint64_t);}id=slabs_clsid(ntotal);it1=slabs_alloc(ntotal, id);it2=slabs_alloc(ntotal, id);it3=slabs_alloc(ntotal, id);if (settings.use_cas) {ntotal2 += sizeof(uint64_t);}id2=slabs_clsid(ntotal2);it4=slabs_alloc(ntotal2, id2);it5=slabs_alloc(ntotal2, id2);

相关文章:

Google重磅发布开源库TFQ,快速建立量子机器学习模型

整理 | 弯月编辑 | 郭芮出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;近日&#xff0c;Google 与滑铁卢大学、大众汽车等联合发布 TensorFlow Quantum&#xff08;TFQ&#xff09;&#xff0c;一个可快速建立量子机器学习模型原型的开源库。TFQ提供了必…

.net3.5的安装与修复

<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />.net3.5的安装与修复.net3.5正常安装的顺序是先安装windows IIS组件&#xff0c;然后再安装.net3.5安装包&#xff0c;而.net3.5安装包的下载地址可以去百度和google上搜一下&…

jquery easy ui 简单字段选择搜索实现

写的比较粗糙&#xff0c;望见谅。 要实现的效果&#xff1a; 代码如下&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>jQuery EasyUI Application Demo</title><link rel"stylesheet" t…

训练数据也外包?这家公司“承包”了不少注释训练数据,原来是这样做的……...

作者 | Lionbridge AI译者 | 天道酬勤 责编 | 徐威龙封图| CSDN│下载于视觉中国出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;在机器学习领域&#xff0c;训练数据准备是最重要且最耗时的任务之一。实际上&#xff0c;许多数据科学家声称数据科学的很…

JavaScript Switch 语句

avaScript Switch 语句如果希望选择执行若干代码块中的一个&#xff0c;你可以使用 switch 语句&#xff1a;语法&#xff1a;switch(n){case 1:执行代码块 1breakcase 2:执行代码块 2breakdefault:如果n即不是1也不是2&#xff0c;则执行此代码}工作原理&#xff1a;switch 后…

参观Speedy Cloud 有感

上周老男孩的所有学生参观了Speedy Cloud &#xff0c;在这里我首先感谢Speedy Cloud的邀请和服务&#xff0c;我们每一个同学的收获都很大&#xff0c;不管是在以后的发展&#xff0c;还是现在的学习&#xff0c;都给了我很大的推动作用&#xff0c;帮助我去了解计算机的发展的…

C语言文件操作函数大全

unix中一切皆文件&#xff0c;所以文件操作至关重要&#xff01; clearerr&#xff08;清除文件流的错误旗标&#xff09; 相关函数 feof 表头文件 #include<stdio.h> 定义函数 void clearerr(FILE * stream); 函数说明 clearerr&#xff08;&#xff09;清除参数stre…

Python进阶之递归函数的用法及其示例

作者 | 程序员adny责编 | 徐威龙封图| CSDN│下载于视觉中国出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09;本篇文章主要介绍了Python进阶之递归函数的用法及其示例&#xff0c;现在分享给大家&#xff0c;也给大家做个参考。一起来看看吧。递归是指函数…

万科的千亿修炼

一位知名企业家说过&#xff0c;改革开放30年只是完成了市场经济的启蒙和启动&#xff0c;在未来的10至20年&#xff0c;中国将出现一批千亿级的企业。此次金融危机在为经济带来震荡的同时&#xff0c;也为我们提供了一个极佳的视角和机会&#xff0c;审视中国千亿企业的过去、…

nginx 开发一个简单的 HTTP 模块

2019独角兽企业重金招聘Python工程师标准>>> 1. 下载 Nginx http://nginx.org/ 2. 目录结构 $ tree -L 2 . ├── mytest_module │ ├── config │ └── ngx_http_mytest_module.c └── nginx ... 3. config # 在 configure 执行时使用 ngx_addon_nam…

Jquery 之Ajax方法$.get() 的运用,扩展链接模型的天地

如前文说道的那个表现层和业务层的链接模型&#xff0c;从上篇博文&#xff08;http://www.cnblogs.com/AflutterFeather/archive/2010/01/07/1641315.html&#xff09;中可以看到&#xff1a;我们通过WebService提供的方法来获取服务端的返回值。 如果不采用WebService&#…

B树建立与遍历

# include <stdio.h> # include <stdlib.h># include "btrees.h"/* 给一个结点分配空间 */ struct btnode * allocateNode(struct btnode *ptr){int i,max;ptr (struct btnode *)malloc(sizeof(struct btnode));if(!ptr){printf("allocated error!…

2.2版本发布!TensorFlow推出开发者技能证书

作者 | 弯月出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;受 COVID-19 的影响&#xff0c;今年的 TensorFlow 开发者大会于2020年3月12日&#xff08;北京时间&#xff09;凌晨以线上直播的方式与全球开发者见面。Google决定开源TensorFlow是为了让每个开发人员和研…

X3D中Profile如何翻译

问题在哪 在计算机术语中&#xff0c;Profile其实是很难用中文对应的词汇来翻译的一个单词。 在X3D国际标准中&#xff0c;就出现了Profile。它把软件产品对X3D的功能实现范围和相应支持程度做了预先的约定&#xff0c;分为Core Profile、Interchange Profile、Interactive Pro…

腾讯提结合ACNet进行细粒度分类,效果达到最新SOTA | CVPR 2020

作者 | VincentLee来源 | 晓飞的算法工程笔记细粒度分类(Fine-Grained Visual Categorization, FGVC)是图片分类的一个分支&#xff0c;由于类别间的相似性非常大&#xff0c;一般人比较难区分&#xff0c;所以是个很有研究意义的领域。受神经树研究的启发&#xff0c;论文设计…

asp.net mvc view中支持多个实体强类型小技巧

在MVC的开发过程中&#xff0c;在一个View里面可能需要调用多个对象&#xff0c;可是传统的方法是一次只能压入一个对象到View里面&#xff0c;这点并不像Castle框架的MVC好用&#xff0c;在Castle里面&#xff0c;可以很方便的把对象压入到前台Html里面&#xff0c;然后通过Ve…

使用指针做函数返回值

使用指针做函数返回值 1、当使用指针做为函数的返回值时&#xff0c;主函数处的char *p;将获得调用函数char *pf;的值&#xff0c;即一个地址值&#xff0c;如oxAE72。此时需要我们注意的是该地址值所指向的空间是否存在(即已向操作系统声明注册&#xff0c;不会被释放&#x…

Android Studio快捷键每日一练(2)

原文地址&#xff1a;http://www.developerphil.com/android-studio-tips-of-the-day-roundup-2/ 12、复制行 苹果&#xff1a;CmdD Windows&#xff1a;CtrlD 顾名思义&#xff0c;就是拷贝当前行并粘贴在下一行&#xff0c;整个过程无需和剪贴板交互。这个功能配合行移动快…

C语言字符char和整型int的关系

C语言并无char类型&#xff0c;就是用Int表示char的&#xff01;char占一个字节&#xff0c;在C语言所有类型中最小。 char *占4字节&#xff08;32位&#xff09;&#xff0c;8字节&#xff08;64位&#xff09; 在C语言中&#xff0c;实际上字符型数据在内存中是以二进制形式…

PyTorch关键算法疑似侵权,Facebook被起诉

作者 | 神经星星来源 | HyperAI超神经&#xff08;ID:HyperAI&#xff09;近期&#xff0c;一纸诉讼书引起社区的广泛讨论。该诉讼由创业公司 Neural Magic 发起&#xff0c;指控 Facebook 发布到 GitHub 的神经网络软件&#xff0c;使用了他们开发的核心算法。而泄露机密的人&…

大数据高效复制的处理案例分析总结

一个老客户提出这样的需求&#xff0c;希望将SQLServer中的某个表的数据快速复制到SQLite数据库里面以便进行定期的备份处理&#xff0c;数据表的记录大概有50多万条记录&#xff0c;表有100个字段左右&#xff0c;除了希望能够快速做好外&#xff0c;效率是第一位的&#xff0…

memset函数使用详解

1.void *memset(void *s,int c,size_t n) 总的作用&#xff1a;将已开辟内存空间 s 的首 n 个字节的值设为值 c。 2.例子 &#xff03;include void main(){ char *s"Golden Global View"; clrscr(); memset(s,G,6); printf("%s",s); getchar(); ret…

节后招人平均工资9000上热搜,为什么有些人去哪里都值钱?

我”荒“了。这是很多中国AI企业的现状。《人民日报》报道称&#xff0c;我国AI的人才缺口超过500万&#xff0c;供求比例仅为1&#xff1a;10&#xff01;很多企业已经开始面临“人才荒”的窘境&#xff0c;外媒爆料说&#xff0c;中国企业已经不断在硅谷挖人了&#xff01;目…

关于定于如何弄的漂亮点

</div></div><div class"panel"><h5 οnclickshowhidediv("sidebar_rss");>订阅博客</h5><div class"panel-content" id"sidebar_rss" style"display: block"><ul class"list&…

Happy New Year 2016

大学之前的时间都是按天来过的&#xff0c;期盼着一天一天地快快长大&#xff0c;期盼着过年穿新衣&#xff0c;阖家团聚&#xff0c;其乐融融&#xff1b; 大学的时间都是按周来过的&#xff0c;根据每周的课表周而复始&#xff0c;虽然单调但也是自由自在&#xff0c;简单充实…

HashTable原理与实现

memcached中hashtable部分的源码&#xff0c;hash部分的源码主要分布在assoc.h/c、hash.h/c中&#xff0c;总得来说代码比较简单&#xff0c;这里就稍微介绍一下。hashtable通常包括哈希函数和解决冲突的方法两个最主要的因素&#xff0c;memcached使用的哈希函数为Bob Jenkins…

as3自定义加载图片类

ImageLoader.as类&#xff1a; package{ import flash.display.Bitmap; import flash.display.Loader; import flash.display.Sprite; import flash.events.Event; import flash.events.ProgressEvent; import flash.net.URLRequest; /** * 图片加载类…

想成为一个数据科学家却不知道从何下手?这份路线图带你打开数据科学大门!...

作者 | Jane译者 | 火火酱 责编 | 徐威龙出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;你想成为一名数据科学家吗&#xff1f;你对数据科学了解很多&#xff0c;想知道关于数据科学天花乱坠的宣传都在讲什么吗&#xff1f;那好&#xff0c;你算是来对了地方。在过去…

bzoj 1691: [Usaco2007 Dec]挑剔的美食家

Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 621 Solved: 280[Submit][Status][Discuss]Description 与很多奶牛一样&#xff0c;Farmer John那群养尊处优的奶牛们对食物越来越挑剔&#xff0c;随便拿堆草就能打发她们午饭的日子自然是一去不返了。现在&#xff0c;Farmer…

PHP内核中的哈希表结构

https://github.com/HonestQiao/tipi/commit/17ca680289e490763a6a402f79afa2a13802bb36 下载&#xff1a;https://github.com/HonestQiao/tipi/tree/master/book/sample/chapt03 原文地址&#xff1a;http://www.nowamagic.net/librarys/veda/detail/1344 PHP中使用最为频…