vpp之vec学习

vpp之vec学习本文深入解析 VPP 框架中的 vec 动态数组结构与操作 API 包括 vec 的内部组成 内存管理机制及如何通过不同宏指令高效增删元素

大家好,欢迎来到IT知识分享网。

概念理解

一个vec包含以下几部分:

 用户自定义头部(aligned to uword boundary) 可选 vec头部(vec_header_t, 主要是定义了vector length: number of elements) user's pointer-> vector element #0 vector element #1 ... typedef struct { 
    #if CLIB_VEC64 > 0 u64 len; #else u32 len; /< Number of elements in vector (NOT its allocated length). */ u32 dlmalloc_header_offset; /< offset to memory allocator offset */ #endif u8 vector_data[0]; /< Vector data . */ } vec_header_t; 

vpp使用vec_resize_allocate_memory来申请内存,申请到的返回值vec是元素0的地址,不是vec头部地址,需要注意

void * vec_resize_allocate_memory (void *v, word length_increment, uword data_bytes, uword header_bytes, uword data_align) { vec_header_t *vh = _vec_find (v); uword old_alloc_bytes, new_alloc_bytes; void *old, *new; # 获取vec头部大小,这里的header_bytes传进来的时候是私有头部的大小,需要加上sizeof (vec_header_t), 并对齐为2的指数次方 header_bytes = vec_header_bytes (header_bytes); # 更新内存总的长度 data_bytes += header_bytes; # 如果v是空的,则申请内存new,并把v设置为new + header_bytes,即用户数据开始位置 if (!v) { new = clib_mem_alloc_aligned_at_offset (data_bytes, data_align, header_bytes, 1 /* yes, call os_out_of_memory */ ); data_bytes = clib_mem_size (new); clib_memset (new, 0, data_bytes); v = new + header_bytes; # 保存vec元素的数量到_vec_len (v) _vec_len (v) = length_increment; return v; } # 如果v不为空,表示已经存在,需要在原有内存的基础上考虑是否增加 vh->len += length_increment; # 旧的vec头部指针 old = v - header_bytes; # 这里应该是判断旧的vec头部地址在合理的范围内 /* Vector header must start heap object. */ ASSERT (clib_mem_is_heap_object (old)); # 获取旧的vec总的内存大小 old_alloc_bytes = clib_mem_size (old); # 如果旧的vec总的内存够用,则直接返回v即可,不需要申请新的内存 /* Need to resize? */ if (data_bytes <= old_alloc_bytes) return v; # 如果新的内存的大小大于旧的内存的3/2的话,则直接按照新的内存大小申请内存,否则按照旧的内存的3/2大小申请内存 new_alloc_bytes = (old_alloc_bytes * 3) / 2; if (new_alloc_bytes < data_bytes) new_alloc_bytes = data_bytes; # 下面就是申请新的new后拷贝old的数据到new,在返回v + header_bytes给用户 new = clib_mem_alloc_aligned_at_offset (new_alloc_bytes, data_align, header_bytes, 1 /* yes, call os_out_of_memory */ ); /* FIXME fail gracefully. */ if (!new) clib_panic ("vec_resize fails, length increment %d, data bytes %d, alignment %d", length_increment, data_bytes, data_align); clib_memcpy_fast (new, old, old_alloc_bytes); clib_mem_free (old); /* Allocator may give a bit of extra room. */ new_alloc_bytes = clib_mem_size (new); v = new; /* Zero new memory. */ memset (v + old_alloc_bytes, 0, new_alloc_bytes - old_alloc_bytes); return v + header_bytes; } 

实际使用举例说明

创建一个u64的vec指针变量,
第1步、使用vec_add1增加一个elm元素,
第2步、使用vec_add1增加一个elm元素,
第3步、使用vec_add批量增加10个元素,
第4步、使用vec_add2增加10个元素,这个时候,vec里面有22个元素,具体代码如下:

u64 elm[1024]; u64 *vec_u64 = 0; u64 *elm_add2 = 0; int index = 0; u64 *tmp_elm; for (index = 0; index < 1024; index++) { 
    elm[index] = index; } index = 0; vec_add1(vec_u64, elm[index]); index++; Log(CM, DEBUG, "vec_u64:%p", vec_u64); Log(CM, DEBUG, "vec_len:%d", vec_len(vec_u64)); Log(CM, DEBUG, "vec_bytes:%d", vec_bytes(vec_u64)); Log(CM, DEBUG, "vec_capacity:%d", vec_capacity(vec_u64,0)); Log(CM, DEBUG, "vec_max_len:%d", vec_max_len(vec_u64)); Log(CM, DEBUG, "vec_end:%p", vec_end(vec_u64)); vec_add1(vec_u64, elm[index]); index++; Log(CM, DEBUG, "vec_u64:%p", vec_u64); Log(CM, DEBUG, "vec_len:%d", vec_len(vec_u64)); Log(CM, DEBUG, "vec_bytes:%d", vec_bytes(vec_u64)); Log(CM, DEBUG, "vec_capacity:%d", vec_capacity(vec_u64,0)); Log(CM, DEBUG, "vec_max_len:%d", vec_max_len(vec_u64)); Log(CM, DEBUG, "vec_end:%p", vec_end(vec_u64)); vec_add(vec_u64, &elm[index], 10); index+=10; Log(CM, DEBUG, "vec_u64:%p", vec_u64); Log(CM, DEBUG, "vec_len:%d", vec_len(vec_u64)); Log(CM, DEBUG, "vec_bytes:%d", vec_bytes(vec_u64)); Log(CM, DEBUG, "vec_capacity:%d", vec_capacity(vec_u64,0)); Log(CM, DEBUG, "vec_max_len:%d", vec_max_len(vec_u64)); Log(CM, DEBUG, "vec_end:%p", vec_end(vec_u64)); vec_add2(vec_u64, elm_add2, 10); for (index = 0; index < 10; index++) { 
    elm_add2[index] = index; } Log(CM, DEBUG, "vec_u64:%p, elm_add2:%p", vec_u64, elm_add2); Log(CM, DEBUG, "vec_len:%d", vec_len(vec_u64)); Log(CM, DEBUG, "vec_bytes:%d", vec_bytes(vec_u64)); Log(CM, DEBUG, "vec_capacity:%d", vec_capacity(vec_u64,0)); Log(CM, DEBUG, "vec_max_len:%d", vec_max_len(vec_u64)); Log(CM, DEBUG, "vec_end:%p", vec_end(vec_u64)); index = 0; vec_foreach(tmp_elm, vec_u64) { 
    Log(CM, DEBUG, "vec_u64[%d]:%d", index++, *tmp_elm); } if (vec_u64) vec_free(vec_u64); 

这里分别使用了vec_add1、vec_add、vec_add2来往vec里面增加元素,他们使用上的参数有区别,需要注意:
vec_add1(V,E) 第二个参数是elm变量,不是使用变量的地址
vec_add(V,E,N) 第二个参数是elm数组的头地址,不是变量的值,需要注意,虽然这里都用E来表示
vec_add2(V,P,N) 第二个参数是一个指针变量,不需要初始化,vec_add2以后,vec会把申请到的空间地址保存到P参数,
这个时候我们需要把我们要保存的变量保存到P指针的位置。

结果如下:

vec_u64:0x7fc66448f82c vec_len:1 vec_bytes:8 vec_capacity:28 vec_max_len:3 vec_end:0x7fc66448f834 vec_u64:0x7fc66448f82c vec_len:2 vec_bytes:16 vec_capacity:28 vec_max_len:3 vec_end:0x7fc66448f83c vec_u64:0x7fc66446c08c vec_len:12 vec_bytes:96 vec_capacity:108 vec_max_len:13 vec_end:0x7fc66446c0ec vec_u64:0x7fc66330eecc, elm_add2:0x7fc66330ef2c vec_len:22 vec_bytes:176 vec_capacity:188 vec_max_len:23 vec_end:0x7fc66330ef7c vec_u64[0]:0 vec_u64[1]:1 vec_u64[2]:2 vec_u64[3]:3 vec_u64[4]:4 vec_u64[5]:5 vec_u64[6]:6 vec_u64[7]:7 vec_u64[8]:8 vec_u64[9]:9 vec_u64[10]:10 vec_u64[11]:11 vec_u64[12]:0 vec_u64[13]:1 vec_u64[14]:2 vec_u64[15]:3 vec_u64[16]:4 vec_u64[17]:5 vec_u64[18]:6 vec_u64[19]:7 vec_u64[20]:8 vec_u64[21]:9 

api说明

  1. vec_new
/ \brief Create new vector of given type and length (unspecified alignment, no header). @param T type of elements in new vector @param N number of elements to add @return V new vector */ #define vec_new(T,N) vec_new_ha(T,N,0,0) 
  1. vec_free
/ \brief Free vector's memory (no header). @param V pointer to a vector @return V (value-result parameter, V=0) */ #define vec_free(V) vec_free_h(V,0) 

释放vec变量

  1. vec_add1
/ \brief Add 1 element to end of vector (unspecified alignment). @param V pointer to a vector @param E element to add @return V (value-result macro parameter) */ #define vec_add1(V,E) vec_add1_ha(V,E,0,0) 
  1. vec_add2
/ \brief Add N elements to end of vector V, return pointer to new elements in P. (no header, unspecified alignment) @param V pointer to a vector @param P pointer to new vector element(s) @param N number of elements to add @return V and P (value-result macro parameters) */ #define vec_add2(V,P,N) vec_add2_ha(V,P,N,0,0) 

增加N个元素到指定的vec里面,P参数是输入的时候是空指针
返回值是vec变量和P指针,vec会申请的空间地址保存到P参数,
这个时候我们需要把我们要保存的变量保存到P指针的位置

  1. vec_add
/ \brief Add N elements to end of vector V (no header, unspecified alignment) @param V pointer to a vector @param E pointer to element(s) to add @param N number of elements to add @return V (value-result macro parameter) */ #define vec_add(V,E,N) vec_add_ha(V,E,N,0,0) 
  1. _vec_find
/ \brief Find the vector header Given the user's pointer to a vector, find the corresponding vector header @param v pointer to a vector @return pointer to the vector's vector_header_t */ #define _vec_find(v) ((vec_header_t *) (v) - 1) 

根据vec变量获取vec头部地址

  1. vec_len
/ \brief Number of elements in vector (rvalue-only, NULL tolerant) vec_len (v) checks for NULL, but cannot be used as an lvalue. If in doubt, use vec_len... */ #define vec_len(v) ((v) ? _vec_len(v) : 0) 

获取vec内部元素的数量

  1. vec_reset_length
/ \brief Reset vector length to zero NULL-pointer tolerant */ #define vec_reset_length(v) do { if (v) _vec_len (v) = 0; } while (0) 

复位vec元素的长度,内存空间不变化,只是把len设置为0而已

  1. vec_bytes
/ \brief Number of data bytes in vector. */ #define vec_bytes(v) (vec_len (v) * sizeof (v[0])) 

获取vec内部数据的长度,即元素的数量乘以每个元素的大小

  1. vec_capacity
/ \brief Total number of bytes that can fit in vector with current allocation. */ #define vec_capacity(v,b) \ ({ \ void * _vec_capacity_v = (void *) (v); \ uword _vec_capacity_b = (b); \ _vec_capacity_b = sizeof (vec_header_t) + _vec_round_size (_vec_capacity_b); \ _vec_capacity_v ? clib_mem_size (_vec_capacity_v - _vec_capacity_b) : 0; \ }) 

计算动态数组占用的内存空间,也包含2种类型的头部,不仅仅是数据部分

  1. vec_max_len
/ \brief Total number of elements that can fit into vector. */ #define vec_max_len(v) (vec_capacity(v,0) / sizeof (v[0])) 

获取当前已分配的内存最多容纳的元素个数

  1. vec_end
/ \brief End (last data address) of vector. */ #define vec_end(v) ((v) + vec_len (v)) 

当前数组元素的最后一个元素的下一个位置,新元素在这里追加

  1. vec_is_member
/ \brief True if given pointer is within given vector. */ #define vec_is_member(v,e) ((e) >= (v) && (e) < vec_end (v)) 

判断一个元素是否合法的数组元素,是否越界

  1. vec_elt
/ \brief Get vector value at index i checking that i is in bounds. */ #define vec_elt_at_index(v,i) \ ({ \ ASSERT ((i) < vec_len (v)); \ (v) + (i); \ }) / \brief Get vector value at index i */ #define vec_elt(v,i) (vec_elt_at_index(v,i))[0] 
  1. vec_foreach and vec_foreach_backwards
/ \brief Vector iterator */ #define vec_foreach(var,vec) for (var = (vec); var < vec_end (vec); var++) / \brief Vector iterator (reverse) */ #define vec_foreach_backwards(var,vec) \ for (var = vec_end (vec) - 1; var >= (vec); var--) 

循环变量vec所有元素,var得到的是地址,
而vec_foreach是从小到大循环遍历,而vec_foreach_backwards
是从大到小循环遍历

  1. vec_foreach_index and vec_foreach_index_backwards
/ \brief Iterate over vector indices. */ #define vec_foreach_index(var,v) for ((var) = 0; (var) < vec_len (v); (var)++) / \brief Iterate over vector indices (reverse). */ #define vec_foreach_index_backwards(var,v) \ for ((var) = vec_len((v)) - 1; (var) >= 0; (var)--) 

循环变量vec所有元素,var得到的是元素的索引,
而vec_foreach_index是从小到大循环遍历,而vec_foreach_index_backwards
是从大到小循环遍历

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/151588.html

(0)
上一篇 2025-03-12 20:10
下一篇 2025-03-12 20:20

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信