SGLang的transfer调用时机
调用/v1/chat/completions接口时,会走到generate接口 —> _global_state.tokenizer_manager.generate_request() —> self._send_one_request() —> self.send_to_scheduler.send_pyobj(),将request发送到两个server的scheduler。
KVPoll的status

KVManager为每个prefill与decode最早便创建的对象,当有一个request到来时,再为每个request建立一个KVSender和一个KVReceiver,KVSender和KVRecevier标识着一对request的结束,用来在应用层监听
在KVSender收到了对应的notify信息后,会开始prefill的计算,当计算完后,调用到batchTransferSync(…)下发发送任务
调用链
主thread
每个transferEngine有一个multiTransport,multiTransport有多个对应的transport(包含RdmaTransport),RdmaTransport有多个RdmaContext(网卡),每个RdmaContext有多个RdmaContext,每个RdmaContext有多个WorkerPool
transferEngine→batchTransferSync
# add task within max_retry_time
transferEngine→submitTransfer
# add transfer to multiTransport(do nothing)
multiTransport→submitTransfer
# add transfer to different transfer type(rdma/hccl)
RdmaTransport→submitTransferTask
# add transfer task to related rdma context(device)
# split task into serveral slices with kBlockSize(65536, 2^18)
RdmaContext->submitPostSend
# add slices of device to its worker_pool(each device have one worker pool)
WorkerPool->submitPostSend
# add to slice_list_map[shared_id]worker thread
workerPool->transferWorker
# use transferWorker thread to Monitor if there are datas to transfer
workerPool->performPostSend
# send slices in local_slice_queue
RdmaEndPoint->submitPostSend
# issue ibv_post_sendworker thread poll
Mooncake发送的buffer注册与qp建联
qp建联
粒度为RdmaEndpoints
int RdmaEndPoint::setupConnectionsByActive()函数建联
主要关注如何交换qp信息

主要通过调用MooncakeEngine中的sendHandShake来交换双方信息,需要交换的信息有本地的nic_path,对端的nic_path以及本地的qp_num(标识对应的qp,而非qp数量),调用的对象为endpoints,而endpoints可以从context→endpoints[peer_server_name]获取,每对端点之间使用不同的qp,每个endpoints唯一标识一个对端结点,可以通过peer_nic_path_指定
// get endpoint by name
auto endpoint = context_.endpoint(entry.first);int TransferMetadata::sendHandshake(const std::string &peer_server_name,
const HandShakeDesc &local_desc,
HandShakeDesc &peer_desc) {
RpcMetaDesc peer_location;
if (getRpcMetaEntry(peer_server_name, peer_location)) {
return ERR_METADATA;
}
auto local = TransferHandshakeUtil::encode(local_desc);
Json::Value peer;
int ret = handshake_plugin_->send(peer_location.ip_or_host_name,
peer_location.rpc_port, local, peer);
if (ret) return ret;
TransferHandshakeUtil::decode(peer, peer_desc);
if (!peer_desc.reply_msg.empty()) {
LOG(ERROR) << "Handshake rejected by " << peer_server_name << ": "
<< peer_desc.reply_msg;
return ERR_METADATA;
}
return 0;
}handshake_plugin的send为同步阻塞操作,因而可以保证建联操作两端有一个影式同步,handshake_plugin为SocketHandShakePlugin结构,实际调用的send函数为doSend,其中涉及将local发送至对端,并接收对端的peer,此处本质是阻塞的同步
int doSend(struct addrinfo *addr, const Json::Value &local,
Json::Value &peer) {
int conn_fd = -1;
int ret = doConnect(addr, conn_fd);
if (ret) {
return ret;
}
ret = writeString(conn_fd, HandShakeRequestType::Connection,
Json::FastWriter{}.write(local));
if (ret) {
LOG(ERROR)
<< "SocketHandShakePlugin: failed to send handshake message: "
"malformed json format, check tcp connection";
close(conn_fd);
return ret;
}
Json::Reader reader;
auto [type, json_str] = readString(conn_fd);
if (type != HandShakeRequestType::Connection) {
LOG(ERROR)
<< "SocketHandShakePlugin: unexpected handshake message type";
close(conn_fd);
return ERR_SOCKET;
}
if (!reader.parse(json_str, peer)) {
LOG(ERROR) << "SocketHandShakePlugin: failed to receive handshake "
"message: "
"malformed json format, check tcp connection";
close(conn_fd);
return ERR_MALFORMED_JSON;
}
close(conn_fd);
return 0;
}Buffer注册
粒度为RdmaContext,设备粒度,不同的endpoints可以共用同一个rkey和lkey,通过调用RdmaContext::registerMemoryRegion(…)可以注册一块内存,将注册后的内存句柄放入memory_region_list中,在需要发送时,根据对应的add找到对应的mr
int RdmaContext::registerMemoryRegion(void *addr, size_t length, int access) {
if (length > (size_t)globalConfig().max_mr_size) {
PLOG(WARNING) << "The buffer length exceeds device max_mr_size, "
<< "shrink it to " << globalConfig().max_mr_size;
length = (size_t)globalConfig().max_mr_size;
}
ibv_mr *mr = ibv_reg_mr(pd_, addr, length, access);
if (!mr) {
PLOG(ERROR) << "Failed to register memory " << addr;
return ERR_CONTEXT;
}
RWSpinlock::WriteGuard guard(memory_regions_lock_);
memory_region_list_.push_back(mr);
return 0;
}获取lkey和rkey的函数如下,仅展示lkey部分函数,rkey同理
uint32_t RdmaContext::lkey(void *addr) {
RWSpinlock::ReadGuard guard(memory_regions_lock_);
for (auto iter = memory_region_list_.begin();
iter != memory_region_list_.end(); ++iter)
if ((*iter)->addr <= addr &&
addr < (char *)((*iter)->addr) + (*iter)->length)
return (*iter)->lkey;
LOG(ERROR) << "Address " << addr << " lkey not found for " << deviceName();
return 0;
}调用registerMemoryRegion(…)的时机为在RdmaTransport::registerLocalMemory(…)函数中调用,在函数中,会把每个context(device)均对buff注册内存,并将lkey和rkey存到buffer_desc的lkey和rkey的队列中
context_list_的初始化在RdmaTransport::initializeRdmaResources(…)
Mooncake ibgda集成
通信comm添加
p2pibgda通信的核心结构为p2pComm,每对p2p维护一个该结构,考虑在RdmaEndPoints中添加该结构(每对连接粒度,RdmaContext为网卡粒度,针对不同的对端ip与网卡,有不同的RdmaEndPoints,其中存放实际建联的qp与cq等)
新增ibgdaEndPoints