libsvm代码阅读(3):关于Cache类的分析(转)

下面来分析Cache类的源码,该类位于svm.cpp中。这个类的主要功能是:负责运算所涉及的内存管理,包括申请、释放等。

简单来说:这个Cache类,首先通过Cache构造函数申请一块空间,这块空间的大小是:L个head_t大小的空间。然后get_data函数保证结构head_t中至少有len个float的内存,并且将可以使用的内存块的指针放在data指针中;而swap_index函数则是用于交换head[i]和head[j]。

Cache类的定义如下:

[cpp]       view plain   copy      

<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">

  1. class Cache
  2. {
  3. public:
  4. Cache(int l,long int size);
  5. ~Cache();
  6. // request data [0,len)
  7. // return some position p where [p,len) need to be filled
  8. // (p >= len if nothing needs to be filled)
  9. int get_data(const int index, Qfloat **data, int len);
  10. void swap_index(int i, int j);
  11. private:
  12. int l;
  13. long int size;//所指定的全部内存,据说用Mb做单位
  14. //结构head_t用来记录所申请内存的指针,并记录长度,而且通过双向的指针,形成链表,增加寻址的速度
  15. struct head_t
  16. {
  17. head_t *prev, *next;    // a circular list是一个双向链表,非循环链表
  18. Qfloat *data;
  19. int len;        // data[0,len) is cached in this entry
  20. };
  21. head_t *head;//变量指针,该指针记录程序中所申请的内存。
  22. head_t lru_head;//双向链表的表头
  23. void lru_delete(head_t *h);//从双向链表中删去某个元素的链接,一般是删去当前所指向的元素
  24. void lru_insert(head_t *h);//在链表后面插入一个新的链接
  25. };

主要包含:

  1. 两个int变量,分别是l和size,l是样本个数,size是指定的全部内存;
  2. 一个构造函数Cache,该函数根据样本数l申请L个head_t的空间;
  3. 一个析构函数~Cache,不用多说;
  4. 一个双向链表的结构head_t;
  5. 一个get_data函数,具体下文再说;
  6. 一个swap_index函数,交换两个head_t的值;
  7. 一个双向链表的删除函数lru_delete,一个插入函数lru_insert;
  8. 一个变量指针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">

  1. Cache::Cache(int l_,long int size_):l(l_),size(size_)
  2. {
  3. //calloc函数的功能与malloc函数的功能相似,都是从堆分配内存该函数与malloc函数的一个显著不同时
  4. //是,calloc函数得到的内存空间是经过初始化的,其内容全为0。
  5. head = (head_t *)calloc(l,sizeof(head_t));  // initialized to 0
  6. size /= sizeof(Qfloat);//先将原来byte数目转化为float的数目。
  7. size -= l * sizeof(head_t) / sizeof(Qfloat);//扣除掉L个head_t的内存数目
  8. size = max(size, 2 * (long int) l); // cache must be large enough for two columns
  9. lru_head.next = lru_head.prev = &lru_head;
  10. }

该函数根据实参:样本数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">

  1. Cache::~Cache()
  2. {
  3. for(head_t *h = lru_head.next; h != &lru_head; h=h->next)
  4. free(h->data);
  5. free(head);
  6. }

这个很简单,不用多说。

双向链表的删去函数代码:

[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">

  1. void Cache::lru_delete(head_t *h)
  2. {
  3. // delete from current location
  4. h->prev->next = h->next;
  5. h->next->prev = h->prev;
  6. }

该函数用于后面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">

  1. void Cache::lru_insert(head_t *h)
  2. {
  3. // insert to last position
  4. h->next = &lru_head;
  5. h->prev = lru_head.prev;
  6. h->prev->next = h;
  7. h->next->prev = h;
  8. }

该函数用于后面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">

  1. int Cache::get_data(const int index, Qfloat **data, int len)
  2. {
  3. head_t *h = &head[index];
  4. if(h->len) lru_delete(h);
  5. int more = len - h->len;
  6. <span style="white-space:pre">  </span>//因为shrink后h->len可能变小,那么more>0,现在要做的就是重新filled,即把内存变成len那么大(主要是通过realloc实现的)
  7. if(more > 0)
  8. {
  9. // free old space,这一步一般不会运行
  10. while(size < more)   //size为所指定的全部内存
  11. {
  12. head_t *old = lru_head.next;
  13. lru_delete(old);
  14. free(old->data);
  15. size += old->len;
  16. old->data = 0;
  17. old->len = 0;
  18. }
  19. // allocate new space
  20. h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len);//把内存扩大到len
  21. size -= more;
  22. swap(h->len,len);
  23. }
  24. lru_insert(h);
  25. *data = h->data;
  26. return len;
  27. }

该函数主要的就是每个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">

  1. void Cache::swap_index(int i, int j)
  2. {
  3. if(i==j) return;
  4. if(head[i].len) lru_delete(&head[i]);//断开双向链表
  5. if(head[j].len) lru_delete(&head[j]);//断开双向链表
  6. swap(head[i].data,head[j].data);
  7. swap(head[i].len,head[j].len);
  8. if(head[i].len) lru_insert(&head[i]);
  9. if(head[j].len) lru_insert(&head[j]);
  10. if(i>j) swap(i,j);
  11. for(head_t *h = lru_head.next; h!=&lru_head; h=h->next)
  12. {
  13. if(h->len > i)
  14. {
  15. if(h->len > j)
  16. swap(h->data[i],h->data[j]);
  17. else
  18. {
  19. // give up
  20. lru_delete(h);
  21. free(h->data);
  22. size += h->len;
  23. h->data = 0;
  24. h->len = 0;
  25. }
  26. }
  27. }
  28. }

这个函数就是交换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">

    1. //
    2. // Kernel Cache
    3. //
    4. // l is the number of total data items
    5. // size is the cache size limit in bytes
    6. //
    7. //类Cache负责运算所涉及的内存的管理,包括申请、释放等
    8. class Cache
    9. {
    10. public:
    11. Cache(int l,long int size);
    12. ~Cache();
    13. // request data [0,len)
    14. // return some position p where [p,len) need to be filled
    15. // (p >= len if nothing needs to be filled)
    16. int get_data(const int index, Qfloat **data, int len);
    17. void swap_index(int i, int j);
    18. private:
    19. int l;
    20. long int size;//所指定的全部内存,据说用Mb做单位
    21. //结构head_t用来记录所申请内存的指针,并记录长度,而且通过双向的指针,形成链表,增加寻址的速度
    22. struct head_t
    23. {
    24. head_t *prev, *next;    // a circular list是一个双向链表,非循环链表
    25. Qfloat *data;
    26. int len;        // data[0,len) is cached in this entry
    27. };
    28. head_t *head;//变量指针,该指针记录程序中所申请的内存。
    29. head_t lru_head;//双向链表的表头
    30. void lru_delete(head_t *h);//从双向链表中删去某个元素的链接,一般是删去当前所指向的元素
    31. void lru_insert(head_t *h);//在链表后面插入一个新的链接
    32. };
    33. Cache::Cache(int l_,long int size_):l(l_),size(size_)
    34. {
    35. //calloc函数的功能与malloc函数的功能相似,都是从堆分配内存该函数与malloc函数的一个显著不同时
    36. //是,calloc函数得到的内存空间是经过初始化的,其内容全为0。
    37. head = (head_t *)calloc(l,sizeof(head_t));  // initialized to 0
    38. size /= sizeof(Qfloat);//先将原来byte数目转化为float的数目。
    39. size -= l * sizeof(head_t) / sizeof(Qfloat);//扣除掉L个head_t的内存数目
    40. size = max(size, 2 * (long int) l); // cache must be large enough for two columns
    41. lru_head.next = lru_head.prev = &lru_head;
    42. }
    43. Cache::~Cache()
    44. {
    45. for(head_t *h = lru_head.next; h != &lru_head; h=h->next)
    46. free(h->data);
    47. free(head);
    48. }
    49. void Cache::lru_delete(head_t *h)
    50. {
    51. // delete from current location
    52. h->prev->next = h->next;
    53. h->next->prev = h->prev;
    54. }
    55. void Cache::lru_insert(head_t *h)
    56. {
    57. // insert to last position
    58. h->next = &lru_head;
    59. h->prev = lru_head.prev;
    60. h->prev->next = h;
    61. h->next->prev = h;
    62. }
    63. //该函数保证head_t[index]中至少有len个float的内存,并且将可以使用的内存块的指针放在data指针中,返回值为申请到的内存
    64. int Cache::get_data(const int index, Qfloat **data, int len)
    65. {
    66. head_t *h = &head[index];
    67. if(h->len) lru_delete(h);
    68. int more = len - h->len;
    69. if(more > 0)
    70. {
    71. // free old space
    72. while(size < more)   //size为所指定的全部内存
    73. {
    74. head_t *old = lru_head.next;
    75. lru_delete(old);
    76. free(old->data);
    77. size += old->len;
    78. old->data = 0;
    79. old->len = 0;
    80. }
    81. // allocate new space
    82. h->data = (Qfloat *)realloc(h->data,sizeof(Qfloat)*len);
    83. size -= more;
    84. swap(h->len,len);
    85. }
    86. lru_insert(h);
    87. *data = h->data;
    88. return len;
    89. }
    90. void Cache::swap_index(int i, int j)
    91. {
    92. if(i==j) return;
    93. if(head[i].len) lru_delete(&head[i]);//断开双向链表
    94. if(head[j].len) lru_delete(&head[j]);//断开双向链表
    95. swap(head[i].data,head[j].data);
    96. swap(head[i].len,head[j].len);
    97. if(head[i].len) lru_insert(&head[i]);
    98. if(head[j].len) lru_insert(&head[j]);
    99. if(i>j) swap(i,j);
    100. for(head_t *h = lru_head.next; h!=&lru_head; h=h->next)
    101. {
    102. if(h->len > i)
    103. {
    104. if(h->len > j)
    105. swap(h->data[i],h->data[j]);
    106. else
    107. {
    108. // give up
    109. lru_delete(h);
    110. free(h->data);
    111. size += h->len;
    112. h->data = 0;
    113. h->len = 0;
    114. }
    115. }
    116. }
    117. }
时间: 2024-11-05 18:44:52

libsvm代码阅读(3):关于Cache类的分析(转)的相关文章

libsvm代码阅读:关于Solver类分析(二)(转)

如果你看完了上篇博文的伪代码,那么我们就可以开始谈谈它的源代码了. 下面先贴出它的类定义,一些成员函数的具体实现先忽略. [cpp]   view plain copy   <EMBED id=ZeroClipboardMovie_1 height=18 name=ZeroClipboardMovie_1 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflas

libsvm代码阅读:关于Solver类分析(一)(转)

如果你看完了上篇博文的伪代码,那么我们就可以开始谈谈它的源代码了. 下面先贴出它的类定义,一些成员函数的具体实现先忽略. [cpp]   view plain copy   <EMBED id=ZeroClipboardMovie_1 height=18 name=ZeroClipboardMovie_1 type=application/x-shockwave-flash align=middle pluginspage=http://www.macromedia.com/go/getflas

libsvm代码阅读:关于Kernel类分析(转)

这一篇博文来分析下Kernel类,代码上很简单,一般都能看懂.Kernel类主要是为SVM的核函数服务的,里面实现了SVM常用的核函数,通过函数指针来使用这些核函数. 其中几个常用核函数如下所示:(一般情况下,使用RBF核函数能取得很好的效果) 关于基类QMatrix在Kernel中的作用并不明显,只是定义了一些纯虚函数,Kernel继承这些函数,Kernel只对swap_index进行了定义.其余的get_Q和get_QD在Kernel并没有用到. [cpp]   view plain cop

libsvm代码阅读(1):基础准备与svm.h头文件(转)

libsvm是国立台湾大学Chih-Jen Lin开发的一个SVM的函数库,是当前应用最广泛的svm函数库,从2000年到2010年,该函数库的下载量达到250000之多.它的最新版本是version 3.17,主要是对是svm_group_classes做了修改. 主页:LIBSVM -- A Library for Support Vector Machines 下载地址:zip.file ortar.gz 我下载后的解压文件如下所示: libsvm函数包的组织结构如下: 1.主文件路径:包

libsvm代码阅读(2):svm.cpp浅谈和函数指针(转)

svm.cpp浅谈 svm.cpp总共有3159行代码,实现了svm算法的核心功能,里面总共有Cache.Kernel.ONE_CLASS_Q.QMatrix.Solver.Solver_NU.SVC_Q.SVR_Q 8个类(如下图1所示),而它们之间的继承和组合关系如图2.图3所示.在这些类中Cache.Kernel.Solver是核心类,对整个算法起支撑作用.在以后的博文中我们将对这3个核心类做重点注解分析,另外还将对svm.cpp中的svm_train函数做一个注解分析. 图1 图2 图3

libsvm代码阅读:关于svm_group_classes函数分析(转)

目前libsvm最新的version是3.17,主要的改变是在svm_group_classes函数中加了几行代码.官方的说明如下: Version 3.17 released on April Fools' day, 2013. We slightly adjust the way class labels are handled internally. By default labels are ordered by their first occurrence in the trainin

libsvm代码阅读:关于svm_train函数分析(转)

在svm中,训练是一个十分重要的步骤,下面我们来看看svm的train部分. 在libsvm中的svm_train中分别有回归和分类两部分,我只对其中分类做介绍. 分类的步骤如下: 统计类别总数,同时记录类别的标号,统计每个类的样本数目 将属于相同类的样本分组,连续存放 计算权重C 训练n(n-1)/2 个模型 初始化nozero数组,便于统计SV //初始化概率数组 训练过程中,需要重建子数据集,样本的特征不变,但样本的类别要改为+1/-1 //如有必要,先调用svm_binary_svc_p

《代码阅读方法与实践》阅读笔记之二

时间过得真快,一转眼,10天就过去了,感觉上次写阅读笔记的场景仿佛还历历在目.<代码阅读方法与实践>这本书真的很难写笔记,本来我看这本书的名字还以为书里大概写的都是些代码阅读的简易方法,心想着这就好写笔记了,没想到竟然好多都是我们之前学过的东西,这倒让我有点无从下手了.大概像我们这些还没有太多经历的大学生,总是习惯于尽量避免自己的工作量,总是试图找到一些完成事情的捷径吧.总之,尽管我不想承认,但我自己心里很清楚,我就是这种人.下面开始言归正传,说说接下来的几章内容归纳. 这本书在前面已经分析了

代码阅读问题

---恢复内容开始--- 下面列举阅读代码过程中遇到的问题和相应的资料查询: 1.namespace的用途:http://www.kuqin.com/language/20080107/3532.html 2.enum 的用途:http://pcedu.pconline.com.cn/empolder/gj/c/0502/562347.html 3.SFML:http://www.sfml-dev.org/ 4.双冒号的用法:http://blog.csdn.net/zimingjushi/ar