下面來分析Cache類的源碼,該類位於svm.cpp中。這個類的主要功能是:負責運算所涉及的內存管理,包括申請、釋放等。web
簡單來講:這個Cache類,首先經過Cache構造函數申請一塊空間,這塊空間的大小是:L個head_t大小的空間。而後get_data函數保證結構head_t中至少有len個float的內存,而且將可使用的內存塊的指針放在data指針中;而swap_index函數則是用於交換head[i]和head[j]。app
Cache類的定義以下:函數
[cpp] view plain copy
oop
<EMBED id=ZeroClipboardMovie_1 height=18 name=ZeroClipboardMovie_1 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=1&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">this
class Cache spa
{ .net
public: 指針
Cache(int l,long int size); code
~Cache(); orm
// request data [0,len)
// return some position p where [p,len) need to be filled
// (p >= len if nothing needs to be filled)
int get_data(const int index, Qfloat **data, int len);
void swap_index(int i, int j);
private:
int l;
long int size;//所指定的所有內存,聽說用Mb作單位
//結構head_t用來記錄所申請內存的指針,並記錄長度,並且經過雙向的指針,造成鏈表,增長尋址的速度
struct head_t
{
head_t *prev, *next; // a circular list是一個雙向鏈表,非循環鏈表
Qfloat *data;
int len; // data[0,len) is cached in this entry
};
head_t *head;//變量指針,該指針記錄程序中所申請的內存。
head_t lru_head;//雙向鏈表的表頭
void lru_delete(head_t *h);//從雙向鏈表中刪去某個元素的連接,通常是刪去當前所指向的元素
void lru_insert(head_t *h);//在鏈表後面插入一個新的連接
};
主要包含:
兩個int變量,分別是l和size,l是樣本個數,size是指定的所有內存;
一個構造函數Cache,該函數根據樣本數l申請L個head_t的空間;
一個析構函數~Cache,不用多說;
一個雙向鏈表的結構head_t;
一個get_data函數,具體下文再說;
一個swap_index函數,交換兩個head_t的值;
一個雙向鏈表的刪除函數lru_delete,一個插入函數lru_insert;
一個變量指針head,該指針指向程序中所申請的內存。
關於上面的結構體head_t,它是一個雙向鏈表方便先後內存的訪問,在文獻LIBSVM:A Libray for SVM中以下說法:
構造函數的代碼以下:
[cpp] view plain copy
<EMBED id=ZeroClipboardMovie_2 height=18 name=ZeroClipboardMovie_2 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=2&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
Cache::Cache(int l_,long int size_):l(l_),size(size_)
{
//calloc函數的功能與malloc函數的功能類似,都是從堆分配內存該函數與malloc函數的一個顯著不一樣時
//是,calloc函數獲得的內存空間是通過初始化的,其內容全爲0。
head = (head_t *)calloc(l,sizeof(head_t)); // initialized to 0
size /= sizeof(Qfloat);//先將原來byte數目轉化爲float的數目。
size -= l * sizeof(head_t) / sizeof(Qfloat);//扣除掉L個head_t的內存數目
size = max(size, 2 * (long int) l); // cache must be large enough for two columns
lru_head.next = lru_head.prev = &lru_head;
}
該函數根據實參:樣本數L,申請L個head_t的空間,初始化爲0;size的處理是:先將輸入的size值(byte單位)轉化爲float的數目,而後再減去L個head_t所佔的空間;其中lru_head由於尚沒有head_t中申請到內存,故雙向鏈表指向本身。
析構函數的代碼以下:
[cpp] view plain copy
<EMBED id=ZeroClipboardMovie_3 height=18 name=ZeroClipboardMovie_3 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=3&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
Cache::~Cache()
{
for(head_t *h = lru_head.next; h != &lru_head; h=h->next)
free(h->data);
free(head);
}
這個很簡單,不用多說。
雙向鏈表的刪去函數代碼:
[cpp] view plain copy
<EMBED id=ZeroClipboardMovie_4 height=18 name=ZeroClipboardMovie_4 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=4&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
void Cache::lru_delete(head_t *h)
{
// delete from current location
h->prev->next = h->next;
h->next->prev = h->prev;
}
該函數用於後面swap_index函數斷開雙向鏈表的實現。只是斷開連接,沒有刪去數據。
雙向鏈表的插入函數代碼:
[cpp] view plain copy
<EMBED id=ZeroClipboardMovie_5 height=18 name=ZeroClipboardMovie_5 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=5&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
void Cache::lru_insert(head_t *h)
{
// insert to last position
h->next = &lru_head;
h->prev = lru_head.prev;
h->prev->next = h;
h->next->prev = h;
}
該函數用於後面swap_index函數恢復前面斷開鏈接的兩個數據的實現。
get_data函數的代碼:
[cpp] view plain copy
<EMBED id=ZeroClipboardMovie_6 height=18 name=ZeroClipboardMovie_6 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=6&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
int Cache::get_data(const int index, Qfloat **data, int len)
{
head_t *h = &head[index];
if(h->len) lru_delete(h);
int more = len - h->len;
<span style="white-space:pre"> </span>//由於shrink後h->len可能變小,那麼more>0,如今要作的就是從新filled,即把內存變成len那麼大(主要是經過realloc實現的)
if(more > 0)
{
// free old space,這一步通常不會運行
while(size < more) //size爲所指定的所有內存
{
head_t *old = lru_head.next;
lru_delete(old);
free(old->data);
size += old->len;
old->data = 0;
old->len = 0;
}
// allocate new space
h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len);//把內存擴大到len
size -= more;
swap(h->len,len);
}
lru_insert(h);
*data = h->data;
return len;
}
該函數主要的就是每一個head[i]具備len大小的內存空間。關於realloc函數是擴大內存用的。
swap_index函數的代碼:
[cpp] view plain copy
<EMBED id=ZeroClipboardMovie_7 height=18 name=ZeroClipboardMovie_7 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=7&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
void Cache::swap_index(int i, int j)
{
if(i==j) return;
if(head[i].len) lru_delete(&head[i]);//斷開雙向鏈表
if(head[j].len) lru_delete(&head[j]);//斷開雙向鏈表
swap(head[i].data,head[j].data);
swap(head[i].len,head[j].len);
if(head[i].len) lru_insert(&head[i]);
if(head[j].len) lru_insert(&head[j]);
if(i>j) swap(i,j);
for(head_t *h = lru_head.next; h!=&lru_head; h=h->next)
{
if(h->len > i)
{
if(h->len > j)
swap(h->data[i],h->data[j]);
else
{
// give up
lru_delete(h);
free(h->data);
size += h->len;
h->data = 0;
h->len = 0;
}
}
}
}
這個函數就是交換head_t[i]和head_t[j]的內容。首先將head_t[i]和head_t[j]從雙向鏈表中脫離出去,而後交換它們的內容,最後從新把它們恢復到鏈表中。
完整代碼以下:
[cpp] view plain copy
<EMBED id=ZeroClipboardMovie_8 height=18 name=ZeroClipboardMovie_8 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflashplayer width=18 src=http://static.blog.csdn.net/scripts/ZeroClipboard/ZeroClipboard.swf wmode="transparent" flashvars="id=8&width=18&height=18" allowfullscreen="false" allowscriptaccess="always" bgcolor="#ffffff" quality="best" menu="false" loop="false">
//
// Kernel Cache
//
// l is the number of total data items
// size is the cache size limit in bytes
//
//類Cache負責運算所涉及的內存的管理,包括申請、釋放等
class Cache
{
public:
Cache(int l,long int size);
~Cache();
// request data [0,len)
// return some position p where [p,len) need to be filled
// (p >= len if nothing needs to be filled)
int get_data(const int index, Qfloat **data, int len);
void swap_index(int i, int j);
private:
int l;
long int size;//所指定的所有內存,聽說用Mb作單位
//結構head_t用來記錄所申請內存的指針,並記錄長度,並且經過雙向的指針,造成鏈表,增長尋址的速度
struct head_t
{
head_t *prev, *next; // a circular list是一個雙向鏈表,非循環鏈表
Qfloat *data;
int len; // data[0,len) is cached in this entry
};
head_t *head;//變量指針,該指針記錄程序中所申請的內存。
head_t lru_head;//雙向鏈表的表頭
void lru_delete(head_t *h);//從雙向鏈表中刪去某個元素的連接,通常是刪去當前所指向的元素
void lru_insert(head_t *h);//在鏈表後面插入一個新的連接
};
Cache::Cache(int l_,long int size_):l(l_),size(size_)
{
//calloc函數的功能與malloc函數的功能類似,都是從堆分配內存該函數與malloc函數的一個顯著不一樣時
//是,calloc函數獲得的內存空間是通過初始化的,其內容全爲0。
head = (head_t *)calloc(l,sizeof(head_t)); // initialized to 0
size /= sizeof(Qfloat);//先將原來byte數目轉化爲float的數目。
size -= l * sizeof(head_t) / sizeof(Qfloat);//扣除掉L個head_t的內存數目
size = max(size, 2 * (long int) l); // cache must be large enough for two columns
lru_head.next = lru_head.prev = &lru_head;
}
Cache::~Cache()
{
for(head_t *h = lru_head.next; h != &lru_head; h=h->next)
free(h->data);
free(head);
}
void Cache::lru_delete(head_t *h)
{
// delete from current location
h->prev->next = h->next;
h->next->prev = h->prev;
}
void Cache::lru_insert(head_t *h)
{
// insert to last position
h->next = &lru_head;
h->prev = lru_head.prev;
h->prev->next = h;
h->next->prev = h;
}
//該函數保證head_t[index]中至少有len個float的內存,而且將可使用的內存塊的指針放在data指針中,返回值爲申請到的內存
int Cache::get_data(const int index, Qfloat **data, int len)
{
head_t *h = &head[index];
if(h->len) lru_delete(h);
int more = len - h->len;
if(more > 0)
{
// free old space
while(size < more) //size爲所指定的所有內存
{
head_t *old = lru_head.next;
lru_delete(old);
free(old->data);
size += old->len;
old->data = 0;
old->len = 0;
}
// allocate new space
h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len);
size -= more;
swap(h->len,len);
}
lru_insert(h);
*data = h->data;
return len;
}
void Cache::swap_index(int i, int j)
{
if(i==j) return;
if(head[i].len) lru_delete(&head[i]);//斷開雙向鏈表
if(head[j].len) lru_delete(&head[j]);//斷開雙向鏈表
swap(head[i].data,head[j].data);
swap(head[i].len,head[j].len);
if(head[i].len) lru_insert(&head[i]);
if(head[j].len) lru_insert(&head[j]);
if(i>j) swap(i,j);
for(head_t *h = lru_head.next; h!=&lru_head; h=h->next)
{
if(h->len > i)
{
if(h->len > j)
swap(h->data[i],h->data[j]);
else
{
// give up
lru_delete(h);
free(h->data);
size += h->len;
h->data = 0;
h->len = 0;
}
}
}
}