DAOS高性能设计之网络上下文内存池(复用HG内存控制器)

2023-11-01 23:25:48 浏览数 (2)

DAOS高性能设计之CART上下文复用(内存池)

优势

1. 免去每次申请内存的开销

2. 动态扩容, 内存池中元素不够时,动态增长

3. 预分配和最大内存个数, 控制内存使用

核心流程总结

1. 在创建网络上下文(crt_context_create)的时候, 初始化内存池(crt_hg_pool_init)

2. 初始化自旋锁(D_SPIN_INIT), 池链表(D_INIT_LIST_HEAD), 设置池元素最大个数(512)和预分配个数(16)并启用内存池(crt_hg_pool_enable)

3. 按预分配个数, 准备上下文(HG_Create), 插入池队列尾部(d_list_add_tail)

4. 发送RPC时, 从池中获取上下文(crt_hg_pool_get), 从池队列头部弹出一个元素并返回(d_list_pop_entry)

5. 获取后判空, 如果池子为空, 则扩容, 即创建上下文(HG_Create)

6. RPC用完后, 通过减引用计数(RPC_DECREF(RPC))中去销毁cart请求(crt_req_destroy)

7. 重置请求上的内存(HG_Free_output), 终结清空RPC(crt_rpc_priv_fini(rpc_priv))

8. 如果是单个RPC(!crp_coll), 且不是服务端的接收RPC请求(!crp_srv), 且RPC是需要回复(!coi_no_reply), 则将该上下文放回内存池(crt_hg_pool_put, d_list_add_tail)

9. 返回内存池时, 判断是否池满, 池满则直接销毁上下文(D_FREE(hdl))

CART发送RPC的参考调用栈如下

代码语言:javascript复制
crt_req_send(req, shard_update_req_cb, remote_arg)  complete_cb = shard_update_req_cb
    crt_context_req_track
        crt_set_timeout
        epi_req_waitq 如果拥塞就进等待队列
        crt_req_timeout_track
        epi_req_q 否则进飞行队列
    crt_req_send_internal
        crt_req_ep_lc_lookup
            crt_req_send_immediately
                crt_hg_req_create -> 创建请求
                    if (!rpc_priv->crp_opc_info->coi_no_reply) -> 双边(需要回复/响应)
                    rpcid = CRT_HG_RPCID
                    rpc_priv->crp_hdl_reuse = crt_hg_pool_get(hg_ctx) -> 复用HG内存控制器(从内存池获取), 自旋锁, 弹出并返回, 计数器减1
                        D_SPIN_LOCK(&hg_pool->chp_lock)
                        hdl = d_list_pop_entry(&hg_pool->chp_list
                        hg_pool->chp_num--
                        return hdl
                    else
                    rpcid = CRT_HG_ONEWAY_RPCID coi_no_reply:1 /* one-way */ 单程(无需响应,默认)
                    if (rpc_priv->crp_hdl_reuse == NULL) -> 如果内存池为空,则新建HG控制器
                    HG_Create
                      hg_core_create
                        hg_core_alloc_na 分配操作id
                    HG_Reset 重用 rpc_priv->crp_hdl_reuse
                crt_hg_req_send
                    HG_Forward(hdl, crt_hg_req_send_cb,...) 回调机制:forward_cb -> request_callback -> hg_cb(由HG_Trigger执行回调)
                        HG_Core_get_rpc_data
                        hg_set_struct
                            proc_cb in_proc_cb|out_proc_cb  crt_proc_common_hdr 被 HG_Register 注册
                        HG_Core_forward(...,hg_core_forward_cb,...)
                            forward(hg_core_handle) hg_core_forward_na|hg_core_forward_self
                                hg_core_forward_na
                                    hg_core_gen_request_tag
                                    NA_Msg_recv_expected hg_core_recv_output_cb NA_OFI_OP_RESET
                                    NA_Msg_send_unexpected hg_core_send_input_cb
                                        na_ofi_msg_send_unexpected
                                            na_ofi_msg_send
                                                fi_rx_addr index, bits, 偏移和位运算计算出地址
                                                fi_tsend -> rxm_ep_tsend
                                                    ssize_t rxm_get_conn 新建或复用连接
                                                    rxm_send_common
                                                na_ofi_op_retry -> na_ofi_cq_process_retries HG_QUEUE_POP_HEAD
        crt_req_hg_addr_lookup -> 见下面
查找目标地址:
crt_req_send -> crt_req_send_internal -> crt_req_ep_lc_lookup -> crp_hg_addr (na_addr) -> 地址作为 HG_Create 的参数(目的地址) -> crt_req_send_immediately -> crt_hg_req_create RPC_STATE_REQ_SENT 创建请求,设置状态为发送 -> crt_hg_req_send -> HG_Forward -> HG_Core_forward -> forward(hg_core_handle) -> hg_core_forward_na -> NA_Msg_send_unexpected -> na_ofi_msg_send_unexpected -> na_ofi_msg_send -> fi_tsend

目标地址: crp_tgt_uri
crt_req_send_internal -> crt_req_ep_lc_lookup -> crt_req_fill_tgt_uri -> crp_tgt_uri

crt_issue_uri_lookup


crt_req_hg_addr_lookup -> 根据url和标签查找NA地址, 我们有基本 URI 但没有标签的 NA 地址的情况
  HG_Addr_lookup2(crt_ctx->cc_hg_ctx.chc_hgcla, rpc_priv->crp_tgt_uri, &hg_addr) -> 根据目标url查询地址信息
  crt_grp_lc_addr_insert -> 缓存, 根据tag查找hg_addr, 填写crt_ctx的查找缓存中某个标签的hg地址。 在调用此例程之前,标签所在的主机等级必须存在于缓存中
    rank = crt_grp_priv_get_primary_rank(passed_grp_priv, rank) -> 获取主rank
    ctx_idx = crt_ctx->cc_idx
    rlink = d_hash_rec_find(&grp_priv->gp_lookup_cache[ctx_idx],(void *)&rank, sizeof(rank)) -> 根据上下文索引在缓存中查找rank
    li = crt_li_link2ptr(rlink) -> 根据链表找到列表条目
    li->li_tag_addr[tag] = *hg_addr -> 将tag作为key, hg_addr作为value存入标签_地址映射中

晓兵(ssbandjl)

博客: https://logread.cn | https://blog.csdn.net/ssbandjl | https://cloud.tencent.com/developer/user/5060293/articles

DAOS汇总: https://cloud.tencent.com/developer/article/2344030

公众号: 云原生云

晓兵技术杂谈(系列)

https://cloud.tencent.com/developer/user/5060293/video

0 人点赞