From 32a3f7fdc91cdb065e175a4b117ecd484f23ee64 Mon Sep 17 00:00:00 2001 From: HaoLiuuu Date: Sun, 5 Oct 2025 15:03:19 +0000 Subject: [PATCH 1/9] init the branch --- csrc/engine/rdma/rdma_endpoint.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/csrc/engine/rdma/rdma_endpoint.cpp b/csrc/engine/rdma/rdma_endpoint.cpp index a12443e..58b8cdc 100644 --- a/csrc/engine/rdma/rdma_endpoint.cpp +++ b/csrc/engine/rdma/rdma_endpoint.cpp @@ -11,6 +11,7 @@ #include #include + namespace slime { RDMATask::RDMATask(std::shared_ptr endpoint, From 03b0e195a6a743b42952bf2f9866ae2bab9e7621 Mon Sep 17 00:00:00 2001 From: HaoLiuuu Date: Thu, 9 Oct 2025 08:57:48 +0000 Subject: [PATCH 2/9] add the RDMATaskPool to supoort the pre post recv --- csrc/engine/rdma/rdma_endpoint.cpp | 547 ++++++++++++++++++++++------- csrc/engine/rdma/rdma_endpoint.h | 120 ++++--- 2 files changed, 492 insertions(+), 175 deletions(-) diff --git a/csrc/engine/rdma/rdma_endpoint.cpp b/csrc/engine/rdma/rdma_endpoint.cpp index 58b8cdc..03bf3ed 100644 --- a/csrc/engine/rdma/rdma_endpoint.cpp +++ b/csrc/engine/rdma/rdma_endpoint.cpp @@ -4,126 +4,243 @@ #include "engine/rdma/rdma_buffer.h" #include "engine/rdma/rdma_common.h" #include "engine/rdma/rdma_context.h" -#include "logging.h" + +#include "utils/logging.h" #include #include #include #include - namespace slime { -RDMATask::RDMATask(std::shared_ptr endpoint, - uint32_t task_id, - OpCode opcode, - std::shared_ptr buffer): - endpoint_(endpoint), slot_id_(task_id), opcode_(opcode), buffer_(buffer) +RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, uint32_t task_id) { - registerDataMemoryRegion(); - fillBuffer(); + rdma_endpoint_ = rdma_endpoint; + task_id_ = task_id; + makeDummyAssignmentBatch(); } -RDMATask::~RDMATask() {} +RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rmda_opcode, uint32_t task_id): + rdma_endpoint_(rdma_endpoint), rdma_operation_(rmda_opcode), task_id_(task_id) +{ + unique_slot_id_ = -1; + rdma_buffer_ = nullptr; + makeDummyAssignmentBatch(); +} -std::string RDMATask::getDataKey(int32_t idx) +RDMASendRecvTask::~RDMASendRecvTask() { - std::string data_prefix = opcode_ == OpCode::SEND ? "DATA_SEND" : "DATA_RECV"; - storage_view_batch_t storage_view_batch = buffer_->storageViewBatch(); - return data_prefix + "@" + std::to_string(storage_view_batch[idx].data_ptr) + "_" - + std::to_string(storage_view_batch[idx].length) + "_" + std::to_string(idx); + SLIME_LOG_INFO("Destroy the RDMASendRecvTask: ", task_id_); } -AssignmentBatch RDMATask::getMetaAssignmentBatch() +// 生成meta Assignment 和 data Assignment +int RDMASendRecvTask::makeAssignmentBatch() { - size_t meta_buffer_idx = slot_id_ % MAX_META_BUFFER_SIZE; - switch (opcode_) { - case OpCode::SEND: { - return AssignmentBatch{Assignment("meta_buffer", - meta_buffer_idx * sizeof(meta_data_t), - meta_buffer_idx * sizeof(meta_data_t), - sizeof(meta_data_t))}; + size_t meta_buffer_idx = unique_slot_id_ % MAX_META_BUFFER_SIZE; + if (rdma_operation_ == OpCode::SEND) { + AssignmentBatch batch; + std::string prefix = "DATA_SEND_"; + std::cout << "rdma_buffer_->batch_size()" << rdma_buffer_->batch_size() << std::endl; + for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { + batch.push_back(Assignment(prefix + std::to_string(unique_slot_id_) + "_" + std::to_string(i), + 0, + 0, + rdma_buffer_->view_batch()[i].length)); } - case OpCode::RECV: { - return AssignmentBatch{ + data_assignment_batch_ = batch; + } + if (rdma_operation_ == OpCode::RECV) { + meta_assignment_batch_ = AssignmentBatch{ Assignment("meta_buffer", meta_buffer_idx * sizeof(meta_data_t), MAX_META_BUFFER_SIZE * sizeof(meta_data_t) + meta_buffer_idx * sizeof(meta_data_t), sizeof(meta_data_t))}; - } - default: - SLIME_ABORT("Unknown Opcode"); - } + return 0; } -AssignmentBatch RDMATask::getDataAssignmentBatch() +int RDMASendRecvTask::makeDummyAssignmentBatch() { - AssignmentBatch batch; - storage_view_batch_t storage_view_batch = buffer_->storageViewBatch(); - for (int i = 0; i < buffer_->batchSize(); ++i) { - batch.push_back(Assignment(getDataKey(i), 0, 0, storage_view_batch[i].length)); - } - return batch; + + dum_meta_assignment_batch_ = AssignmentBatch{Assignment("dum_meta_buffer_", 0, 0, 16 * sizeof(uint32_t))}; + dum_data_assignment_batch_ = AssignmentBatch{Assignment("dum_data_buffer_", 0, 0, 16 * sizeof(uint32_t))}; + + return 0; } -int RDMATask::registerDataMemoryRegion() +int RDMASendRecvTask::makeMetaMR() { - auto mr_is_exist = endpoint_->dataCtx()->get_mr(getDataKey(0)); + auto& meta_buffer = rdma_endpoint_->metaBuffer(); - if (mr_is_exist != nullptr) { + std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; + for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { + auto mr = rdma_endpoint_->dataCtx()->get_mr(prefix + std::to_string(unique_slot_id_) + "_" + std::to_string(i)); + if (rdma_operation_ == OpCode::RECV) { + meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i] = + reinterpret_cast(mr->addr); + meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; + meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i] = mr->length; + meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_slot = unique_slot_id_; + } + } - return 0; + return 0; +} + +int RDMASendRecvTask::makeDataMR() +{ + + std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; + std::string MR_KEY = prefix + std::to_string(unique_slot_id_) + "_"; + auto mr = rdma_endpoint_->dataCtx()->get_mr(MR_KEY + std::to_string(0)); + if (mr != nullptr) { + SLIME_LOG_DEBUG("The DATA MR has been REGISTERED! The SLOT_ID is: ", unique_slot_id_); } else { - storage_view_batch_t storage_view_batch = buffer_->storageViewBatch(); - for (int i = 0; i < buffer_->batchSize(); ++i) { - endpoint_->dataCtx()->register_memory_region( - getDataKey(i), storage_view_batch[i].data_ptr, storage_view_batch[i].length); + auto view_batch = rdma_buffer_->view_batch(); + for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { + std::cout << view_batch[i].data_ptr << std::endl; + rdma_endpoint_->dataCtx()->register_memory_region( + MR_KEY + std::to_string(i), view_batch[i].data_ptr, view_batch[i].length); } - return 0; } -} -void RDMATask::fillBuffer() + return 0; +} +int RDMASendRecvTask::makeRemoteDataMR() { - if (opcode_ == OpCode::SEND) { - std::vector& meta_buf = endpoint_->getMetaBuffer(); + if (!rdma_buffer_ || !rdma_endpoint_) { + SLIME_LOG_ERROR("Null pointer detected: rdma_buffer_=" << rdma_buffer_ + << ", rdma_endpoint_=" << rdma_endpoint_); + return -1; } - else if (opcode_ == OpCode::RECV) { - std::vector& meta_buf = endpoint_->getMetaBuffer(); - for (size_t i = 0; i < buffer_->batchSize(); ++i) { - auto mr = endpoint_->dataCtx()->get_mr(getDataKey(i)); - meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i] = - reinterpret_cast(mr->addr); - meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; - meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i] = mr->length; + + std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; + std::string MR_KEY = prefix + std::to_string(unique_slot_id_) + "_"; + auto mr = rdma_endpoint_->dataCtx()->get_remote_mr(MR_KEY + std::to_string(0)); + + if (mr.addr == 0) { + auto& meta_buffer = rdma_endpoint_->metaBuffer(); + for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { + uint64_t addr = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i]; + uint32_t size = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i]; + uint32_t rkey = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i]; + rdma_endpoint_->dataCtx()->register_remote_memory_region(MR_KEY + std::to_string(i), addr, size, rkey); } - meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_slot = slot_id_; - meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_qpidx = - slot_id_ % endpoint_->dataCtxQPNum(); } + else { - SLIME_LOG_ERROR("Unsupported opcode in RDMATask::fillBuffer()"); + SLIME_LOG_DEBUG("The REMOTE DATA MR has been REGISTERED! The SLOT_ID is: ", unique_slot_id_); + } + + return 0; +} + +void RDMASendRecvTask::configurationTask(std::shared_ptr rdma_buffer, + uint32_t unique_slot_id, + OpCode rmda_opcode) +{ + rdma_buffer_ = rdma_buffer; + unique_slot_id_ = unique_slot_id; + rdma_operation_ = rmda_opcode; + makeAssignmentBatch(); + makeDataMR(); + makeMetaMR(); +} + +RDMASendRecvTaskPool::RDMASendRecvTaskPool(std::shared_ptr rdma_endpoint) +{ + rdma_endpoint_ = rdma_endpoint; + pool_size_ = 128; + + for (size_t i = 0; i < pool_size_; ++i) { + auto task = std::make_shared(rdma_endpoint_, i); + rdma_send_task_pool_.push_back(task); + // rdma_endpoint_->postSendTask(task); + rdma_send_task_in_use_.push_back(false); } + + for (size_t i = 0; i < pool_size_; ++i) { + rdma_recv_task_pool_.push_back(std::make_shared(rdma_endpoint_, i)); + rdma_recv_task_in_use_.push_back(false); + } +} + +RDMASendRecvTaskPool::~RDMASendRecvTaskPool() +{ + SLIME_LOG_INFO("Destroy the RDMASendRecvTaskPool"); } -int RDMATask::registerRemoteDataMemoryRegion() +std::shared_ptr RDMASendRecvTaskPool::fetchSendRecvTask(std::shared_ptr rdma_buffer, + uint32_t unique_slot_id, + OpCode rdma_operation) { - auto mr_is_exist = endpoint_->dataCtx()->get_remote_mr(getDataKey(0)); - if (mr_is_exist.addr == 0) { - std::vector& meta_buf = endpoint_->getMetaBuffer(); - for (size_t i = 0; i < buffer_->batchSize(); ++i) { - uint64_t addr = meta_buf[slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i]; - uint32_t length = meta_buf[slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i]; - uint32_t rkey = meta_buf[slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i]; - endpoint_->dataCtx()->register_remote_memory_region(getDataKey(i), addr, length, rkey); + if (rdma_operation == OpCode::SEND) { + + std::unique_lock lock(pool_mutex_); + + task_available_cv_.wait(lock, [this]() { + return std::find(this->rdma_send_task_in_use_.begin(), this->rdma_send_task_in_use_.end(), false) + != this->rdma_send_task_in_use_.end(); + }); + + for (size_t i = 0; i < rdma_send_task_in_use_.size(); ++i) { + if (!rdma_send_task_in_use_[i]) { + rdma_send_task_in_use_[i] = true; + auto task = rdma_send_task_pool_[i]; + task->configurationTask(rdma_buffer, unique_slot_id, rdma_operation); + rdma_endpoint_->postSendTask(task); + return task; + } + } + } + else if (rdma_operation == OpCode::RECV) { + + std::unique_lock lock(pool_mutex_); + + task_available_cv_.wait(lock, [this]() { + return std::find(this->rdma_recv_task_in_use_.begin(), this->rdma_recv_task_in_use_.end(), false) + != this->rdma_recv_task_in_use_.end(); + }); + + for (size_t i = 0; i < rdma_recv_task_in_use_.size(); ++i) { + if (!rdma_recv_task_in_use_[i]) { + rdma_recv_task_in_use_[i] = true; + auto task = rdma_recv_task_pool_[i]; + task->configurationTask(rdma_buffer, unique_slot_id, rdma_operation); + return task; + } } - return 0; } else { + SLIME_LOG_ERROR("Unsupported RDMA operation in fetchSendRecvTask!"); + return nullptr; + } + + return nullptr; +} + +int RDMASendRecvTaskPool::releaseSendRecvTask(std::shared_ptr rdma_task) +{ + + if (rdma_task->rdma_operation_ == OpCode::SEND) { + std::unique_lock lock(pool_mutex_); + rdma_send_task_in_use_[rdma_task->task_id_] = false; + rdma_endpoint_->postSendTask(rdma_task); + task_available_cv_.notify_one(); + return 0; + } + else if (rdma_task->rdma_operation_ == OpCode::RECV) { + std::unique_lock lock(pool_mutex_); + rdma_recv_task_in_use_[rdma_task->task_id_] = false; + task_available_cv_.notify_one(); return 0; } + else { + SLIME_LOG_ERROR("Unsupported RDMA operation in releaseSendRecvTask!"); + return -1; + } } RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, uint8_t ib_port, const std::string& link_type, size_t qp_num) @@ -147,32 +264,20 @@ RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, uint8_t ib_port, const s memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); meta_ctx_->register_memory_region( "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); -} - -RDMAEndpoint::RDMAEndpoint(const std::string& data_dev_name, - const std::string& meta_dev_name, - uint8_t ib_port, - const std::string& link_type, - size_t qp_num) -{ - SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); - data_ctx_ = std::make_shared(qp_num, 0); - meta_ctx_ = std::make_shared(1, 0); - data_ctx_->init(data_dev_name, ib_port, link_type); - meta_ctx_->init(meta_dev_name, ib_port, link_type); + std::cout << meta_buffer_[0].mr_addr[0] << std::endl; - data_ctx_qp_num_ = data_ctx_->qp_list_len_; - meta_ctx_qp_num_ = meta_ctx_->qp_list_len_; - SLIME_LOG_INFO("The QP number of data plane is: ", data_ctx_qp_num_); - SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); - SLIME_LOG_INFO("RDMA Endpoint Init Success and Launch the RDMA Endpoint Task Threads..."); + dum_meta_buffer_.reserve(16); + memset(dum_meta_buffer_.data(), 0, dum_meta_buffer_.size() * sizeof(uint32_t)); + meta_ctx_->register_memory_region("dum_meta_buffer_", + reinterpret_cast(dum_meta_buffer_.data()), + sizeof(uint32_t) * dum_meta_buffer_.size()); - const size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; - meta_buffer_.reserve(max_meta_buffer_size); - memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); - meta_ctx_->register_memory_region( - "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); + dum_data_buffer_.reserve(16); + memset(dum_data_buffer_.data(), 1048, dum_data_buffer_.size() * sizeof(uint32_t)); + data_ctx_->register_memory_region("dum_data_buffer_", + reinterpret_cast(dum_data_buffer_.data()), + sizeof(uint32_t) * dum_data_buffer_.size()); } RDMAEndpoint::~RDMAEndpoint() @@ -190,40 +295,44 @@ RDMAEndpoint::~RDMAEndpoint() void RDMAEndpoint::connect(const json& data_ctx_info, const json& meta_ctx_info) { + std::cout << "DDD" << std::endl; data_ctx_->connect(data_ctx_info); meta_ctx_->connect(meta_ctx_info); - + std::cout << "DDDDDD" << std::endl; data_ctx_->launch_future(); meta_ctx_->launch_future(); - + std::cout << "DDDDDDDDDDDD" << std::endl; RDMA_tasks_threads_running_ = true; - rdma_tasks_threads_ = std::thread([this] { this->waitandPopTask(std::chrono::milliseconds(100)); }); + rdma_tasks_threads_ = std::thread([this] { this->mainQueueThread(std::chrono::milliseconds(100)); }); + std::cout << "DDDDDDDDDDDDDDDDDD" << std::endl; + rdma_task_pool_ = std::make_shared(shared_from_this()); + std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; } -void RDMAEndpoint::addSendTask(std::shared_ptr buffer) +void RDMAEndpoint::addSendTask(std::shared_ptr rdma_buffer) { std::unique_lock lock(rdma_tasks_mutex_); - ++send_slot_id_; - auto task = std::make_shared(shared_from_this(), send_slot_id_, OpCode::SEND, buffer); - send_batch_slot_[send_slot_id_] = task; + ++unique_SEND_SLOT_ID_; + auto task = rdma_task_pool_->fetchSendRecvTask(rdma_buffer, unique_SEND_SLOT_ID_, OpCode::SEND); + send_task_[unique_SEND_SLOT_ID_] = task; rdma_tasks_queue_.push(task); rdma_tasks_cv_.notify_one(); } -void RDMAEndpoint::addRecvTask(std::shared_ptr buffer) +void RDMAEndpoint::addRecvTask(std::shared_ptr rdma_buffer) { std::unique_lock lock(rdma_tasks_mutex_); - ++recv_slot_id_; - auto task = std::make_shared(shared_from_this(), recv_slot_id_, OpCode::RECV, buffer); - recv_batch_slot_[recv_slot_id_] = task; + ++unique_RECV_SLOT_ID_; + auto task = rdma_task_pool_->fetchSendRecvTask(rdma_buffer, unique_RECV_SLOT_ID_, OpCode::RECV); + recv_task_[unique_RECV_SLOT_ID_] = task; rdma_tasks_queue_.push(task); rdma_tasks_cv_.notify_one(); } -void RDMAEndpoint::waitandPopTask(std::chrono::milliseconds timeout) +void RDMAEndpoint::mainQueueThread(std::chrono::milliseconds timeout) { while (RDMA_tasks_threads_running_) { - std::shared_ptr task; + std::shared_ptr task; { std::unique_lock lock(rdma_tasks_mutex_); @@ -241,7 +350,7 @@ void RDMAEndpoint::waitandPopTask(std::chrono::milliseconds timeout) } if (task) { - switch (task->opcode_) { + switch (task->rdma_operation_) { case OpCode::SEND: asyncSendData(task); break; @@ -256,47 +365,225 @@ void RDMAEndpoint::waitandPopTask(std::chrono::milliseconds timeout) } } -void RDMAEndpoint::asyncSendData(std::shared_ptr task) +void RDMAEndpoint::postSendTask(std::shared_ptr task) { - size_t batch_size = task->buffer_->batchSize(); - uint32_t slot_id = task->slot_id_; - auto data_callback = [this, task](int status, int _) mutable { - std::unique_lock lock(rdma_tasks_mutex_); - this->send_batch_slot_[task->slot_id_]->buffer_->send_done_callback(); + this->rdma_task_pool_->releaseSendRecvTask(task); + task->rdma_buffer_->send_done_callback(); }; auto meta_callback = [this, task, data_callback](int status, int slot_id) mutable { - std::unique_lock lock(this->rdma_tasks_mutex_); - task->registerRemoteDataMemoryRegion(); - AssignmentBatch data_assign_batch = task->getDataAssignmentBatch(); - auto data_atx = this->data_ctx_->submit( - OpCode::WRITE_WITH_IMM, data_assign_batch, data_callback, RDMAContext::UNDEFINED_QPI, slot_id); + task->makeRemoteDataMR(); + AssignmentBatch data_assign_batch = task->data_assignment_batch_; + auto data_atx = this->data_ctx_->submit(OpCode::WRITE_WITH_IMM, + data_assign_batch, + data_callback, + RDMAContext::UNDEFINED_QPI, + task->unique_slot_id_); }; { - AssignmentBatch meta_data = task->getMetaAssignmentBatch(); - meta_ctx_->submit(OpCode::RECV, meta_data, meta_callback); + AssignmentBatch meta_assignment_batch = task->dum_meta_assignment_batch_; + meta_ctx_->submit(OpCode::RECV, meta_assignment_batch, meta_callback); } } -void RDMAEndpoint::asyncRecvData(std::shared_ptr task) +void RDMAEndpoint::asyncSendData(std::shared_ptr task) +{ + std::cout << "The send task has been pre post" << std::endl; +} + +void RDMAEndpoint::asyncRecvData(std::shared_ptr task) { auto data_callback = [this, task](int status, int slot_id) mutable { - std::unique_lock lock(rdma_tasks_mutex_); - recv_batch_slot_[slot_id]->buffer_->recv_done_callback(); + this->rdma_task_pool_->releaseSendRecvTask(task); + task->rdma_buffer_->recv_done_callback(); }; - auto meta_callback = [this, task, data_callback](int status, int _) mutable { - std::unique_lock lock(this->rdma_tasks_mutex_); - AssignmentBatch assign_batch = task->getDataAssignmentBatch(); - auto data_atx = this->data_ctx_->submit(OpCode::RECV, assign_batch, data_callback, RDMAContext::UNDEFINED_QPI); + auto meta_callback = [this, task](int status, int slot_id) mutable { + std::cout << "The recv task has been pre post" << std::endl; }; { - AssignmentBatch meta_data = task->getMetaAssignmentBatch(); - meta_ctx_->submit(OpCode::WRITE_WITH_IMM, meta_data, meta_callback, RDMAContext::UNDEFINED_QPI, task->slot_id_); + auto data_atx = this->data_ctx_->submit(OpCode::RECV, + task->dum_data_assignment_batch_, + data_callback, + RDMAContext::UNDEFINED_QPI, + task->unique_slot_id_); + + AssignmentBatch meta_assignment_batch = task->meta_assignment_batch_; + meta_ctx_->submit(OpCode::WRITE_WITH_IMM, + meta_assignment_batch, + meta_callback, + RDMAContext::UNDEFINED_QPI, + task->unique_slot_id_); } } +// void RDMATask::fillBuffer() +// { +// if (opcode_ == OpCode::SEND) { +// std::vector& meta_buf = endpoint_->getMetaBuffer(); +// } +// else if (opcode_ == OpCode::RECV) { +// std::vector& meta_buf = endpoint_->getMetaBuffer(); +// for (size_t i = 0; i < buffer_->batchSize(); ++i) { +// auto mr = endpoint_->dataCtx()->get_mr(getDataKey(i)); +// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i] = +// reinterpret_cast(mr->addr); +// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; +// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i] = mr->length; +// } +// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_slot = slot_id_; +// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_qpidx = +// slot_id_ % endpoint_->dataCtxQPNum(); +// } +// else { +// SLIME_LOG_ERROR("Unsupported opcode in RDMATask::fillBuffer()"); +// } +// } + +// RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, uint8_t ib_port, const std::string& link_type, size_t qp_num) +// { +// SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); + +// data_ctx_ = std::make_shared(qp_num, 0); +// meta_ctx_ = std::make_shared(1, 0); + +// data_ctx_->init(dev_name, ib_port, link_type); +// meta_ctx_->init(dev_name, ib_port, link_type); + +// data_ctx_qp_num_ = data_ctx_->qp_list_len_; +// meta_ctx_qp_num_ = meta_ctx_->qp_list_len_; +// SLIME_LOG_INFO("The QP number of data plane is: ", data_ctx_qp_num_); +// SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); +// SLIME_LOG_INFO("RDMA Endpoint Init Success and Launch the RDMA Endpoint Task Threads..."); + +// const size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; +// meta_buffer_.reserve(max_meta_buffer_size); +// memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); +// meta_ctx_->register_memory_region( +// "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); +// } + +// RDMAEndpoint::RDMAEndpoint(const std::string& data_dev_name, +// const std::string& meta_dev_name, +// uint8_t ib_port, +// const std::string& link_type, +// size_t qp_num) +// { +// SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); +// data_ctx_ = std::make_shared(qp_num, 0); +// meta_ctx_ = std::make_shared(1, 0); + +// data_ctx_->init(data_dev_name, ib_port, link_type); +// meta_ctx_->init(meta_dev_name, ib_port, link_type); + +// data_ctx_qp_num_ = data_ctx_->qp_list_len_; +// meta_ctx_qp_num_ = meta_ctx_->qp_list_len_; +// SLIME_LOG_INFO("The QP number of data plane is: ", data_ctx_qp_num_); +// SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); +// SLIME_LOG_INFO("RDMA Endpoint Init Success and Launch the RDMA Endpoint Task Threads..."); + +// const size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; +// meta_buffer_.reserve(max_meta_buffer_size); +// memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); +// meta_ctx_->register_memory_region( +// "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); +// } + +// RDMAEndpoint::~RDMAEndpoint() +// { +// { +// std::unique_lock lock(rdma_tasks_mutex_); +// RDMA_tasks_threads_running_ = false; +// } + +// rdma_tasks_cv_.notify_all(); + +// if (rdma_tasks_threads_.joinable()) +// rdma_tasks_threads_.join(); +// } + +// void RDMAEndpoint::connect(const json& data_ctx_info, const json& meta_ctx_info) +// { +// data_ctx_->connect(data_ctx_info); +// meta_ctx_->connect(meta_ctx_info); + +// data_ctx_->launch_future(); +// meta_ctx_->launch_future(); + +// RDMA_tasks_threads_running_ = true; +// rdma_tasks_threads_ = std::thread([this] { this->waitandPopTask(std::chrono::milliseconds(100)); }); +// } + +// void RDMAEndpoint::addSendTask(std::shared_ptr buffer) +// { +// std::unique_lock lock(rdma_tasks_mutex_); +// ++send_slot_id_; +// auto task = std::make_shared(shared_from_this(), send_slot_id_, OpCode::SEND, buffer); +// send_batch_slot_[send_slot_id_] = task; +// rdma_tasks_queue_.push(task); +// rdma_tasks_cv_.notify_one(); +// } + +// void RDMAEndpoint::addRecvTask(std::shared_ptr buffer) +// { +// std::unique_lock lock(rdma_tasks_mutex_); +// ++recv_slot_id_; +// auto task = std::make_shared(shared_from_this(), recv_slot_id_, OpCode::RECV, buffer); +// recv_batch_slot_[recv_slot_id_] = task; +// rdma_tasks_queue_.push(task); +// rdma_tasks_cv_.notify_one(); +// } + +// void RDMAEndpoint::asyncSendData(std::shared_ptr task) +// { +// // size_t batch_size = task->buffer_->batchSize(); +// // uint32_t slot_id = task->slot_id_; + +// auto data_callback = [this, task](int status, int _) mutable { +// std::unique_lock lock(rdma_tasks_mutex_); + +// task->buffer_->send_done_callback(); +// }; + +// auto meta_callback = [this, task, data_callback](int status, int slot_id) mutable { +// std::unique_lock lock(this->rdma_tasks_mutex_); +// // Assert +// task->registerRemoteDataMemoryRegion(); +// AssignmentBatch data_assign_batch = task->getDataAssignmentBatch(); +// auto data_atx = this->data_ctx_->submit( +// OpCode::WRITE_WITH_IMM, data_assign_batch, data_callback, RDMAContext::UNDEFINED_QPI, slot_id); +// }; + +// { +// AssignmentBatch meta_data = task->getMetaAssignmentBatch(); +// meta_ctx_->submit(OpCode::RECV, meta_data, meta_callback); +// } +// } + +// void RDMAEndpoint::asyncRecvData(std::shared_ptr task) +// { +// auto data_callback = [this, task](int status, int slot_id) mutable { +// std::unique_lock lock(rdma_tasks_mutex_); +// recv_batch_slot_[slot_id]->buffer_->recv_done_callback(); +// }; + +// auto meta_callback = [this, task, data_callback](int status, int _) mutable { +// std::unique_lock lock(this->rdma_tasks_mutex_); +// AssignmentBatch assign_batch = task->getDataAssignmentBatch(); + +// }; + +// { +// // post recv +// auto data_atx = this->data_ctx_->submit(OpCode::RECV, assign_batch, data_callback, +// RDMAContext::UNDEFINED_QPI); +// AssignmentBatch meta_data = task->getMetaAssignmentBatch(); +// meta_ctx_->submit(OpCode::WRITE_WITH_IMM, meta_data, meta_callback, RDMAContext::UNDEFINED_QPI, +// task->slot_id_); +// } +// } + } // namespace slime diff --git a/csrc/engine/rdma/rdma_endpoint.h b/csrc/engine/rdma/rdma_endpoint.h index e772f32..5e2a7ea 100644 --- a/csrc/engine/rdma/rdma_endpoint.h +++ b/csrc/engine/rdma/rdma_endpoint.h @@ -5,7 +5,6 @@ #include #include #include -#include #include #include #include @@ -34,39 +33,66 @@ typedef struct MetaData { } __attribute__((packed)) meta_data_t; -typedef struct RDMATask { - explicit RDMATask(std::shared_ptr endpoint, - uint32_t task_id, - OpCode opcode, - std::shared_ptr buffer); - ~RDMATask(); +struct RDMASendRecvTask { - std::string getMetaKey(); - std::string getDataKey(int32_t idx); + RDMASendRecvTask(std::shared_ptr rdma_endpoint, uint32_t task_id); + RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rmda_opcode, uint32_t task_id); - AssignmentBatch getMetaAssignmentBatch(); - AssignmentBatch getDataAssignmentBatch(); + ~RDMASendRecvTask(); - int registerMetaMemoryRegion(); - int registerDataMemoryRegion(); - int registerRemoteDataMemoryRegion(); + int makeAssignmentBatch(); + int makeMetaMR(); + int makeDataMR(); + int makeRemoteDataMR(); + int makeDummyAssignmentBatch(); - void fillBuffer(); + void configurationTask(std::shared_ptr rdma_buffer, uint32_t unique_slot_id, OpCode rmda_opcode); - int targetQPI(); + uint32_t task_id_; + uint32_t unique_slot_id_; + OpCode rdma_operation_; - uint32_t slot_id_; - OpCode opcode_; - std::shared_ptr buffer_; + std::shared_ptr rdma_buffer_; + std::shared_ptr rdma_endpoint_; + AssignmentBatch meta_assignment_batch_; + AssignmentBatch dum_meta_assignment_batch_; + AssignmentBatch data_assignment_batch_; + AssignmentBatch dum_data_assignment_batch_; +}; + +class RDMASendRecvTaskPool { + +public: + RDMASendRecvTaskPool(std::shared_ptr rdma_endpoint); + ~RDMASendRecvTaskPool(); - std::shared_ptr endpoint_; -} rdma_task_t; + std::shared_ptr + fetchSendRecvTask(std::shared_ptr rdma_buffer, uint32_t unique_slot_id, OpCode rdma_operation); + + int releaseSendRecvTask(std::shared_ptr rdma_task); + +private: + std::shared_ptr rdma_endpoint_; + std::vector> rdma_send_task_pool_; + std::vector> rdma_recv_task_pool_; + std::vector rdma_send_task_in_use_; + std::vector rdma_recv_task_in_use_; + + mutable std::mutex pool_mutex_; + std::condition_variable task_available_cv_; + + size_t pool_size_; +}; class RDMAEndpoint: public std::enable_shared_from_this { + friend class RDMABuffer; public: - explicit RDMAEndpoint(const std::string& dev_name, uint8_t ib_port, const std::string& link_type, size_t qp_num); + explicit RDMAEndpoint(const std::string& dev_name, + uint8_t ib_port, + const std::string& link_type, + size_t qp_num = 1); explicit RDMAEndpoint(const std::string& data_dev_name, const std::string& meta_dev_name, @@ -74,21 +100,23 @@ class RDMAEndpoint: public std::enable_shared_from_this { const std::string& link_type, size_t qp_num); + // TODO: 设计聚合多网卡传输的Send Recv + ~RDMAEndpoint(); void connect(const json& data_ctx_info, const json& meta_ctx_info); - std::shared_ptr createRDMABuffer(storage_view_batch_t batch); + void addRecvTask(std::shared_ptr rdma_buffer); + void addSendTask(std::shared_ptr rdma_buffer); - void addRecvTask(std::shared_ptr buffer); - void addSendTask(std::shared_ptr buffer); + void postSendTask(std::shared_ptr task); - json getDataContextInfo() const + json dataCtxInfo() const { return data_ctx_->endpoint_info(); } - json getMetaContextInfo() const + json metaCtxInfo() const { return meta_ctx_->endpoint_info(); } @@ -102,6 +130,12 @@ class RDMAEndpoint: public std::enable_shared_from_this { { return meta_ctx_; } + + std::vector& metaBuffer() + { + return meta_buffer_; + } + int metaCtxQPNum() { return meta_ctx_qp_num_; @@ -111,41 +145,37 @@ class RDMAEndpoint: public std::enable_shared_from_this { return data_ctx_qp_num_; } - std::vector& getMetaBuffer() - { - return meta_buffer_; - } - private: - void waitandPopTask(std::chrono::milliseconds timeout); + void mainQueueThread(std::chrono::milliseconds timeout); + + void asyncRecvData(std::shared_ptr task); + void asyncSendData(std::shared_ptr task); - void asyncRecvData(std::shared_ptr task); - void asyncSendData(std::shared_ptr task); + std::shared_ptr rdma_task_pool_; size_t data_ctx_qp_num_; size_t meta_ctx_qp_num_; - std::atomic send_slot_id_{RDMAContext::UNDEFINED_IMM_DATA}; - std::atomic recv_slot_id_{RDMAContext::UNDEFINED_IMM_DATA}; + std::atomic unique_SEND_SLOT_ID_{RDMAContext::UNDEFINED_IMM_DATA}; + std::atomic unique_RECV_SLOT_ID_{RDMAContext::UNDEFINED_IMM_DATA}; + std::vector dum_meta_buffer_; + std::vector dum_data_buffer_; std::vector meta_buffer_; - uint32_t batch_size_; - - std::map> send_batch_slot_; - std::map> recv_batch_slot_; + std::unordered_map> send_task_; + std::unordered_map> recv_task_; std::shared_ptr data_ctx_; std::shared_ptr meta_ctx_; - std::queue> rdma_tasks_queue_; - std::thread rdma_tasks_threads_; + std::queue> rdma_tasks_queue_; + std::thread rdma_tasks_threads_; std::condition_variable rdma_tasks_cv_; std::mutex rdma_tasks_mutex_; - std::map> imm_data_callback_; - bool RDMA_tasks_threads_running_; + bool RDMA_tasks_threads_running_; }; } // namespace slime From e29eb5ce87b04f3b377ca97a19afbab116ae9487 Mon Sep 17 00:00:00 2001 From: HaoLiuuu Date: Sun, 19 Oct 2025 21:22:24 +0800 Subject: [PATCH 3/9] the queue template and the basic component of task in RDMAEndpoint --- .clangd/index/affinity.h.EBE212AD8338670F.idx | Bin 0 -> 764 bytes .../index/assignment.cpp.4ED8BC4CD3E27642.idx | Bin 0 -> 794 bytes .../index/assignment.h.89BA5D141D7922F0.idx | Bin 0 -> 1442 bytes .clangd/index/env.h.0FEC928CA83B4A12.idx | Bin 0 -> 716 bytes .../index/ibv_helper.cpp.B9C37D37FE9C9E0F.idx | Bin 0 -> 1394 bytes .../index/ibv_helper.h.619E178CE5CD4A5A.idx | Bin 0 -> 1164 bytes .clangd/index/json.hpp.694255043525F602.idx | Bin 0 -> 204696 bytes .clangd/index/logging.h.639CE923F9EFB290.idx | Bin 0 -> 886 bytes .../memory_pool.cpp.F09354329F2DF7D7.idx | Bin 0 -> 2320 bytes .../index/memory_pool.h.BE12C74670B7FCD5.idx | Bin 0 -> 2426 bytes .../rdma_assignment.cpp.EBA22C6C3C860620.idx | Bin 0 -> 1644 bytes .../rdma_assignment.h.65691B28352F6406.idx | Bin 0 -> 4080 bytes .../rdma_buffer.cpp.3EBA5C344A7697A3.idx | Bin 0 -> 1326 bytes .../index/rdma_buffer.h.C3DF879253C4FDCB.idx | Bin 0 -> 2490 bytes .../index/rdma_common.h.605E04D5C1594016.idx | Bin 0 -> 616 bytes .../index/rdma_config.h.96DB954603481CC6.idx | Bin 0 -> 1690 bytes .../rdma_context.cpp.A7591533B08A3B05.idx | Bin 0 -> 11958 bytes .../index/rdma_context.h.AB3EB1CCA367DFDD.idx | Bin 0 -> 6456 bytes .../rdma_endpoint.cpp.91B25917C6102BE7.idx | Bin 0 -> 6954 bytes .../rdma_endpoint.h.38575B1A8A5DD704.idx | Bin 0 -> 6796 bytes .clangd/index/rdma_env.h.41EE5E62C891E141.idx | Bin 0 -> 1204 bytes .../rdma_scheduler.cpp.8797BF9AAD52AD88.idx | Bin 0 -> 1970 bytes .../rdma_scheduler.h.3C45BC5D63D0F8B8.idx | Bin 0 -> 2454 bytes .../index/recv_test.cpp.FE47085B653922D7.idx | Bin 0 -> 2438 bytes .../index/send_test.cpp.40B395064D1F6AC5.idx | Bin 0 -> 3134 bytes .../index/sendrecv.cpp.2A1EA1731CF16F30.idx | Bin 0 -> 552 bytes .clangd/index/utils.cpp.22E6B8ED360F65D7.idx | Bin 0 -> 1252 bytes .clangd/index/utils.h.4F2362F6A4B3ABA7.idx | Bin 0 -> 534 bytes compile_commands.json | 1 + csrc/engine/rdma/rdma_buffer.h | 4 +- csrc/engine/rdma/rdma_endpoint.cpp | 2 +- csrc/engine/rdma/rdma_endpoint.h | 219 ++++++- tests/cpp/CMakeLists.txt | 30 + tests/cpp/recv_test.cpp | 96 +++ tests/cpp/send_test.cpp | 86 +++ tests/cpp/sendrecv.cpp | 594 +++++++++--------- 36 files changed, 716 insertions(+), 316 deletions(-) create mode 100644 .clangd/index/affinity.h.EBE212AD8338670F.idx create mode 100644 .clangd/index/assignment.cpp.4ED8BC4CD3E27642.idx create mode 100644 .clangd/index/assignment.h.89BA5D141D7922F0.idx create mode 100644 .clangd/index/env.h.0FEC928CA83B4A12.idx create mode 100644 .clangd/index/ibv_helper.cpp.B9C37D37FE9C9E0F.idx create mode 100644 .clangd/index/ibv_helper.h.619E178CE5CD4A5A.idx create mode 100644 .clangd/index/json.hpp.694255043525F602.idx create mode 100644 .clangd/index/logging.h.639CE923F9EFB290.idx create mode 100644 .clangd/index/memory_pool.cpp.F09354329F2DF7D7.idx create mode 100644 .clangd/index/memory_pool.h.BE12C74670B7FCD5.idx create mode 100644 .clangd/index/rdma_assignment.cpp.EBA22C6C3C860620.idx create mode 100644 .clangd/index/rdma_assignment.h.65691B28352F6406.idx create mode 100644 .clangd/index/rdma_buffer.cpp.3EBA5C344A7697A3.idx create mode 100644 .clangd/index/rdma_buffer.h.C3DF879253C4FDCB.idx create mode 100644 .clangd/index/rdma_common.h.605E04D5C1594016.idx create mode 100644 .clangd/index/rdma_config.h.96DB954603481CC6.idx create mode 100644 .clangd/index/rdma_context.cpp.A7591533B08A3B05.idx create mode 100644 .clangd/index/rdma_context.h.AB3EB1CCA367DFDD.idx create mode 100644 .clangd/index/rdma_endpoint.cpp.91B25917C6102BE7.idx create mode 100644 .clangd/index/rdma_endpoint.h.38575B1A8A5DD704.idx create mode 100644 .clangd/index/rdma_env.h.41EE5E62C891E141.idx create mode 100644 .clangd/index/rdma_scheduler.cpp.8797BF9AAD52AD88.idx create mode 100644 .clangd/index/rdma_scheduler.h.3C45BC5D63D0F8B8.idx create mode 100644 .clangd/index/recv_test.cpp.FE47085B653922D7.idx create mode 100644 .clangd/index/send_test.cpp.40B395064D1F6AC5.idx create mode 100644 .clangd/index/sendrecv.cpp.2A1EA1731CF16F30.idx create mode 100644 .clangd/index/utils.cpp.22E6B8ED360F65D7.idx create mode 100644 .clangd/index/utils.h.4F2362F6A4B3ABA7.idx create mode 120000 compile_commands.json create mode 100644 tests/cpp/recv_test.cpp create mode 100644 tests/cpp/send_test.cpp diff --git a/.clangd/index/affinity.h.EBE212AD8338670F.idx b/.clangd/index/affinity.h.EBE212AD8338670F.idx new file mode 100644 index 0000000000000000000000000000000000000000..75a6234fbe1a1dc6bbf0016ac5012f5efaece2e1 GIT binary patch literal 764 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 literal 0 HcmV?d00001 diff --git a/.clangd/index/env.h.0FEC928CA83B4A12.idx b/.clangd/index/env.h.0FEC928CA83B4A12.idx new file mode 100644 index 0000000000000000000000000000000000000000..8ee98cdd0779f3dee10e3f6fbe8e8763b9cf6bba GIT binary patch literal 716 zcmWIYbaOkx#K7R3;#rZKT9U}Zz`(!*#Kk2=nMZ+i2O|SR#hl*rw%msd1lS(@)|3

J-;V!_wae@g3P8KFc1{MZJcE+9d#s<^(zrMxGzzkHODX6IcCV(n{+SyovDn86){^nNT zzk-R0A1DL@>|9)2{16pDUQudVF&{A0V0JMvf$d;n*HqTjV*VZcQtR=-WsEHB^8E5b zU0x-2--7pj0&zv;rF)i4yPIG0@-~nwqbQ>u%;~x}H&vjInT1{2O4ADZD8(xyJoTa00f&7{UIDes^2$n16>k@FtP`mNa^)1|RPQ7| zFt(esKAVw=osWZ0Ab#b_ZL5PU(}A**B9apGJvQcV3M|+FfPG8BAgeW z08LO3R*)}B%}E4>RdG>raWII%zAk-3G0%$)qHG+TT--do44fcQ2;hJ*AZZcG<3@<` JBA8qVCIARI${7Fv literal 0 HcmV?d00001 diff --git a/.clangd/index/ibv_helper.cpp.B9C37D37FE9C9E0F.idx b/.clangd/index/ibv_helper.cpp.B9C37D37FE9C9E0F.idx new file mode 100644 index 0000000000000000000000000000000000000000..ae87be299414a0b4dad41367ab593d695ee1f5bf GIT binary patch literal 1394 zcmb7@|5s9X7{@R8F;Ffba=9pep>c9VVXH+Fk|V!imS$)k8K4anuuf`|jP>n~@hLtzqTiIc`S#wszb^Lc+_5~KL6=4t-n6%w z)=vy|TzaDXAY<(1wJ7<~Yg2*rfJLL>>|ENNvn$V+Y>6^mDEQ|&`cV6I_4$Sse{f8s zv&ZB0wdEUpS9#Y)efQzs)Mz>PI%ecDDtjC57JrnoNnYylYVP$x)s*}vzR#Ed@EyA_TO90P~wf-iIQKr>3d#E+VhK7xgr#pi^WtOTdF_&pGuSV8LLY4Mg zXE${Dw_Ny?f4V-QUa?MMCJf|u>xPaiBJ$644Zi$+bXvOqaOuQ+?%SCQ`^rzcO`Dbd ziRWr{nrwR7+AUGy>$i1VD@7}A*dHmeG?K=j=5}o8zt=K(#L$ z-r7*M8%10Y1lI#S&h`bqIN|{8iHIAEz$VZP2Yz~RV_*%6aG-(+h-|h9h}cj82>&VQ zqrA$VJDPPU;tCZ)AjH`cAh9tP* z?!^3@xpg8tfD2W^RDkQ{dNsh4K$2_c{_fMC1ev=F=qSli5lLwkj8Li(0XT{>nM!QiS$T@ z5V4mSz6y{8$TaI)=|9M3$00<4DzI1|&LhR;t1<8cWdfNWgm{PfLx@1>$319RS?D#2 zL8XK&p$t==^i&2D4#AQMm=3%bNxB90;6yNaim!vv)kb+U%Td#QAj~0zN!*Uf^ zg{JFvYT++~7}FDsY+JGrYzacZMbF9wjn03n2y7%FIdLZqHl0vs64`-5r!pNGDD7V+ Cd)ABq literal 0 HcmV?d00001 diff --git a/.clangd/index/ibv_helper.h.619E178CE5CD4A5A.idx b/.clangd/index/ibv_helper.h.619E178CE5CD4A5A.idx new file mode 100644 index 0000000000000000000000000000000000000000..cf4545d98c6a3590fb407cbb389b0375a87ed344 GIT binary patch literal 1164 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 literal 0 HcmV?d00001 diff --git a/.clangd/index/logging.h.639CE923F9EFB290.idx b/.clangd/index/logging.h.639CE923F9EFB290.idx new file mode 100644 index 0000000000000000000000000000000000000000..e11db399eea1d3e8d0953d015450d71c6524eead GIT binary patch literal 886 zcmWIYbaTsNW?*nm@vO*AElFfyU|`?@;^LB`%*Q}_7b62h#hl&=2fbJfdD?z!aPK~} zj{S7SW_Hb2t^)d>8B`~qJjY_ByJc>oV@>(yT}^T+Edm~&o<8?KQ}_Lvh0n$-ynPeQ zuZv9I;J_!SR%Jh9@3HvD&5qkYuitkiu9&03%CtAXmc7+*$}CI1@3)V?HNX68v)c;E z9nU9}fBlyhB=+Y!C6uS(?L7N^2r;@`e17zgl9UvE_6G@o~?Y>SxC;f=G3_8qre zEH`)enp%dfE|ZNIiYs%IT!4O^kU!VxbWBh#4+Aeh1G_Z0v;>%7;ACN9V&G$CWM@2j zcan?Q{ZFPm3_L&;e8POnU;?OunUO(&1*oDZHLdt4Fw9^o8JQr;@@6le5%;V-@L_ z2XbRMV#Oxxbm|YWpR^OmP3A}zhdCO^1-Uqw({*odsz4tT3p*bVpAZYXsEDYP$gSW> z3wtGWfNGRDlq3YiN*J`~<}?7g?i}vol1I|6&-ti66UcSqa1xIZ4fK0;edabG*M-AH ze7?uV{7r!c8yH#Gg$0Dg5~j=yQ=KSj43tgaNDx!}@Axx+%PEji${flPNu~}NKR>&)RSzG8J4 zZ%U~4R3O)!!(2q;8~5&=%cuPSa@{!G#PiKeS~3o=js|koIMgIQ#Wb$(aog_)x*4M0Il&M|72!ak2>(W{)RcGI^z#m<+de_Rg_Q}3y zpM8G&oPDi^q$DbvZSC!=25k#^*LDV+4Gz^uVSJ8bBu1VXc7FTbc(1|B zW;p*i1ARg|g_@MPi$(Xo*)ALj8;E@Evx_ZDd2JQ#^_w-18!DS7ddhUA#pR9nI*y$# z`c6Hi?zodWYW-Q=J^WHT>-eB}vDd?C>-fnn|MvT!tG0NwVxog#?3E{te3AGUyZLXoZv6mRo5^W`_6dY<3r(&k-_mPTYV?2eb>8-h#PgC z_{=HAKacpA|7Fvfij0%}X|7e((?-4zXI1Ob%W=U^TAeeFUrBu!Tz7Gz@$k{oqRgGy zoc_|p$i~5`{J^A|9Jyyr^S6Y~^FT@IBIQs>w?U~AKAr%N9duKMBX_uDJ) zz5jejk!>EgJlL1LWbLuPCa&w9?&>i-A7Tv$J*{3_zI+(ew%a$ZjQxIvm}Ys22`XL? zAI4R&RY3%sDF-}p`@XvVN>}T8nk521DIgbc?XqE@~GliJ}Ts=oGn)SCnZE-L!xGAAoZs6Ao^%3@6jwzm>(brP;eD&h2QM_s-hLuo{j|Ziu}~p zv}kbHIiW2Si1rN7xXhC-ewWDJX@Fs7xXg>@1YUt{;=$A{9lA zFv}!2h3+_|+ny2Ea0?A#*YIKeE|<+E5ycboJP_={7pf!U$%LQ`3IaP-km{~@ ztL^O2FC&MblrM_HMEGrKltis{S9=W(}Y^ni< zBqo=K$96jnti=M5B197s2^%zOt?k=eS=m>^Xpoddq${EVPykSlX?83cT$b{^DZCW=vF(JC6o$7V;o1Tr-?ud zQ%VjcfKqa#Kov&?jN(KAbsQbgz%c-A9Gl?m8mE$p&yVRD@kx^lh)r(x?^W%_>2?QQW)-DxA_k7?{0=fA5aSM%XB$br%Gf;8Or(Kdey-kH0_-H&<} zZh1Cd0PmRn%z-l<3l0F&u4Wn(44LlK_ozwi!`k*GL`adO1jGH3WChPO8F+{3FnxEM zHnrSY^!MM<=hE}~q5~Qv2B1x1lNPkco#cn}PJj+L1)B?-ZtUqPTzMMOA5gC%vcUVxzgybjdAz^#i=->t9P>8)zA7`P4oehJq}? zs-M?=m(0qpy#K5B$(s&kAA2sh!UZw8nBd5ngeHh)b}@r(7Mh_!SzIh|Su7$8Tp#R% z1A#IO;^RmR;^26!16nZ#=HT3!o(|<;8u~gB``X=s-R-auBi9HSVlJjP+^BEpEpWO6 z0;-Xk3u&WhU;;`2E)y*C-8dGuH*g8%$KX>LOvm! abrJ>Tg=G{Bok-G-W$(b5=YpIa+5ZJTNoU^x literal 0 HcmV?d00001 diff --git a/.clangd/index/memory_pool.h.BE12C74670B7FCD5.idx b/.clangd/index/memory_pool.h.BE12C74670B7FCD5.idx new file mode 100644 index 0000000000000000000000000000000000000000..06501328d1eadb0a691e413a4dacaebc844ce413 GIT binary patch literal 2426 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 XefB!@ODXs%AxR!zfD_;#sZjhk!V_Q* literal 0 HcmV?d00001 diff --git a/.clangd/index/rdma_buffer.h.C3DF879253C4FDCB.idx b/.clangd/index/rdma_buffer.h.C3DF879253C4FDCB.idx new file mode 100644 index 0000000000000000000000000000000000000000..5ee3891e3665d3e5f02ac9d9ca7a321826368ee6 GIT binary patch literal 2490 zcmYLL4NwzT9^X7R*=$HQn=dp8Bq1b(U=$D<9khU!gC>L+Ob#Ty>!6579g!*4~M>%UP78OOjde&B3zvuw!jkR>3w*Ou3ZFVNVc`v{B zfA9bPAHUtjo}!{InjoHA?pa$`vt}tt5QH3ljcXcS`i+PnK93`awe5R@rS+#R@;ett zayxEZz20d$S~3yGiX0z)RxC*!ZThm_+!i|D;i-ModQbm#_L1I^wQF0hMNe+~B-gW2 zt2$K`CmxL4mh}$z{XM03ea@lp+F$<~nGej_Gk)ub<(WO{k~6)E`~8>cdcIZllqdP z^^Cmp+Sc&oI`OIJh7LRvI&(B|=ln?1e}1VOKNp@AW-KeV99|-6yOBAsq@&u|xhd!w zZprS+Z_+HDMKrFfTeb`$Np8L=|9SRFf)_~4k*Vh^N1ioNje3brg-ev|Ffe$Og1|9_7 ze`NIi@GAEL7FqBO7SIcf0$;Iab@|~_r*K?!O_L-D6@<7QyxSd5w z*uQ!7JcYowi2E)s>YkXvAU)VuBv#mX5ct=Q=kI<+(zZGQiGVkeCWZ%r-?BFvJ@kRP zoki2|{`2YivHf=q?LToXJ)mch73>4LfRzWqzHOsEb>`8ArwkGU@3DGj@F4K*Wu1vX zZJsuVMOwUnAzc`&kj~VzTlQZ(&@<(qp42lG2ZH^F{>jBf3znQ_kpbH;q!-5Wb1bhX z>B5P&ViqAdKTDNme$Cg^^=74?%tsfcILx4ju&l zq4z-h$-M8rWsnZ|5^IT*2Z4WbW8;(h=xs5J(y)JdbYAR!lTWLe@B6m~ry(cUXOJ0U z9t8V_niY-HNP<8wr{5HBCwGWQD&3Oq#EmCHe8b@^i9{5a9oat~o}d5L)4Q-ZO_8SC z*?N9S-_^5rL{jB+xsjwQ=n5lF9PGel4d*G2qzqDnilkgpmkRN@ioE|qX{GOlf5C1} zxzm6%24``Z!YvL{uC zafb5U#q{ok;_r8X4|c6xM^dTUR6vK;p~Er&-f|j%s{*E6HQ*uuAGqWn-jn#)`>L## zB&mQkkU~;r)-u4LEodjHkS%1-dG=G;m+Qh>2}xOumSmDjHYNjR$+B3IDq%|!NXp0h z0R5z2mZSgZ*gjj24X)2c1`7%t&8Z32AGohW>QEzIeQ?pi8m{7Sjlq;FFf2JR<@9FZ zC*9Tt{k9|A{%%9==aT2gGJK=)5CpweZ-V$}O@JQSqxoXxVBgNjKnkqXnRL(eH@2Ls z`(-_!mt?2&i{*XYLoXIYfpxl_g;=;CCY)ehy8G|??))qFU|VOVGYhcP=?AQFt_bU^{B0NW5LQ-y-8zNjx7Ei}&3m$S(J4|=?{b~7EVF;;9?b2Ya zg^+R@i(4NYOO>W75#RWahF?L=)dSgFHX#&^otKYCwpVR{H8!J-i%CkhOqusw562on$e(;(6V@qT)`+=m0C(D@d|>F#fF?a|9{{^ Q3_tOBKoFYPP#PQl3P<8qFiSPCM=Bsyfh__BKm@GLXPj zYMjGzJ_asM24*g9E=e%KzyUFX;Y@M$soein>-ZR$fbyJtoMK=CF8?Lw1%v*>-b-u@ zj6fSW_&KD&1dLC`wH$o(yy@%q5IWU>CqF2692R z!hFKS1oi<7GoL1(`IQSz3hyIy_!(K4IaxS)Vc`MP0tyTh+ap{zDx*Z0SeUt3xVTuD zxmmcmV6FkG0XYTcd>|L(_Fd=xUA}eUeKgQOJ|#X4hN9G*L{Lx`7bO?#gBWZe0s>fJ b3kts~U8FVgt z(Mi;~4b8?jh#A{70gW5JjEqcin|srM+aEI+<6?}mOhsKBb1{?cdxuxNq@VtJzvuZq zzvugWueYSQuyA)G!{o<`o9k=VR}qF`toSsoZ>;+T-!jKA%^ka12O7SWtv_u1prX2~ zcle3Xp|T9we<^V2WbNjKFa0~cwQsa!dvWU2vGeY+_BXh-iGWlJ4BUtKu6p#8VQpIW94yf<@m)!v8+zBt1)ZLD9ppT*X? zPj}8Koip3c32ufJ1VPln@R|g;%)y+k-LA-LJC}mITX2gy;(1%awWA%%%h^esU1V5C zqQjztF^|t@S=Mu)?96k~t)WEDi4iHuDXBUh8c{(QN8084FW5ON)^G|=QAd1@Ri!`P zUzgx*aBv>vL*|e}M?Al!_rxPNMcVJ?oXDq>beoQNep}7i{8jCDPue*f@-D&U))CKt zvPOQlZTr=pB*ujfBV5F$OSs^8AJC?3@GhJ%UHn5zpW49?V|we%-b4`Kct;#%hS?8*6Ht$`f!L4^E44 z`Y93C%X^au8{h*VeTpv0yBFPjWzU7V9AQNwS_v!7lnM-eSWef)^v>JO2fns8nh7h1 z9IG1^LvhVtL>CVga0HkFKa`I+O`XGsucElm>C3>}Dz)CQ zT_UI34K2;^&W0MijFx=74^*?%Y;c*n-0%oA)C>~qopCPXdO`6dj926^#w$M1ulPYl zQNVx_0E0>p%v3VN!C9;L+t%7j^xz^c6qh1W)UY(9M^k2jR0=Mas%}jlI&?HD{)3uMi!%i`{Zhy<8`RL8hJ8)*=AZ{YQ zE{eG{M6FH8CXqYOk>TOD(ye7sj<3gixy~4+=B}*2jjxKz_=zE0mE1( oLVhANO@!un$gv;olfT@SIM-w05{M}=X%cU?FeIL5k|x6c0q722Jpcdz literal 0 HcmV?d00001 diff --git a/.clangd/index/rdma_context.cpp.A7591533B08A3B05.idx b/.clangd/index/rdma_context.cpp.A7591533B08A3B05.idx new file mode 100644 index 0000000000000000000000000000000000000000..475def68bd595c72adf2e7aab35ec8521c2021dd GIT binary patch literal 11958 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 literal 0 HcmV?d00001 diff --git a/.clangd/index/rdma_context.h.AB3EB1CCA367DFDD.idx b/.clangd/index/rdma_context.h.AB3EB1CCA367DFDD.idx new file mode 100644 index 0000000000000000000000000000000000000000..957ba4efcd1113c7b2c85f140b4be6ad50875663 GIT binary patch literal 6456 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 literal 0 HcmV?d00001 diff --git a/.clangd/index/rdma_endpoint.cpp.91B25917C6102BE7.idx b/.clangd/index/rdma_endpoint.cpp.91B25917C6102BE7.idx new file mode 100644 index 0000000000000000000000000000000000000000..bf76a56aeb894432125f04a0b1feabf798f59c9d GIT binary patch literal 6954 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 literal 0 HcmV?d00001 diff --git a/.clangd/index/rdma_endpoint.h.38575B1A8A5DD704.idx b/.clangd/index/rdma_endpoint.h.38575B1A8A5DD704.idx new file mode 100644 index 0000000000000000000000000000000000000000..a0db104dc85e61f8b2c5814936359021d4563163 GIT binary patch literal 6796 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 literal 0 HcmV?d00001 diff --git a/.clangd/index/rdma_scheduler.cpp.8797BF9AAD52AD88.idx b/.clangd/index/rdma_scheduler.cpp.8797BF9AAD52AD88.idx new file mode 100644 index 0000000000000000000000000000000000000000..2a2defb1199b0dfc6f168062d7ffcd66c3433e62 GIT binary patch literal 1970 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 M#ZdlYsCY5_KmW{(R{#J2 literal 0 HcmV?d00001 diff --git a/.clangd/index/recv_test.cpp.FE47085B653922D7.idx b/.clangd/index/recv_test.cpp.FE47085B653922D7.idx new file mode 100644 index 0000000000000000000000000000000000000000..d5e58da0920f28a7573de3bd647f25b589e4024b GIT binary patch literal 2438 zcmZuy3p7-D82`KO;EuV&jK?s-i18TAWQ>-m$cfTpYs$$x5z-pPMA=e03GHFESv^Fy zHpwDZLUwyfNl8)>%Ics^5y`Q1%-R1P89CkmIrlr?`QG2}`~LqcUg_bn9|ORB>&iWn zuw7eF000es(p|BUc}f77LBXEfJaKn(2S>Xv|3%i>P2&|0@1*Xp;x|3jAM>v$ynskv zShl(S&@Y?Job+~|vdb!Py}FaGIr24gYAe|pR9O8Edqt53gdbu|TDe+=u>5fzUip?SB+2ux!ftQ=HqYwLg zPOM?qcV|WB^6Qp&m6{3O4s`z5xqNi%gu_djPu}iLF@LYEv-HakRbBJ@PJ_vjki~)V zf8O<{WF+DlZ9H~aTvubZxcW(J?cxce=7^6$DmCLlL)RSgEhNU7FKe7K$|O4?_^qKy zsWPRr>O~a2ps+%dmktU1)$3%c-rKHw7IhcP1e2zPxlW~9&WHcevv#2F9;@vi`|>9> zT`tN_0_Q_Z-JTUN^lzy1VzYIsJ|{#D+Z`zGc6&SYx%Hg6x4FZupuFMN;!nJkp!&yB z^B*r&UXzyGt1|kG$3F932BZm+5I}+B8b9uzcy%6v0~!Z#X@G{y2|$T)!ecyn>t1xg z-W(k0K)oi=#N|Yxx7Ln%6S8)Q!bUWv9j1*GdfR_}_D`znmm!GBj2;I#uF%_!9gm(8 zu4=`>0@C+w1T7e zwm=Q3-Z`S7)~X@+6b|f3`*&%#Yi&vPSI%yBjrrmzvsnf@4nK~v;kowz6T zaN}MZHbUo28*u4>KHI;q{+P92-c&XlA?^-w=Hl<0DhSX!rF#Jfny`N&w45m7`#w(X z9et?9#DM_TM}(dm-(Tc^SGS&3jRQMUfB)R;sb{T&EEzzBkX;X#+Ho1OeucgFs3Qk-^T3_@;LVK{YmMa@>;Pjb0zr@(2Q~j?t z9>aka>HH39yrVAJBP+&%0gV6c{-(L)-ZrJphxloe=1;3`$=;TCnSvljGx_JtFRj_? z$_mTuTQpz;@zF-L)#OBh&yfmdQLONO6`b#k|D5^$nwhm+=&{ig2TaoXPAp(qg}q^H z$H6?(`py_+C*Q~mp~3y&e8GokQZfIs@tFDaL$*{LXh8pO?`H*;ArW2Qsm4YaGxZUd zNa6oiz?kQw5{=V1;FIEe%Qn5_XXWN59O#qoPkq$V2i4~;zQ%zrslFjoW#F3q`gZs} z%+ya5smR}5^6=(!WIY=P4ATAiBvJAnG#qlk0lYsX`#n|IkQ7D#$q@&(r1;8~x3!On z#(Z#KNQzHvSh!S#z;TN9ao;F!J*JGxmk5=oLgj!a=M@%~moak@lx@1f8TGlW7T%!rBa z&RkLX-<1(UsAXsjg<3|&Vx3p*xiK}ZBsFA>^*plQxBeN4>ZxOTYH;b@cQ6!($FW1X zCOi{6R&V@~!=neeIgO%RbIcqTX@OZl z%E$PS3NQha{jS5H?{sK^$_zflQ~NbaU*nKdT^p{uthWWz7|=GB?`oSWkM6aD}R=nYo3PB@dV?lps{HHH!F4s8Oj(h$=;08ADb6 E2g)!bng9R* literal 0 HcmV?d00001 diff --git a/.clangd/index/send_test.cpp.40B395064D1F6AC5.idx b/.clangd/index/send_test.cpp.40B395064D1F6AC5.idx new file mode 100644 index 0000000000000000000000000000000000000000..c8c0a48fbb2a833af4e1d3d5675ad730f9e99b3d GIT binary patch literal 3134 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 { ~RDMABuffer() = default; - const size_t batchSize() + const size_t batch_size() { return storage_view_batch_.size(); } - const storage_view_batch_t& storageViewBatch() + const storage_view_batch_t& view_batch() { return storage_view_batch_; } diff --git a/csrc/engine/rdma/rdma_endpoint.cpp b/csrc/engine/rdma/rdma_endpoint.cpp index 03bf3ed..8acabbf 100644 --- a/csrc/engine/rdma/rdma_endpoint.cpp +++ b/csrc/engine/rdma/rdma_endpoint.cpp @@ -5,7 +5,7 @@ #include "engine/rdma/rdma_common.h" #include "engine/rdma/rdma_context.h" -#include "utils/logging.h" +#include "logging.h" #include #include diff --git a/csrc/engine/rdma/rdma_endpoint.h b/csrc/engine/rdma/rdma_endpoint.h index 5e2a7ea..402613b 100644 --- a/csrc/engine/rdma/rdma_endpoint.h +++ b/csrc/engine/rdma/rdma_endpoint.h @@ -3,7 +3,10 @@ #include "engine/rdma/rdma_context.h" #include +#include #include +#include +#include #include #include #include @@ -33,33 +36,167 @@ typedef struct MetaData { } __attribute__((packed)) meta_data_t; -struct RDMASendRecvTask { - RDMASendRecvTask(std::shared_ptr rdma_endpoint, uint32_t task_id); - RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rmda_opcode, uint32_t task_id); - ~RDMASendRecvTask(); +/** + * RDMAEndPointTask - RDMA endpoint task management + * + * Manages RDMA "pre" (RECV) operations from submission to completion. + * + * Workflow: + * 1. RDMAEndPointTask enqueued --> initiates meta_recv or data_recv (based on operation type) + * 2. Poll queue head's is_finished status repeatedly + * 3. When is_finished == true --> pop RDMAEndPointTask from queue and + * forward information to meta queue + * + * Components: + * 1. rdma_endpoint - For submit operations + * 2. rdma_buffer - For DATA RECV pre post + * 3. AssignmentBatch - + * 4. callback - Callback function in RDMAContext for handling is_finished status + * 5. is_finished - Flag indicating whether the task is completed + */ +struct RDMAEndPointTask +{ + + // Delete default and copy constructors to enforce specific construction + RDMAEndPointTask() = delete; + RDMAEndPointTask(const RDMAEndPointTask&) = delete; + + /** + * Constructor for RECV operations + * @param task_id Unique task identifier + * @param rdma_opcode RDMA operation code + * @param rdma_endpoint RDMA endpoint for operation submission + **/ + RDMAEndPointTask(uint32_t task_id, + OpCode rdma_opcode = OpCode::RECV, + std::shared_ptr rdma_endpoint = nullptr); + + /** + * Constructor for SEND operations + * @param task_id Unique task identifier + * @param rdma_opcode RDMA operation code (default: SEND) + * @param rdma_endpoint RDMA endpoint for operation submission + * @param rdma_buffer RDMA buffer for data transmission + */ + RDMAEndPointTask(uint32_t task_id, + OpCode rdma_opcode = OpCode::SEND, + std::shared_ptr rdma_endpoint = nullptr, + std::shared_ptr rdma_buffer = nullptr); + + ~RDMAEndPointTask(); + + + + + + std::shared_ptr rdma_endpoint_{nullptr}; // Used for submit the assignment_batch_ to RDMAContext + std::shared_ptr rdma_buffer_{nullptr}; // Used for the data pre post recv + std::function callback_; // Store the callback + AssignmentBatch assignment_batch_; + std::atomic is_finished_; // The indicator of the if the recv is finished +}; + + + + + + + + + + + +/** + * ProxyQueue - The Queue template which is used in the proxy + * + * Workflow: + * + * Components: + * + */ +template +class ProxyQueue +{ +private: + std::queue queue_; + mutable std::mutex mutex_; + std::condition_variable cv_; + +public: - int makeAssignmentBatch(); - int makeMetaMR(); - int makeDataMR(); - int makeRemoteDataMR(); - int makeDummyAssignmentBatch(); + ProxyQueue() = default; + ProxyQueue(const ProxyQueue&) = delete; + ProxyQueue& operator=(const ProxyQueue&) = delete; - void configurationTask(std::shared_ptr rdma_buffer, uint32_t unique_slot_id, OpCode rmda_opcode); - uint32_t task_id_; - uint32_t unique_slot_id_; - OpCode rdma_operation_; + void enqueue(T element) + { + { + std::lock_guard lock(mutex_); + queue_.push(std::move(element)); + } + cv_.notify_one(); + } + + T dequeue() + { + std::unique_lock lock(mutex_); + cv_.wait(lock, [this] { return !queue_.empty();}); + + T element = std::move(queue_.front()); + queue_.pop(); + return element; + } + + + bool fetchQueue(T& element) + { + std::lock_guard lock(mutex_); + if (queue_.empty()) + return false; + + element = std::move(queue_.front()); + queue_.pop(); + return true; + } + + + template + bool fetchQueue(T& element, const std::chrono::duration &time_out) + { + std::unique_lock lock(mutex_); + if (!cv_.wait_for(lock, time_out, [this] { return !queue_.empty(); })) + { + return false; + } + element = std::move(queue_.front()); + queue_.pop(); + return true; + } + + bool empty() const + { + std::lock_guard lock(mutex_); + return queue_.empty(); + } - std::shared_ptr rdma_buffer_; - std::shared_ptr rdma_endpoint_; - AssignmentBatch meta_assignment_batch_; - AssignmentBatch dum_meta_assignment_batch_; - AssignmentBatch data_assignment_batch_; - AssignmentBatch dum_data_assignment_batch_; + size_t size() const + { + std::lock_guard lock(mutex_); + return queue_.size(); + } }; + + + + + + + + class RDMASendRecvTaskPool { public: @@ -145,12 +282,27 @@ class RDMAEndpoint: public std::enable_shared_from_this { return data_ctx_qp_num_; } + + private: void mainQueueThread(std::chrono::milliseconds timeout); void asyncRecvData(std::shared_ptr task); void asyncSendData(std::shared_ptr task); + + std::queue meta_recv_queue; + std::queue meta_queue; + std::queue send_queue; + + + mutable std::mutex mutex_proxy + + + + + + std::shared_ptr rdma_task_pool_; size_t data_ctx_qp_num_; @@ -178,4 +330,33 @@ class RDMAEndpoint: public std::enable_shared_from_this { bool RDMA_tasks_threads_running_; }; + +// struct RDMASendRecvTask { + +// RDMASendRecvTask(std::shared_ptr rdma_endpoint, uint32_t task_id); +// RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rdma_opcode, uint32_t task_id); + +// ~RDMASendRecvTask(); + +// int makeAssignmentBatch(); +// int makeMetaMR(); +// int makeDataMR(); +// int makeRemoteDataMR(); +// int makeDummyAssignmentBatch(); + +// void configurationTask(std::shared_ptr rdma_buffer, uint32_t unique_slot_id, OpCode rmda_opcode); + +// uint32_t task_id_; +// uint32_t unique_slot_id_; +// OpCode rdma_operation_; + +// std::shared_ptr rdma_buffer_; +// std::shared_ptr rdma_endpoint_; +// AssignmentBatch meta_assignment_batch_; +// AssignmentBatch dum_meta_assignment_batch_; +// AssignmentBatch data_assignment_batch_; +// AssignmentBatch dum_data_assignment_batch_; + +// }; + } // namespace slime diff --git a/tests/cpp/CMakeLists.txt b/tests/cpp/CMakeLists.txt index cca5acf..33347d8 100644 --- a/tests/cpp/CMakeLists.txt +++ b/tests/cpp/CMakeLists.txt @@ -11,6 +11,36 @@ target_link_libraries( _slime_engine _slime_rdma gflags zmq pthread ) + +add_executable( + send_test + send_test.cpp +) + +target_include_directories(send_test PUBLIC ${ZeroMQ_INCLUDE_DIRS}) + +target_link_libraries( + send_test + PUBLIC + _slime_engine _slime_rdma gflags zmq pthread +) + + +add_executable( + recv_test + recv_test.cpp +) + +target_include_directories(recv_test PUBLIC ${ZeroMQ_INCLUDE_DIRS}) + +target_link_libraries( + recv_test + PUBLIC + _slime_engine _slime_rdma gflags zmq pthread +) + + + if (BUILD_ASCEND_DIRECT) add_subdirectory(ascend_direct) endif() diff --git a/tests/cpp/recv_test.cpp b/tests/cpp/recv_test.cpp new file mode 100644 index 0000000..fac5905 --- /dev/null +++ b/tests/cpp/recv_test.cpp @@ -0,0 +1,96 @@ +#include "engine/rdma/rdma_buffer.h" +#include "engine/rdma/rdma_endpoint.cpp" + +#include +#include +#include +#include +#include + +using json = nlohmann::json; +using namespace slime; + +DEFINE_string(DEVICE_NAME, "rxe_0", "device name"); +DEFINE_uint32(IB_PORT, 1, "device name"); +DEFINE_string(LINK_TYPE, "RoCE", "IB or RoCE"); + +DEFINE_string(PEER_ADDR, "127.0.0.1", "peer IP address"); +DEFINE_int32(PORT_DATA, 5557, "ZMQ control port"); +DEFINE_int32(PORT_MRCN, 5558, "ZMQ control port"); + +int main(int argc, char** argv) +{ + + std::cout << "Init the RMDA ENDPOINT OF SEND... " << std::endl; + // Construct the end_point + auto end_point = std::make_shared(FLAGS_DEVICE_NAME, FLAGS_IB_PORT, FLAGS_LINK_TYPE, 4); + + std::cout << "RDMA QP INFO VIA TCP... " << std::endl; + // RDMA control plane via TCP + zmq::context_t zmq_ctx_data(2); + zmq::context_t zmq_ctx_mmrg(2); + + zmq::socket_t sock_data(zmq_ctx_data, ZMQ_REQ); + zmq::socket_t sock_mmrg(zmq_ctx_mmrg, ZMQ_REQ); + + sock_data.connect("tcp://" + FLAGS_PEER_ADDR + ":" + std::to_string(FLAGS_PORT_DATA)); + sock_mmrg.connect("tcp://" + FLAGS_PEER_ADDR + ":" + std::to_string(FLAGS_PORT_MRCN)); + + zmq::message_t local_data_channel_info(end_point->dataCtxInfo().dump()); + zmq::message_t local_meta_channel_info(end_point->metaCtxInfo().dump()); + + sock_data.send(local_data_channel_info, zmq::send_flags::none); + sock_mmrg.send(local_meta_channel_info, zmq::send_flags::none); + + std::cout << "Send the RDMA Info to other side..." << std::endl; + + zmq::message_t data_channel_info; + zmq::message_t meta_channel_info; + + auto send_data_result = sock_data.recv(data_channel_info); + auto recv_data_result = sock_mmrg.recv(meta_channel_info); + + end_point->connect(json::parse(data_channel_info.to_string()), json::parse(meta_channel_info.to_string())); + std::cout << "Connect Success..." << std::endl; + std::cout << "Finish the connection of QP, start to RECV of buf_0 and buf_1... " << std::endl; + + const uint32_t batch_size_buf_0 = 1; + std::vector data_buf_0_0(8192, 'A'); + std::vector ptrs_buf_0 = {reinterpret_cast(data_buf_0_0.data())}; + std::vector data_sizes_buf_0 = {data_buf_0_0.size()}; + std::vector offset_buf_0 = {0}; + + const uint32_t batch_size_buf_1 = 2; + std::vector data_buf_1_0(1024, 'B'); + std::vector data_buf_1_1(2048, 'C'); + std::vector ptrs_buf_1 = {reinterpret_cast(data_buf_1_0.data()), + reinterpret_cast(data_buf_1_1.data())}; + std::vector data_sizes_buf_1 = {data_buf_1_0.size(), data_buf_1_1.size()}; + std::vector offset_buf_1 = {0, 0}; + + RDMABuffer buf_0(end_point, ptrs_buf_0, data_sizes_buf_0, offset_buf_0); + RDMABuffer buf_1(end_point, ptrs_buf_1, data_sizes_buf_1, offset_buf_0); + std::cout << "Launch EDNPOINT ..." << std::endl; + + buf_1.recv(); + buf_0.recv(); + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Wait Send Complete..." << std::endl; + buf_0.waitRecv(); + buf_1.waitRecv(); + + bool data_buf_0_0_correct = std::all_of(data_buf_0_0.begin(), data_buf_0_0.end(), [](char c) { return c == '0'; }); + bool data_buf_1_0_correct = std::all_of(data_buf_1_0.begin(), data_buf_1_0.end(), [](char c) { return c == '1'; }); + bool data_buf_1_1_correct = std::all_of(data_buf_1_1.begin(), data_buf_1_1.end(), [](char c) { return c == '2'; }); + assert(data_buf_0_0_correct && "Data_0_0 should contain '0'"); + assert(data_buf_1_0_correct && "Data_1_0 should contain '1'"); + assert(data_buf_1_1_correct && "Data_1_1 should contain '2'"); + + std::cout << "The RECV test completed and data verified." << std::endl; + + return 0; +} diff --git a/tests/cpp/send_test.cpp b/tests/cpp/send_test.cpp new file mode 100644 index 0000000..6554a79 --- /dev/null +++ b/tests/cpp/send_test.cpp @@ -0,0 +1,86 @@ +#include "engine/rdma/rdma_buffer.h" +#include "engine/rdma/rdma_endpoint.h" +#include +#include +#include +#include +#include + +using json = nlohmann::json; +using namespace slime; + +DEFINE_string(DEVICE_NAME, "rxe_0", "RDMA device name"); +DEFINE_string(LINK_TYPE, "RoCE", "IB or RoCE"); +DEFINE_int32(IB_PORT, 1, "RDMA port number"); +DEFINE_int32(PORT_DATA, 5557, "ZMQ control port"); +DEFINE_int32(PORT_MRCN, 5558, "ZMQ control port"); + +int main(int argc, char** argv) +{ + + std::cout << "Init the RMDA ENDPOINT OF RECV... " << std::endl; + // Construct the end_point + auto end_point = std::make_shared(FLAGS_DEVICE_NAME, FLAGS_IB_PORT, FLAGS_LINK_TYPE, 4); + + std::cout << "RDMA QP INFO VIA TCP... " << std::endl; + // RDMA control plane via TCP + zmq::context_t zmq_ctx_data(2); + zmq::context_t zmq_ctx_mmrg(2); + + zmq::socket_t sock_data(zmq_ctx_data, ZMQ_REP); + zmq::socket_t sock_mmrg(zmq_ctx_mmrg, ZMQ_REP); + + sock_data.bind("tcp://*:" + std::to_string(FLAGS_PORT_DATA)); + sock_mmrg.bind("tcp://*:" + std::to_string(FLAGS_PORT_MRCN)); + + zmq::message_t data_channel_info; + zmq::message_t mmrg_channel_info; + auto data_channel_info_res = sock_data.recv(data_channel_info); + auto mmrg_channel_info_res = sock_mmrg.recv(mmrg_channel_info); + + std::cout << "Send the RDMA Info to other side..." << std::endl; + zmq::message_t local_data_channel_info(end_point->dataCtxInfo().dump()); + zmq::message_t local_meta_channel_info(end_point->metaCtxInfo().dump()); + + sock_data.send(local_data_channel_info, zmq::send_flags::none); + sock_mmrg.send(local_meta_channel_info, zmq::send_flags::none); + + end_point->connect(json::parse(data_channel_info.to_string()), json::parse(mmrg_channel_info.to_string())); + std::cout << "Connect Success..." << std::endl; + std::cout << "Finish the connection of QP, start to SEND of buf_0 and buf_1..." << std::endl; + + const uint32_t batch_size_buf_0 = 1; + std::vector data_buf_0(8192, '0'); + + std::vector ptrs_buf_0 = {reinterpret_cast(data_buf_0.data())}; + std::vector data_sizes_buf_0 = {data_buf_0.size()}; + std::vector offset_buf_0 = {0}; + + const uint32_t batch_size_buf_1 = 2; + std::vector data_buf_1_0(1024, '1'); + std::vector data_buf_1_1(2048, '2'); + + std::vector ptrs_buf_1 = {reinterpret_cast(data_buf_1_0.data()), + reinterpret_cast(data_buf_1_1.data())}; + std::vector data_sizes_buf_1 = {data_buf_1_0.size(), data_buf_1_1.size()}; + std::vector offset_buf_1 = {0,0}; + + RDMABuffer buf_0(end_point, ptrs_buf_0, data_sizes_buf_0, offset_buf_0); + RDMABuffer buf_1(end_point, ptrs_buf_1, data_sizes_buf_1, offset_buf_1); + std::cout << "Launch EDNPOINT ..." << std::endl; + + buf_1.send(); + buf_0.send(); + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Main thread working Test..." << std::endl; + std::cout << "Wait SEND Complete..." << std::endl; + buf_0.waitSend(); + buf_1.waitSend(); + + std::cout << "The SEND test completed." << std::endl; + + return 0; +} diff --git a/tests/cpp/sendrecv.cpp b/tests/cpp/sendrecv.cpp index 170ef71..7a17e59 100644 --- a/tests/cpp/sendrecv.cpp +++ b/tests/cpp/sendrecv.cpp @@ -10,333 +10,339 @@ #include #include -using json = nlohmann::json; -using namespace slime; -using namespace std::chrono; - -DEFINE_string(device, "rxe_0", "RDMA device name"); -DEFINE_string(LINK_TYPE, "RoCE", "IB or RoCE"); -DEFINE_int32(IB_PORT, 1, "RDMA port number"); -DEFINE_string(PEER_ADDR, "127.0.0.1", "peer IP address"); -DEFINE_int32(PORT_DATA, 5557, "ZMQ control port"); -DEFINE_int32(PORT_META, 5558, "ZMQ control port"); -DEFINE_string(OUTPUT_FILE, "rdma_test_results.csv", "output file for performance results"); - -DEFINE_bool(send, false, "Run in send mode"); -DEFINE_bool(recv, false, "Run in recv mode"); - -DEFINE_int32(num_qp, 1, "Number of QPs"); -DEFINE_int32(num_packets, 100, "Number of packets"); -DEFINE_int32(min_packet_size, 11, "Minimum size of packet size (2^(min_packet_size) bytes)"); -DEFINE_int32(max_packet_size, 11, "Maximum size of packet size (2^(max_packet_size) bytes)"); - -typedef struct Result { - size_t packet_size; - size_t total_bytes; - size_t packet_num; - double min_latency_ms; - double max_latency_ms; - double avg_latency_ms; - double p50_latency_ms; - double p99_latency_ms; - - double min_bandwidth_MBs; - double max_bandwidth_MBs; - double avg_bandwidth_MBs; - - double p50_bandwidth_MBs; - double p99_bandwidth_MBs; - -} Result_t; - -double calculatePercentile(const std::vector& data, double percentile) + +int main() { - if (data.empty()) - return 0.0; - - std::vector sorted_data = data; - std::sort(sorted_data.begin(), sorted_data.end()); - - double position = percentile * (sorted_data.size() - 1); - size_t index = static_cast(position); - double fraction = position - index; - - if (index + 1 < sorted_data.size()) { - return sorted_data[index] + fraction * (sorted_data[index + 1] - sorted_data[index]); - } - else { - return sorted_data[index]; - } + return 0; } -void initConnection(std::shared_ptr& end_point) -{ - if (FLAGS_send) { - std::cout << "Initializing RDMA endpoint in SEND mode..." << std::endl; +// using json = nlohmann::json; +// using namespace slime; +// using namespace std::chrono; + +// DEFINE_string(device, "rxe_0", "RDMA device name"); +// DEFINE_string(LINK_TYPE, "RoCE", "IB or RoCE"); +// DEFINE_int32(IB_PORT, 1, "RDMA port number"); +// DEFINE_string(PEER_ADDR, "127.0.0.1", "peer IP address"); +// DEFINE_int32(PORT_DATA, 5557, "ZMQ control port"); +// DEFINE_int32(PORT_META, 5558, "ZMQ control port"); +// DEFINE_string(OUTPUT_FILE, "rdma_test_results.csv", "output file for performance results"); + +// DEFINE_bool(send, false, "Run in send mode"); +// DEFINE_bool(recv, false, "Run in recv mode"); + +// DEFINE_int32(num_qp, 1, "Number of QPs"); +// DEFINE_int32(num_packets, 100, "Number of packets"); +// DEFINE_int32(min_packet_size, 11, "Minimum size of packet size (2^(min_packet_size) bytes)"); +// DEFINE_int32(max_packet_size, 11, "Maximum size of packet size (2^(max_packet_size) bytes)"); + +// typedef struct Result { +// size_t packet_size; +// size_t total_bytes; +// size_t packet_num; +// double min_latency_ms; +// double max_latency_ms; +// double avg_latency_ms; +// double p50_latency_ms; +// double p99_latency_ms; + +// double min_bandwidth_MBs; +// double max_bandwidth_MBs; +// double avg_bandwidth_MBs; + +// double p50_bandwidth_MBs; +// double p99_bandwidth_MBs; + +// } Result_t; + +// double calculatePercentile(const std::vector& data, double percentile) +// { +// if (data.empty()) +// return 0.0; - zmq::context_t zmq_ctx_data(2); - zmq::context_t zmq_ctx_meta(2); +// std::vector sorted_data = data; +// std::sort(sorted_data.begin(), sorted_data.end()); - zmq::socket_t sock_data(zmq_ctx_data, ZMQ_REP); - zmq::socket_t sock_meta(zmq_ctx_meta, ZMQ_REP); +// double position = percentile * (sorted_data.size() - 1); +// size_t index = static_cast(position); +// double fraction = position - index; - sock_data.bind("tcp://*:" + std::to_string(FLAGS_PORT_DATA)); - sock_meta.bind("tcp://*:" + std::to_string(FLAGS_PORT_META)); +// if (index + 1 < sorted_data.size()) { +// return sorted_data[index] + fraction * (sorted_data[index + 1] - sorted_data[index]); +// } +// else { +// return sorted_data[index]; +// } +// } + +// void initConnection(std::shared_ptr& end_point) +// { +// if (FLAGS_send) { +// std::cout << "Initializing RDMA endpoint in SEND mode..." << std::endl; - zmq::message_t peer_data_info; - zmq::message_t peer_meta_info; - auto data_res = sock_data.recv(peer_data_info); - auto meta_res = sock_meta.recv(peer_meta_info); +// zmq::context_t zmq_ctx_data(2); +// zmq::context_t zmq_ctx_meta(2); - zmq::message_t local_data_info(end_point->getDataContextInfo().dump()); - zmq::message_t local_meta_info(end_point->getMetaContextInfo().dump()); +// zmq::socket_t sock_data(zmq_ctx_data, ZMQ_REP); +// zmq::socket_t sock_meta(zmq_ctx_meta, ZMQ_REP); - sock_data.send(local_data_info, zmq::send_flags::none); - sock_meta.send(local_meta_info, zmq::send_flags::none); +// sock_data.bind("tcp://*:" + std::to_string(FLAGS_PORT_DATA)); +// sock_meta.bind("tcp://*:" + std::to_string(FLAGS_PORT_META)); - end_point->connect( - json::parse(std::string(static_cast(peer_data_info.data()), peer_data_info.size())), - json::parse(std::string(static_cast(peer_meta_info.data()), peer_meta_info.size())) - ); - } +// zmq::message_t peer_data_info; +// zmq::message_t peer_meta_info; +// auto data_res = sock_data.recv(peer_data_info); +// auto meta_res = sock_meta.recv(peer_meta_info); + +// zmq::message_t local_data_info(end_point->getDataContextInfo().dump()); +// zmq::message_t local_meta_info(end_point->getMetaContextInfo().dump()); + +// sock_data.send(local_data_info, zmq::send_flags::none); +// sock_meta.send(local_meta_info, zmq::send_flags::none); + +// end_point->connect( +// json::parse(std::string(static_cast(peer_data_info.data()), peer_data_info.size())), +// json::parse(std::string(static_cast(peer_meta_info.data()), peer_meta_info.size())) +// ); +// } - else if (FLAGS_recv) { - std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; - - zmq::context_t zmq_ctx_data(2); - zmq::context_t zmq_ctx_meta(2); - std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; - zmq::socket_t sock_data(zmq_ctx_data, ZMQ_REQ); - zmq::socket_t sock_meta(zmq_ctx_meta, ZMQ_REQ); - - sock_data.connect("tcp://" + FLAGS_PEER_ADDR + ":" + std::to_string(FLAGS_PORT_DATA)); - sock_meta.connect("tcp://" + FLAGS_PEER_ADDR + ":" + std::to_string(FLAGS_PORT_META)); - std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; - zmq::message_t local_data_info(end_point->getDataContextInfo().dump()); - zmq::message_t local_meta_info(end_point->getMetaContextInfo().dump()); - - sock_data.send(local_data_info, zmq::send_flags::none); - sock_meta.send(local_meta_info, zmq::send_flags::none); - std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; - zmq::message_t peer_data_info; - zmq::message_t peer_meta_info; - auto data_res = sock_data.recv(peer_data_info); - auto meta_res = sock_meta.recv(peer_meta_info); - std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; - end_point->connect( - json::parse(std::string(static_cast(peer_data_info.data()), peer_data_info.size())), - json::parse(std::string(static_cast(peer_meta_info.data()), peer_meta_info.size())) - ); - } - - std::cout << "RDMA Endpoint connection has been successfully established." << std::endl; -} +// else if (FLAGS_recv) { +// std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; + +// zmq::context_t zmq_ctx_data(2); +// zmq::context_t zmq_ctx_meta(2); +// std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; +// zmq::socket_t sock_data(zmq_ctx_data, ZMQ_REQ); +// zmq::socket_t sock_meta(zmq_ctx_meta, ZMQ_REQ); + +// sock_data.connect("tcp://" + FLAGS_PEER_ADDR + ":" + std::to_string(FLAGS_PORT_DATA)); +// sock_meta.connect("tcp://" + FLAGS_PEER_ADDR + ":" + std::to_string(FLAGS_PORT_META)); +// std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; +// zmq::message_t local_data_info(end_point->getDataContextInfo().dump()); +// zmq::message_t local_meta_info(end_point->getMetaContextInfo().dump()); + +// sock_data.send(local_data_info, zmq::send_flags::none); +// sock_meta.send(local_meta_info, zmq::send_flags::none); +// std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; +// zmq::message_t peer_data_info; +// zmq::message_t peer_meta_info; +// auto data_res = sock_data.recv(peer_data_info); +// auto meta_res = sock_meta.recv(peer_meta_info); +// std::cout << "Initializing RDMA endpoint in RECV mode..." << std::endl; +// end_point->connect( +// json::parse(std::string(static_cast(peer_data_info.data()), peer_data_info.size())), +// json::parse(std::string(static_cast(peer_meta_info.data()), peer_meta_info.size())) +// ); +// } -int singleTest(std::shared_ptr end_point, - std::shared_ptr buf, - size_t iterations, - size_t packet_size, - double& latency, - double& bandwidth) -{ +// std::cout << "RDMA Endpoint connection has been successfully established." << std::endl; +// } - std::vector> futures; - std::atomic completed(0); - // warm up - if (FLAGS_send) { - buf->send(); - buf->waitSend(); - } - else if (FLAGS_recv) { - buf->recv(); - buf->waitRecv(); - } - auto start = std::chrono::high_resolution_clock::now(); - for (size_t i = 0; i < iterations; i++) { - // //auto buffer = std::make_shared(buf->endpoint_, buf->ptrs_, buf->offset_, buf->data_size_); - // if (FLAGS_send) { - // buffer->send(); - // futures.emplace_back(std::async(std::launch::async, [&buffer, &completed]() { - // buffer->waitSend(); - // completed++; - // })); - // } - // else if (FLAGS_recv) { - // buffer->recv(); - // futures.emplace_back(std::async(std::launch::async, [&buffer, &completed]() { - // buffer->waitRecv(); - // completed++; - // })); - // } - } - for (auto& fut : futures) { - fut.wait(); - } - auto end = std::chrono::high_resolution_clock::now(); - auto duration_ns = std::chrono::duration_cast(end - start); - - double total_bytes = 2 * packet_size * iterations; - latency = (duration_ns.count() / 1000000.0); - bandwidth = (total_bytes / (duration_ns.count() / 1000000000.0)) / (1024.0 * 1024.0); - return completed; -} +// int singleTest(std::shared_ptr end_point, +// std::shared_ptr buf, +// size_t iterations, +// size_t packet_size, +// double& latency, +// double& bandwidth) +// { -void runTest(std::shared_ptr end_point, size_t packet_size, size_t total_bytes, Result_t& result) -{ - const size_t num_packets = total_bytes / packet_size; - std::vector data_buffer_0(packet_size, FLAGS_send ? 'A' : '0'); - std::vector data_buffer_1(packet_size, FLAGS_send ? 'A' : '0'); - std::vector ptrs = {reinterpret_cast(data_buffer_0.data()),reinterpret_cast(data_buffer_1.data())}; - std::vector offsets = {0,0}; - std::vector sizes = {data_buffer_0.size(), data_buffer_1.size()}; - auto buf = std::make_shared(end_point, ptrs, offsets, sizes); - - std::vector latencies; - std::vector bandwidths; - std::vector success_rates; - - size_t num_tests = FLAGS_num_packets; - size_t iterations = FLAGS_num_packets / num_tests; - for (size_t i = 0; i < num_tests; ++i) { - auto buf = std::make_shared(end_point, ptrs, offsets, sizes); - double latency; - double bandwidth; - int success_count = singleTest(end_point, buf, iterations, packet_size, latency, bandwidth); - latencies.push_back(latency); - bandwidths.push_back(bandwidth); - } - // statistic - - for (auto lat : latencies) - std::cout << "Latency: " << lat << " ms\n"; - - auto [min_lat, max_lat] = std::minmax_element(latencies.begin(), latencies.end()); - double sum_lat = std::accumulate(latencies.begin(), latencies.end(), 0.0); - double mean_lat = sum_lat / FLAGS_num_packets; - - auto [min_bw, max_bw] = std::minmax_element(bandwidths.begin(), bandwidths.end()); - double sum_bw = std::accumulate(bandwidths.begin(), bandwidths.end(), 0.0); - double mean_bw = sum_bw / num_tests; - - // Store results - result.packet_size = packet_size; - result.total_bytes = total_bytes; - result.packet_num = FLAGS_num_packets; - result.min_latency_ms = *min_lat; - result.max_latency_ms = *max_lat; - result.avg_latency_ms = mean_lat; - result.p50_latency_ms = calculatePercentile(latencies, 0.50); - result.p99_latency_ms = calculatePercentile(latencies, 0.99); - - result.min_bandwidth_MBs = *min_bw; - result.max_bandwidth_MBs = *max_bw; - result.avg_bandwidth_MBs = mean_bw; - result.p50_bandwidth_MBs = calculatePercentile(bandwidths, 0.50); - result.p99_bandwidth_MBs = calculatePercentile(bandwidths, 0.99); -} +// std::vector> futures; +// std::atomic completed(0); +// // warm up +// if (FLAGS_send) { +// buf->send(); +// buf->waitSend(); +// } +// else if (FLAGS_recv) { +// buf->recv(); +// buf->waitRecv(); +// } +// auto start = std::chrono::high_resolution_clock::now(); +// for (size_t i = 0; i < iterations; i++) { +// // //auto buffer = std::make_shared(buf->endpoint_, buf->ptrs_, buf->offset_, buf->data_size_); +// // if (FLAGS_send) { +// // buffer->send(); +// // futures.emplace_back(std::async(std::launch::async, [&buffer, &completed]() { +// // buffer->waitSend(); +// // completed++; +// // })); +// // } +// // else if (FLAGS_recv) { +// // buffer->recv(); +// // futures.emplace_back(std::async(std::launch::async, [&buffer, &completed]() { +// // buffer->waitRecv(); +// // completed++; +// // })); +// // } +// } +// for (auto& fut : futures) { +// fut.wait(); +// } +// auto end = std::chrono::high_resolution_clock::now(); +// auto duration_ns = std::chrono::duration_cast(end - start); -void print(const std::vector& results) -{ - std::cout << "\nPerformance Results:\n"; - - // 打印延迟统计 - std::cout << "\nLatency Statistics (ms):\n"; - std::cout << "================================================================================\n"; - std::cout << std::left << std::setw(12) << "Size(B)" << std::setw(12) << "Packets" << std::setw(12) << "Min" - << std::setw(12) << "Max" << std::setw(12) << "Avg" << std::setw(12) << "P50" << std::setw(12) << "P99" - << std::endl; - std::cout << "================================================================================\n"; - - for (const auto& res : results) { - std::cout << std::left << std::setw(12) << res.packet_size << std::setw(12) << res.packet_num << std::setw(12) - << std::setprecision(4) << res.min_latency_ms << std::setw(12) << std::setprecision(4) - << res.max_latency_ms << std::setw(12) << std::setprecision(4) << res.avg_latency_ms << std::setw(12) - << std::setprecision(4) << res.p50_latency_ms << std::setw(12) << std::setprecision(4) - << res.p99_latency_ms << std::endl; - } - std::cout << "================================================================================\n"; - - // 打印带宽统计 - std::cout << "\nBandwidth Statistics (MB/s):\n"; - std::cout << "================================================================================\n"; - std::cout << std::left << std::setw(12) << "Size(B)" << std::setw(12) << "Total(B)" << std::setw(12) << "Min" - << std::setw(12) << "Max" << std::setw(12) << "Avg" << std::setw(12) << "P50" << std::setw(12) << "P99" - << std::endl; - std::cout << "================================================================================\n"; - - for (const auto& res : results) { - std::cout << std::left << std::setw(12) << res.packet_size << std::setw(12) << res.total_bytes << std::setw(12) - << std::setprecision(4) << res.min_bandwidth_MBs << std::setw(12) << std::setprecision(4) - << res.max_bandwidth_MBs << std::setw(12) << std::setprecision(4) << res.avg_bandwidth_MBs - << std::setw(12) << std::setprecision(4) << res.p50_bandwidth_MBs << std::setw(12) - << std::setprecision(4) << res.p99_bandwidth_MBs << std::endl; - } - std::cout << "================================================================================\n"; -} +// double total_bytes = 2 * packet_size * iterations; +// latency = (duration_ns.count() / 1000000.0); +// bandwidth = (total_bytes / (duration_ns.count() / 1000000000.0)) / (1024.0 * 1024.0); +// return completed; +// } -// void save(const std::vector& results, const std::string& filename) +// void runTest(std::shared_ptr end_point, size_t packet_size, size_t total_bytes, Result_t& result) // { -// std::ofstream outfile(filename); -// if (!outfile.is_open()) { -// std::cerr << "Failed to open output file: " << filename << std::endl; -// return; +// const size_t num_packets = total_bytes / packet_size; +// std::vector data_buffer_0(packet_size, FLAGS_send ? 'A' : '0'); +// std::vector data_buffer_1(packet_size, FLAGS_send ? 'A' : '0'); +// std::vector ptrs = {reinterpret_cast(data_buffer_0.data()),reinterpret_cast(data_buffer_1.data())}; +// std::vector offsets = {0,0}; +// std::vector sizes = {data_buffer_0.size(), data_buffer_1.size()}; +// auto buf = std::make_shared(end_point, ptrs, offsets, sizes); + +// std::vector latencies; +// std::vector bandwidths; +// std::vector success_rates; + +// size_t num_tests = FLAGS_num_packets; +// size_t iterations = FLAGS_num_packets / num_tests; +// for (size_t i = 0; i < num_tests; ++i) { +// auto buf = std::make_shared(end_point, ptrs, offsets, sizes); +// double latency; +// double bandwidth; +// int success_count = singleTest(end_point, buf, iterations, packet_size, latency, bandwidth); +// latencies.push_back(latency); +// bandwidths.push_back(bandwidth); // } +// // statistic + +// for (auto lat : latencies) +// std::cout << "Latency: " << lat << " ms\n"; + +// auto [min_lat, max_lat] = std::minmax_element(latencies.begin(), latencies.end()); +// double sum_lat = std::accumulate(latencies.begin(), latencies.end(), 0.0); +// double mean_lat = sum_lat / FLAGS_num_packets; + +// auto [min_bw, max_bw] = std::minmax_element(bandwidths.begin(), bandwidths.end()); +// double sum_bw = std::accumulate(bandwidths.begin(), bandwidths.end(), 0.0); +// double mean_bw = sum_bw / num_tests; + +// // Store results +// result.packet_size = packet_size; +// result.total_bytes = total_bytes; +// result.packet_num = FLAGS_num_packets; +// result.min_latency_ms = *min_lat; +// result.max_latency_ms = *max_lat; +// result.avg_latency_ms = mean_lat; +// result.p50_latency_ms = calculatePercentile(latencies, 0.50); +// result.p99_latency_ms = calculatePercentile(latencies, 0.99); + +// result.min_bandwidth_MBs = *min_bw; +// result.max_bandwidth_MBs = *max_bw; +// result.avg_bandwidth_MBs = mean_bw; +// result.p50_bandwidth_MBs = calculatePercentile(bandwidths, 0.50); +// result.p99_bandwidth_MBs = calculatePercentile(bandwidths, 0.99); +// } + +// void print(const std::vector& results) +// { +// std::cout << "\nPerformance Results:\n"; -// outfile << "Packet Size (Bytes),Total Bytes (Bytes),Amount of Packet" -// << "Min Latency (ms),Max Latency (ms),Avg Latency (ms), Latency StdDev (ms)," -// << "Min Bandwidth (MB/s),Max Bandwidth (MB/s),Avg Bandwidth (MB/s),Bandwidth StdDev (MB/s)," -// << "Success Rate (%)\n"; +// // 打印延迟统计 +// std::cout << "\nLatency Statistics (ms):\n"; +// std::cout << "================================================================================\n"; +// std::cout << std::left << std::setw(12) << "Size(B)" << std::setw(12) << "Packets" << std::setw(12) << "Min" +// << std::setw(12) << "Max" << std::setw(12) << "Avg" << std::setw(12) << "P50" << std::setw(12) << "P99" +// << std::endl; +// std::cout << "================================================================================\n"; // for (const auto& res : results) { -// outfile << res.packet_size << "," << res.total_bytes << "," << res.packet_num << "," << std::setprecision(9) -// << res.min_latency_ms << "," << res.max_latency_ms << "," << res.avg_latency_ms << "," -// << res.stddev_latency << "," << res.min_bandwidth_MBs << "," << res.max_bandwidth_MBs << "," -// << res.avg_bandwidth_MBs << "," << res.stddev_bandwidth << "," << res.success_rate << "\n"; +// std::cout << std::left << std::setw(12) << res.packet_size << std::setw(12) << res.packet_num << std::setw(12) +// << std::setprecision(4) << res.min_latency_ms << std::setw(12) << std::setprecision(4) +// << res.max_latency_ms << std::setw(12) << std::setprecision(4) << res.avg_latency_ms << std::setw(12) +// << std::setprecision(4) << res.p50_latency_ms << std::setw(12) << std::setprecision(4) +// << res.p99_latency_ms << std::endl; // } +// std::cout << "================================================================================\n"; -// outfile.close(); -// std::cout << "Results saved to " << filename << std::endl; +// // 打印带宽统计 +// std::cout << "\nBandwidth Statistics (MB/s):\n"; +// std::cout << "================================================================================\n"; +// std::cout << std::left << std::setw(12) << "Size(B)" << std::setw(12) << "Total(B)" << std::setw(12) << "Min" +// << std::setw(12) << "Max" << std::setw(12) << "Avg" << std::setw(12) << "P50" << std::setw(12) << "P99" +// << std::endl; +// std::cout << "================================================================================\n"; + +// for (const auto& res : results) { +// std::cout << std::left << std::setw(12) << res.packet_size << std::setw(12) << res.total_bytes << std::setw(12) +// << std::setprecision(4) << res.min_bandwidth_MBs << std::setw(12) << std::setprecision(4) +// << res.max_bandwidth_MBs << std::setw(12) << std::setprecision(4) << res.avg_bandwidth_MBs +// << std::setw(12) << std::setprecision(4) << res.p50_bandwidth_MBs << std::setw(12) +// << std::setprecision(4) << res.p99_bandwidth_MBs << std::endl; +// } +// std::cout << "================================================================================\n"; // } -int main(int argc, char** argv) -{ - gflags::ParseCommandLineFlags(&argc, &argv, true); +// // void save(const std::vector& results, const std::string& filename) +// // { +// // std::ofstream outfile(filename); +// // if (!outfile.is_open()) { +// // std::cerr << "Failed to open output file: " << filename << std::endl; +// // return; +// // } + +// // outfile << "Packet Size (Bytes),Total Bytes (Bytes),Amount of Packet" +// // << "Min Latency (ms),Max Latency (ms),Avg Latency (ms), Latency StdDev (ms)," +// // << "Min Bandwidth (MB/s),Max Bandwidth (MB/s),Avg Bandwidth (MB/s),Bandwidth StdDev (MB/s)," +// // << "Success Rate (%)\n"; + +// // for (const auto& res : results) { +// // outfile << res.packet_size << "," << res.total_bytes << "," << res.packet_num << "," << std::setprecision(9) +// // << res.min_latency_ms << "," << res.max_latency_ms << "," << res.avg_latency_ms << "," +// // << res.stddev_latency << "," << res.min_bandwidth_MBs << "," << res.max_bandwidth_MBs << "," +// // << res.avg_bandwidth_MBs << "," << res.stddev_bandwidth << "," << res.success_rate << "\n"; +// // } + +// // outfile.close(); +// // std::cout << "Results saved to " << filename << std::endl; +// // } + +// int main(int argc, char** argv) +// { +// gflags::ParseCommandLineFlags(&argc, &argv, true); - if (!FLAGS_send && !FLAGS_recv) { - std::cerr << "Please specify mode: --send or --recv" << std::endl; - return 1; - } +// if (!FLAGS_send && !FLAGS_recv) { +// std::cerr << "Please specify mode: --send or --recv" << std::endl; +// return 1; +// } - if (FLAGS_send && FLAGS_recv) { - std::cerr << "Cannot specify both --send and --recv" << std::endl; - return 1; - } +// if (FLAGS_send && FLAGS_recv) { +// std::cerr << "Cannot specify both --send and --recv" << std::endl; +// return 1; +// } - auto end_point = std::make_shared(FLAGS_device, FLAGS_IB_PORT, FLAGS_LINK_TYPE, FLAGS_num_qp); - initConnection(end_point); +// auto end_point = std::make_shared(FLAGS_device, FLAGS_IB_PORT, FLAGS_LINK_TYPE, FLAGS_num_qp); +// initConnection(end_point); - std::vector packet_sizes; - for (int i = FLAGS_min_packet_size; i <= FLAGS_max_packet_size; i++) { - packet_sizes.push_back(1 << i); - } +// std::vector packet_sizes; +// for (int i = FLAGS_min_packet_size; i <= FLAGS_max_packet_size; i++) { +// packet_sizes.push_back(1 << i); +// } - std::vector results; - for (size_t size : packet_sizes) { +// std::vector results; +// for (size_t size : packet_sizes) { - size_t total_bytes = size * FLAGS_num_packets; - std::cout << "\nTesting with packet size: " << size << " bytes (" << (size >> 10) - << " KB), total: " << (double)total_bytes / (1024 * 1024) - << " MB, number of packets: " << FLAGS_num_packets << std::endl; +// size_t total_bytes = size * FLAGS_num_packets; +// std::cout << "\nTesting with packet size: " << size << " bytes (" << (size >> 10) +// << " KB), total: " << (double)total_bytes / (1024 * 1024) +// << " MB, number of packets: " << FLAGS_num_packets << std::endl; - Result_t result; - runTest(end_point, size, total_bytes, result); - results.push_back(result); - } +// Result_t result; +// runTest(end_point, size, total_bytes, result); +// results.push_back(result); +// } - print(results); - // save(results, FLAGS_OUTPUT_FILE); +// print(results); +// // save(results, FLAGS_OUTPUT_FILE); - return 0; -} \ No newline at end of file +// return 0; +// } \ No newline at end of file From 6676c6f0d6c778a448bb0a8f7cc2f596a3e0aa67 Mon Sep 17 00:00:00 2001 From: HaoLiuuu Date: Sun, 19 Oct 2025 21:23:22 +0800 Subject: [PATCH 4/9] remove the clangd change --- .clangd/index/affinity.h.EBE212AD8338670F.idx | Bin 764 -> 0 bytes .../index/assignment.cpp.4ED8BC4CD3E27642.idx | Bin 794 -> 0 bytes .../index/assignment.h.89BA5D141D7922F0.idx | Bin 1442 -> 0 bytes .clangd/index/env.h.0FEC928CA83B4A12.idx | Bin 716 -> 0 bytes .../index/ibv_helper.cpp.B9C37D37FE9C9E0F.idx | Bin 1394 -> 0 bytes .../index/ibv_helper.h.619E178CE5CD4A5A.idx | Bin 1164 -> 0 bytes .clangd/index/json.hpp.694255043525F602.idx | Bin 204696 -> 0 bytes .clangd/index/logging.h.639CE923F9EFB290.idx | Bin 886 -> 0 bytes .../index/memory_pool.cpp.F09354329F2DF7D7.idx | Bin 2320 -> 0 bytes .../index/memory_pool.h.BE12C74670B7FCD5.idx | Bin 2426 -> 0 bytes .../rdma_assignment.cpp.EBA22C6C3C860620.idx | Bin 1644 -> 0 bytes .../rdma_assignment.h.65691B28352F6406.idx | Bin 4080 -> 0 bytes .../index/rdma_buffer.cpp.3EBA5C344A7697A3.idx | Bin 1326 -> 0 bytes .../index/rdma_buffer.h.C3DF879253C4FDCB.idx | Bin 2490 -> 0 bytes .../index/rdma_common.h.605E04D5C1594016.idx | Bin 616 -> 0 bytes .../index/rdma_config.h.96DB954603481CC6.idx | Bin 1690 -> 0 bytes .../rdma_context.cpp.A7591533B08A3B05.idx | Bin 11958 -> 0 bytes .../index/rdma_context.h.AB3EB1CCA367DFDD.idx | Bin 6456 -> 0 bytes .../rdma_endpoint.cpp.91B25917C6102BE7.idx | Bin 6954 -> 0 bytes .../index/rdma_endpoint.h.38575B1A8A5DD704.idx | Bin 6796 -> 0 bytes .clangd/index/rdma_env.h.41EE5E62C891E141.idx | Bin 1204 -> 0 bytes .../rdma_scheduler.cpp.8797BF9AAD52AD88.idx | Bin 1970 -> 0 bytes .../rdma_scheduler.h.3C45BC5D63D0F8B8.idx | Bin 2454 -> 0 bytes .../index/recv_test.cpp.FE47085B653922D7.idx | Bin 2438 -> 0 bytes .../index/send_test.cpp.40B395064D1F6AC5.idx | Bin 3134 -> 0 bytes .../index/sendrecv.cpp.2A1EA1731CF16F30.idx | Bin 552 -> 0 bytes .clangd/index/utils.cpp.22E6B8ED360F65D7.idx | Bin 1252 -> 0 bytes .clangd/index/utils.h.4F2362F6A4B3ABA7.idx | Bin 534 -> 0 bytes 28 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .clangd/index/affinity.h.EBE212AD8338670F.idx delete mode 100644 .clangd/index/assignment.cpp.4ED8BC4CD3E27642.idx delete mode 100644 .clangd/index/assignment.h.89BA5D141D7922F0.idx delete mode 100644 .clangd/index/env.h.0FEC928CA83B4A12.idx delete mode 100644 .clangd/index/ibv_helper.cpp.B9C37D37FE9C9E0F.idx delete mode 100644 .clangd/index/ibv_helper.h.619E178CE5CD4A5A.idx delete mode 100644 .clangd/index/json.hpp.694255043525F602.idx delete mode 100644 .clangd/index/logging.h.639CE923F9EFB290.idx delete mode 100644 .clangd/index/memory_pool.cpp.F09354329F2DF7D7.idx delete mode 100644 .clangd/index/memory_pool.h.BE12C74670B7FCD5.idx delete mode 100644 .clangd/index/rdma_assignment.cpp.EBA22C6C3C860620.idx delete mode 100644 .clangd/index/rdma_assignment.h.65691B28352F6406.idx delete mode 100644 .clangd/index/rdma_buffer.cpp.3EBA5C344A7697A3.idx delete mode 100644 .clangd/index/rdma_buffer.h.C3DF879253C4FDCB.idx delete mode 100644 .clangd/index/rdma_common.h.605E04D5C1594016.idx delete mode 100644 .clangd/index/rdma_config.h.96DB954603481CC6.idx delete mode 100644 .clangd/index/rdma_context.cpp.A7591533B08A3B05.idx delete mode 100644 .clangd/index/rdma_context.h.AB3EB1CCA367DFDD.idx delete mode 100644 .clangd/index/rdma_endpoint.cpp.91B25917C6102BE7.idx delete mode 100644 .clangd/index/rdma_endpoint.h.38575B1A8A5DD704.idx delete mode 100644 .clangd/index/rdma_env.h.41EE5E62C891E141.idx delete mode 100644 .clangd/index/rdma_scheduler.cpp.8797BF9AAD52AD88.idx delete mode 100644 .clangd/index/rdma_scheduler.h.3C45BC5D63D0F8B8.idx delete mode 100644 .clangd/index/recv_test.cpp.FE47085B653922D7.idx delete mode 100644 .clangd/index/send_test.cpp.40B395064D1F6AC5.idx delete mode 100644 .clangd/index/sendrecv.cpp.2A1EA1731CF16F30.idx delete mode 100644 .clangd/index/utils.cpp.22E6B8ED360F65D7.idx delete mode 100644 .clangd/index/utils.h.4F2362F6A4B3ABA7.idx diff --git a/.clangd/index/affinity.h.EBE212AD8338670F.idx b/.clangd/index/affinity.h.EBE212AD8338670F.idx deleted file mode 100644 index 75a6234fbe1a1dc6bbf0016ac5012f5efaece2e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 764 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 diff --git a/.clangd/index/env.h.0FEC928CA83B4A12.idx b/.clangd/index/env.h.0FEC928CA83B4A12.idx deleted file mode 100644 index 8ee98cdd0779f3dee10e3f6fbe8e8763b9cf6bba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 716 zcmWIYbaOkx#K7R3;#rZKT9U}Zz`(!*#Kk2=nMZ+i2O|SR#hl*rw%msd1lS(@)|3

J-;V!_wae@g3P8KFc1{MZJcE+9d#s<^(zrMxGzzkHODX6IcCV(n{+SyovDn86){^nNT zzk-R0A1DL@>|9)2{16pDUQudVF&{A0V0JMvf$d;n*HqTjV*VZcQtR=-WsEHB^8E5b zU0x-2--7pj0&zv;rF)i4yPIG0@-~nwqbQ>u%;~x}H&vjInT1{2O4ADZD8(xyJoTa00f&7{UIDes^2$n16>k@FtP`mNa^)1|RPQ7| zFt(esKAVw=osWZ0Ab#b_ZL5PU(}A**B9apGJvQcV3M|+FfPG8BAgeW z08LO3R*)}B%}E4>RdG>raWII%zAk-3G0%$)qHG+TT--do44fcQ2;hJ*AZZcG<3@<` JBA8qVCIARI${7Fv diff --git a/.clangd/index/ibv_helper.cpp.B9C37D37FE9C9E0F.idx b/.clangd/index/ibv_helper.cpp.B9C37D37FE9C9E0F.idx deleted file mode 100644 index ae87be299414a0b4dad41367ab593d695ee1f5bf..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1394 zcmb7@|5s9X7{@R8F;Ffba=9pep>c9VVXH+Fk|V!imS$)k8K4anuuf`|jP>n~@hLtzqTiIc`S#wszb^Lc+_5~KL6=4t-n6%w z)=vy|TzaDXAY<(1wJ7<~Yg2*rfJLL>>|ENNvn$V+Y>6^mDEQ|&`cV6I_4$Sse{f8s zv&ZB0wdEUpS9#Y)efQzs)Mz>PI%ecDDtjC57JrnoNnYylYVP$x)s*}vzR#Ed@EyA_TO90P~wf-iIQKr>3d#E+VhK7xgr#pi^WtOTdF_&pGuSV8LLY4Mg zXE${Dw_Ny?f4V-QUa?MMCJf|u>xPaiBJ$644Zi$+bXvOqaOuQ+?%SCQ`^rzcO`Dbd ziRWr{nrwR7+AUGy>$i1VD@7}A*dHmeG?K=j=5}o8zt=K(#L$ z-r7*M8%10Y1lI#S&h`bqIN|{8iHIAEz$VZP2Yz~RV_*%6aG-(+h-|h9h}cj82>&VQ zqrA$VJDPPU;tCZ)AjH`cAh9tP* z?!^3@xpg8tfD2W^RDkQ{dNsh4K$2_c{_fMC1ev=F=qSli5lLwkj8Li(0XT{>nM!QiS$T@ z5V4mSz6y{8$TaI)=|9M3$00<4DzI1|&LhR;t1<8cWdfNWgm{PfLx@1>$319RS?D#2 zL8XK&p$t==^i&2D4#AQMm=3%bNxB90;6yNaim!vv)kb+U%Td#QAj~0zN!*Uf^ zg{JFvYT++~7}FDsY+JGrYzacZMbF9wjn03n2y7%FIdLZqHl0vs64`-5r!pNGDD7V+ Cd)ABq diff --git a/.clangd/index/ibv_helper.h.619E178CE5CD4A5A.idx b/.clangd/index/ibv_helper.h.619E178CE5CD4A5A.idx deleted file mode 100644 index cf4545d98c6a3590fb407cbb389b0375a87ed344..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1164 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 diff --git a/.clangd/index/logging.h.639CE923F9EFB290.idx b/.clangd/index/logging.h.639CE923F9EFB290.idx deleted file mode 100644 index e11db399eea1d3e8d0953d015450d71c6524eead..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 886 zcmWIYbaTsNW?*nm@vO*AElFfyU|`?@;^LB`%*Q}_7b62h#hl&=2fbJfdD?z!aPK~} zj{S7SW_Hb2t^)d>8B`~qJjY_ByJc>oV@>(yT}^T+Edm~&o<8?KQ}_Lvh0n$-ynPeQ zuZv9I;J_!SR%Jh9@3HvD&5qkYuitkiu9&03%CtAXmc7+*$}CI1@3)V?HNX68v)c;E z9nU9}fBlyhB=+Y!C6uS(?L7N^2r;@`e17zgl9UvE_6G@o~?Y>SxC;f=G3_8qre zEH`)enp%dfE|ZNIiYs%IT!4O^kU!VxbWBh#4+Aeh1G_Z0v;>%7;ACN9V&G$CWM@2j zcan?Q{ZFPm3_L&;e8POnU;?OunUO(&1*oDZHLdt4Fw9^o8JQr;@@6le5%;V-@L_ z2XbRMV#Oxxbm|YWpR^OmP3A}zhdCO^1-Uqw({*odsz4tT3p*bVpAZYXsEDYP$gSW> z3wtGWfNGRDlq3YiN*J`~<}?7g?i}vol1I|6&-ti66UcSqa1xIZ4fK0;edabG*M-AH ze7?uV{7r!c8yH#Gg$0Dg5~j=yQ=KSj43tgaNDx!}@Axx+%PEji${flPNu~}NKR>&)RSzG8J4 zZ%U~4R3O)!!(2q;8~5&=%cuPSa@{!G#PiKeS~3o=js|koIMgIQ#Wb$(aog_)x*4M0Il&M|72!ak2>(W{)RcGI^z#m<+de_Rg_Q}3y zpM8G&oPDi^q$DbvZSC!=25k#^*LDV+4Gz^uVSJ8bBu1VXc7FTbc(1|B zW;p*i1ARg|g_@MPi$(Xo*)ALj8;E@Evx_ZDd2JQ#^_w-18!DS7ddhUA#pR9nI*y$# z`c6Hi?zodWYW-Q=J^WHT>-eB}vDd?C>-fnn|MvT!tG0NwVxog#?3E{te3AGUyZLXoZv6mRo5^W`_6dY<3r(&k-_mPTYV?2eb>8-h#PgC z_{=HAKacpA|7Fvfij0%}X|7e((?-4zXI1Ob%W=U^TAeeFUrBu!Tz7Gz@$k{oqRgGy zoc_|p$i~5`{J^A|9Jyyr^S6Y~^FT@IBIQs>w?U~AKAr%N9duKMBX_uDJ) zz5jejk!>EgJlL1LWbLuPCa&w9?&>i-A7Tv$J*{3_zI+(ew%a$ZjQxIvm}Ys22`XL? zAI4R&RY3%sDF-}p`@XvVN>}T8nk521DIgbc?XqE@~GliJ}Ts=oGn)SCnZE-L!xGAAoZs6Ao^%3@6jwzm>(brP;eD&h2QM_s-hLuo{j|Ziu}~p zv}kbHIiW2Si1rN7xXhC-ewWDJX@Fs7xXg>@1YUt{;=$A{9lA zFv}!2h3+_|+ny2Ea0?A#*YIKeE|<+E5ycboJP_={7pf!U$%LQ`3IaP-km{~@ ztL^O2FC&MblrM_HMEGrKltis{S9=W(}Y^ni< zBqo=K$96jnti=M5B197s2^%zOt?k=eS=m>^Xpoddq${EVPykSlX?83cT$b{^DZCW=vF(JC6o$7V;o1Tr-?ud zQ%VjcfKqa#Kov&?jN(KAbsQbgz%c-A9Gl?m8mE$p&yVRD@kx^lh)r(x?^W%_>2?QQW)-DxA_k7?{0=fA5aSM%XB$br%Gf;8Or(Kdey-kH0_-H&<} zZh1Cd0PmRn%z-l<3l0F&u4Wn(44LlK_ozwi!`k*GL`adO1jGH3WChPO8F+{3FnxEM zHnrSY^!MM<=hE}~q5~Qv2B1x1lNPkco#cn}PJj+L1)B?-ZtUqPTzMMOA5gC%vcUVxzgybjdAz^#i=->t9P>8)zA7`P4oehJq}? zs-M?=m(0qpy#K5B$(s&kAA2sh!UZw8nBd5ngeHh)b}@r(7Mh_!SzIh|Su7$8Tp#R% z1A#IO;^RmR;^26!16nZ#=HT3!o(|<;8u~gB``X=s-R-auBi9HSVlJjP+^BEpEpWO6 z0;-Xk3u&WhU;;`2E)y*C-8dGuH*g8%$KX>LOvm! abrJ>Tg=G{Bok-G-W$(b5=YpIa+5ZJTNoU^x diff --git a/.clangd/index/memory_pool.h.BE12C74670B7FCD5.idx b/.clangd/index/memory_pool.h.BE12C74670B7FCD5.idx deleted file mode 100644 index 06501328d1eadb0a691e413a4dacaebc844ce413..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2426 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 XefB!@ODXs%AxR!zfD_;#sZjhk!V_Q* diff --git a/.clangd/index/rdma_buffer.h.C3DF879253C4FDCB.idx b/.clangd/index/rdma_buffer.h.C3DF879253C4FDCB.idx deleted file mode 100644 index 5ee3891e3665d3e5f02ac9d9ca7a321826368ee6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2490 zcmYLL4NwzT9^X7R*=$HQn=dp8Bq1b(U=$D<9khU!gC>L+Ob#Ty>!6579g!*4~M>%UP78OOjde&B3zvuw!jkR>3w*Ou3ZFVNVc`v{B zfA9bPAHUtjo}!{InjoHA?pa$`vt}tt5QH3ljcXcS`i+PnK93`awe5R@rS+#R@;ett zayxEZz20d$S~3yGiX0z)RxC*!ZThm_+!i|D;i-ModQbm#_L1I^wQF0hMNe+~B-gW2 zt2$K`CmxL4mh}$z{XM03ea@lp+F$<~nGej_Gk)ub<(WO{k~6)E`~8>cdcIZllqdP z^^Cmp+Sc&oI`OIJh7LRvI&(B|=ln?1e}1VOKNp@AW-KeV99|-6yOBAsq@&u|xhd!w zZprS+Z_+HDMKrFfTeb`$Np8L=|9SRFf)_~4k*Vh^N1ioNje3brg-ev|Ffe$Og1|9_7 ze`NIi@GAEL7FqBO7SIcf0$;Iab@|~_r*K?!O_L-D6@<7QyxSd5w z*uQ!7JcYowi2E)s>YkXvAU)VuBv#mX5ct=Q=kI<+(zZGQiGVkeCWZ%r-?BFvJ@kRP zoki2|{`2YivHf=q?LToXJ)mch73>4LfRzWqzHOsEb>`8ArwkGU@3DGj@F4K*Wu1vX zZJsuVMOwUnAzc`&kj~VzTlQZ(&@<(qp42lG2ZH^F{>jBf3znQ_kpbH;q!-5Wb1bhX z>B5P&ViqAdKTDNme$Cg^^=74?%tsfcILx4ju&l zq4z-h$-M8rWsnZ|5^IT*2Z4WbW8;(h=xs5J(y)JdbYAR!lTWLe@B6m~ry(cUXOJ0U z9t8V_niY-HNP<8wr{5HBCwGWQD&3Oq#EmCHe8b@^i9{5a9oat~o}d5L)4Q-ZO_8SC z*?N9S-_^5rL{jB+xsjwQ=n5lF9PGel4d*G2qzqDnilkgpmkRN@ioE|qX{GOlf5C1} zxzm6%24``Z!YvL{uC zafb5U#q{ok;_r8X4|c6xM^dTUR6vK;p~Er&-f|j%s{*E6HQ*uuAGqWn-jn#)`>L## zB&mQkkU~;r)-u4LEodjHkS%1-dG=G;m+Qh>2}xOumSmDjHYNjR$+B3IDq%|!NXp0h z0R5z2mZSgZ*gjj24X)2c1`7%t&8Z32AGohW>QEzIeQ?pi8m{7Sjlq;FFf2JR<@9FZ zC*9Tt{k9|A{%%9==aT2gGJK=)5CpweZ-V$}O@JQSqxoXxVBgNjKnkqXnRL(eH@2Ls z`(-_!mt?2&i{*XYLoXIYfpxl_g;=;CCY)ehy8G|??))qFU|VOVGYhcP=?AQFt_bU^{B0NW5LQ-y-8zNjx7Ei}&3m$S(J4|=?{b~7EVF;;9?b2Ya zg^+R@i(4NYOO>W75#RWahF?L=)dSgFHX#&^otKYCwpVR{H8!J-i%CkhOqusw562on$e(;(6V@qT)`+=m0C(D@d|>F#fF?a|9{{^ Q3_tOBKoFYPP#PQl3P<8qFiSPCM=Bsyfh__BKm@GLXPj zYMjGzJ_asM24*g9E=e%KzyUFX;Y@M$soein>-ZR$fbyJtoMK=CF8?Lw1%v*>-b-u@ zj6fSW_&KD&1dLC`wH$o(yy@%q5IWU>CqF2692R z!hFKS1oi<7GoL1(`IQSz3hyIy_!(K4IaxS)Vc`MP0tyTh+ap{zDx*Z0SeUt3xVTuD zxmmcmV6FkG0XYTcd>|L(_Fd=xUA}eUeKgQOJ|#X4hN9G*L{Lx`7bO?#gBWZe0s>fJ b3kts~U8FVgt z(Mi;~4b8?jh#A{70gW5JjEqcin|srM+aEI+<6?}mOhsKBb1{?cdxuxNq@VtJzvuZq zzvugWueYSQuyA)G!{o<`o9k=VR}qF`toSsoZ>;+T-!jKA%^ka12O7SWtv_u1prX2~ zcle3Xp|T9we<^V2WbNjKFa0~cwQsa!dvWU2vGeY+_BXh-iGWlJ4BUtKu6p#8VQpIW94yf<@m)!v8+zBt1)ZLD9ppT*X? zPj}8Koip3c32ufJ1VPln@R|g;%)y+k-LA-LJC}mITX2gy;(1%awWA%%%h^esU1V5C zqQjztF^|t@S=Mu)?96k~t)WEDi4iHuDXBUh8c{(QN8084FW5ON)^G|=QAd1@Ri!`P zUzgx*aBv>vL*|e}M?Al!_rxPNMcVJ?oXDq>beoQNep}7i{8jCDPue*f@-D&U))CKt zvPOQlZTr=pB*ujfBV5F$OSs^8AJC?3@GhJ%UHn5zpW49?V|we%-b4`Kct;#%hS?8*6Ht$`f!L4^E44 z`Y93C%X^au8{h*VeTpv0yBFPjWzU7V9AQNwS_v!7lnM-eSWef)^v>JO2fns8nh7h1 z9IG1^LvhVtL>CVga0HkFKa`I+O`XGsucElm>C3>}Dz)CQ zT_UI34K2;^&W0MijFx=74^*?%Y;c*n-0%oA)C>~qopCPXdO`6dj926^#w$M1ulPYl zQNVx_0E0>p%v3VN!C9;L+t%7j^xz^c6qh1W)UY(9M^k2jR0=Mas%}jlI&?HD{)3uMi!%i`{Zhy<8`RL8hJ8)*=AZ{YQ zE{eG{M6FH8CXqYOk>TOD(ye7sj<3gixy~4+=B}*2jjxKz_=zE0mE1( oLVhANO@!un$gv;olfT@SIM-w05{M}=X%cU?FeIL5k|x6c0q722Jpcdz diff --git a/.clangd/index/rdma_context.cpp.A7591533B08A3B05.idx b/.clangd/index/rdma_context.cpp.A7591533B08A3B05.idx deleted file mode 100644 index 475def68bd595c72adf2e7aab35ec8521c2021dd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11958 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 diff --git a/.clangd/index/rdma_context.h.AB3EB1CCA367DFDD.idx b/.clangd/index/rdma_context.h.AB3EB1CCA367DFDD.idx deleted file mode 100644 index 957ba4efcd1113c7b2c85f140b4be6ad50875663..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6456 zcmZ`;d0bT07oT%^;0z2igTosTm|+uT89)(aO;8pQbVN`w10xbqWKkqRRDNoRD7d0v z2&9DuhL}pZlp=|1XklsY8;S^unwUyjzw>wt{S%+*e$Tu2`_4P}-23kP9*Z6q7B*~< zNEAGCTuxe2)<={`B$DEP3$iljBodKm-T;v(r=oJo_4FGyiU-NP7LiNm-ny9Xyyv%& zqodXjYq{27ZsnGKan+K=^16dv@m`q&&yK8kKFGYi;PsKihkNhD-}`HD-i`G7!1Rp0 z-VH|&?>K+y)z>RKetNiP!|?J8t?}(GS!)W49l{+0i>q@VSzTW-Y5PKOA{pV=SwDy*U7F_R%(teFPQJ|zVOr^Nxym?wLbJ{=fNS~ z+a}(wgUQ2d3gVNkycci#NoV%%z?K!7nfBTKZR2xuYHuC?R4(_~bkKc@t99=b4 zK8+3kD>}|L>EWmC%QCI+ANsNK7qca=-6bp6u9@3vaNIC0&cVz6q8?#G9w%9aBER~7dTgeNvn{qpqG(Hpa^*3L>vihid}@^YOYQnYP& zs>@z4#Rm&%?eq?b-(RcOO;7W2^F7#kTwU50bl{2p;%rmvA9DZvJ-6ZB$@wQ+{QVxr z1tmU>&AF0bk)k|UD2t;bSAIA{bLwPTPyVy}sVmzqx$8b~P5k4~qIG4VCl|c#eEaI& zhv@_E2Q8fVI$=!W?A0Tzw(VVZWaND9U7xrKMf5)tOxjOBsO-(LsBQEysZCn2C@rx< zED}){X5Vi7cC=1LJi|ovNl>2z8xBNbW?SN)p)##_<{xHvWF%^=h`s|BcVI9F0>@~M z`9)93&KC+Y4EU zur}#yL=rX*pusrp^QNTQs4EI04P#@Yi-@#{SO`AkU;K9Cy9RBSkthJ4(E$b>Kyx5) zRKutJGd}0j=Q84koF{<4p&SStv&OqIKbss6QIL`RlS+uRgjfm;i|Vi4mbzB#R1kNb z@fk=y1C_wg-1%;rrr^m+1LB9zX#r&m=9UG4V?Bq;`)P$ij*N(rvz$nl6D0=%$M(+f zMV_LVp)w*vP7^4az={KbV{iGj)lD~(0vU0_HM$8lH(>+^0>`0jyv~2yjRpk?=ObQE z#OsMdUGv)XlhCSP(eg%GJL5jg_S`Vt|}epgsl890;6Ig`26_j`oQ%;)0wrpgx1K zupn^8NH#CN_|VZpK^&2jqDWEqHNtp^b!6+gS0VcpM9a@OLz>Y)-w4Au(fL-ZYh`2{ z-oKKlRuTse1RuI=Rrj7xnr_QTBJGfe+Ek&?P7+Kfb_pGnjt{k}#R|L{~n^WchG@e1O_ z&!?0~ONp((`1nxj$0>`6hk?itJ^TgAzi`j7AaG`0a{vCL@ov3FA|vFy1A}+?j<6tb z=K9fleJ&kMlaUbQY$ejI#DarE`E2)I{qohhzEK( z2ZPVSFb)JS3-djEvI9T4A|n*$|UwM~yrlFrn$XP|ytB4~90;h1ZL(?JG`e+%^$9S%Q z_6h`YAaFMH?EJ>cbylr{j6y$|%1o!eMi}4n`oYqt3qR>q5x{XT%1hqY2;&v|Mmlew z6Qfa(iG1eP66so^5g0q-9v8fPd;YS5MDUDqkd%X|!1$^$Ha+b?WSoMG=NTp7S^^^k zMveQJi1%DB$I8fiIFn1DyabLM2%N7+y_@^<=GkZ2w>d|iqotWf7~fksCEE1V?mQXs z$C-42stey{76i_r^!=BtdiNYrkO|1i2W39Ea3FA+7DgtSb|rnOAi>D#2K-rbAaG8& zq_<2p$qtZ_5g6|^&|ZUJ4g^lilktr=?`7D_$Z+Ib1k;Nc4+{e4{678C`&}mj%t#D! z+Cba}5)K5;uTRvPU%dCUD#!pndmBNz5tIVsw}YK}_U{HCkr88NRz#IgY&Z}&*L(ji z+1D6%hF#}iDp*c4jWB*&z3%8`<@aFVkNR|tu#Bd*V9N7h$*@X#8n`raKBL1%=Io1?_#d&fU0Az>p@)4T(eP2kN)^% zLq<_PMqL7u5>WC=2$@>7R!h-tL)^S5+E49=cEFGTw8PcmXh#f*@FJW>qVV0Ox0?5pQ0z}Pg3xb5p%=pMP74J zgi~4+-2>7dX1+aO*8`r+5^7$;FkX0JwphVse&tbF%htzDII$$7Bop&}UVB?+j{F~9 zUI6(8pm`mJH?aE4s%f6hp`Nf9WNZP?EihWW^uoOR<>wppDB6Se7(~%QbP%>WS~rN7 zXt-#sFr)CGkhFn{*JU^XR+OpkN{@f$lM;zuivfQsUX@{r8UTN`gwtfUWw(kC{2j3v znJ2;MB$)8p41KY}j2B`UGAqCwM$HyKlg>(zVGgZ>hfs6|m0?8DnMRodad#NyVVe&| z`GB|vs5O9N8-d!$;2J`$A&5nUDq?Ugq1H0EiBOvuEFn}0gQbKjWpFE@wh|-tro@o! zBY7Se{3S5H#5leJ##dkXF`xT2wR<4C->7lKR4Ilqv!uQ zCti|btPSAX0HX;fNCT_RT+VH(UV_QVQe|m*S&9#3G%2q_ak&}w87iy$)9q>SY$R_6 z&&?1VI;HqV_W7?mu)}4Li^JGSjpLX9aBwnS=Bjc>@ER4Ju>zGBq}ai#QC@vw2P-|J zI@>>RDypB39B3q_|mrMTlRv&yi;HXXMI@_;oEOA;gp3zE{WIIGMyDqE-Cd}oeEBaz3 zE3bEv8SEX5yPlQ2ynvNZbh13zlA=@Psn}+TvrKVy#JSim1MxCIEC6uLHGBa+g=R7#b^ac}a`ud0+ZICTF_kbPeG|Ew5#f%_`VcubUrDp7bgo!^?2Xz=b*k z)EO`;Y;$joa9leF7y2|9ody$LyyA_la^)2-cCd1{&{i+fX4TBk@RoK^w1cL&y7Xqo zl1JCE;~5Nk1~$bj&QCjg`LqLe+yvvBV7(!u(&MMN=(PWQFL*hO4`H)UM%51xfAifC7>UWj;I_K-Q$gGDM^^o+{%bEKJ%o_U=J5trD&b+L~U|CIFJI}LI z@7K%b=!yN}^NY_*bPSf2<~7g4cg*;-_8Ly?4jA8Io|#W1`2?4L6|+E=yc|d0j12#J zeKq;u=Mf6@TnvuIFp|RUWO@$lcv+4Ix>l^klg~}+W`Q4fu^-w2(g3u>#o=g2NF&gm zqMTw&(Xl46*39=@vmNlWUjx=P;7T|#e_*RsPs9>K=1@Y_eGNhmg>A{H4|;e7JDR|{ z32b?lkD;*=|A;0*&p)WW92r|6Y74|3J96ZOYEIRU*r7I4TQ)8p`n~S^#P&R-mUmT!owlX;3;2B#}WG6#L3Y8*1CaP&1lAnD5FO${D z_I#aybhb!1{iu9dWyi?Y|J-n=|17uAkfLK4N);GaF<+luV7v>=&qjG@k5}wK$%XH+ zY8i~+>kDLVhY{N$m~hoa%bo2zGD?n$aOrk{dRR>1JmK?1w zoHxYZUmT0Ro583VR3~bp(v8E!j6^F2t6~_+7a_Pm*y7{2Usl*Wou-xJ(=$h9j>Yph zJZBPLh%kxot3vpy13Q>%gD*1hlC3cKiUPfqK~xze60V-O`zTCzqH{1k~@@SW4TY5geL)y5TfjIY=id;Hw|u*cuVAA81%$6`;oTR8TNvl)jy zlWitr`#IQr&g`+Tz~(E^60Qtkq+UP|gttv8+cG)`mSGqP(gZ8MD8VeS#mIwU`aJ{W zW{j*q2a5Dnno06x7I>ZMcbvcABM0#FrzTkk6U z)LP5LXnZ(s>UwZvFti?YZ=e42=KZ+3!+l|TmVjUH>Oap=$-KthlfsPQtC&{zi%$c{CL3tK*OB5V*f08LnIp5Z`$;m znts!^-?ZyDE&5G$ziHZUn)I84`%PuPY1MC<^_$lHrhUJu5KKZVPA>mt!CUDzT46F! jX{s_aA7o)^rPf&6*xK0-b`atJ5#j$Fcta#o^_%|(ea_a8 diff --git a/.clangd/index/rdma_endpoint.cpp.91B25917C6102BE7.idx b/.clangd/index/rdma_endpoint.cpp.91B25917C6102BE7.idx deleted file mode 100644 index bf76a56aeb894432125f04a0b1feabf798f59c9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6954 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 diff --git a/.clangd/index/rdma_endpoint.h.38575B1A8A5DD704.idx b/.clangd/index/rdma_endpoint.h.38575B1A8A5DD704.idx deleted file mode 100644 index a0db104dc85e61f8b2c5814936359021d4563163..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6796 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 diff --git a/.clangd/index/rdma_scheduler.cpp.8797BF9AAD52AD88.idx b/.clangd/index/rdma_scheduler.cpp.8797BF9AAD52AD88.idx deleted file mode 100644 index 2a2defb1199b0dfc6f168062d7ffcd66c3433e62..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1970 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 M#ZdlYsCY5_KmW{(R{#J2 diff --git a/.clangd/index/recv_test.cpp.FE47085B653922D7.idx b/.clangd/index/recv_test.cpp.FE47085B653922D7.idx deleted file mode 100644 index d5e58da0920f28a7573de3bd647f25b589e4024b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2438 zcmZuy3p7-D82`KO;EuV&jK?s-i18TAWQ>-m$cfTpYs$$x5z-pPMA=e03GHFESv^Fy zHpwDZLUwyfNl8)>%Ics^5y`Q1%-R1P89CkmIrlr?`QG2}`~LqcUg_bn9|ORB>&iWn zuw7eF000es(p|BUc}f77LBXEfJaKn(2S>Xv|3%i>P2&|0@1*Xp;x|3jAM>v$ynskv zShl(S&@Y?Job+~|vdb!Py}FaGIr24gYAe|pR9O8Edqt53gdbu|TDe+=u>5fzUip?SB+2ux!ftQ=HqYwLg zPOM?qcV|WB^6Qp&m6{3O4s`z5xqNi%gu_djPu}iLF@LYEv-HakRbBJ@PJ_vjki~)V zf8O<{WF+DlZ9H~aTvubZxcW(J?cxce=7^6$DmCLlL)RSgEhNU7FKe7K$|O4?_^qKy zsWPRr>O~a2ps+%dmktU1)$3%c-rKHw7IhcP1e2zPxlW~9&WHcevv#2F9;@vi`|>9> zT`tN_0_Q_Z-JTUN^lzy1VzYIsJ|{#D+Z`zGc6&SYx%Hg6x4FZupuFMN;!nJkp!&yB z^B*r&UXzyGt1|kG$3F932BZm+5I}+B8b9uzcy%6v0~!Z#X@G{y2|$T)!ecyn>t1xg z-W(k0K)oi=#N|Yxx7Ln%6S8)Q!bUWv9j1*GdfR_}_D`znmm!GBj2;I#uF%_!9gm(8 zu4=`>0@C+w1T7e zwm=Q3-Z`S7)~X@+6b|f3`*&%#Yi&vPSI%yBjrrmzvsnf@4nK~v;kowz6T zaN}MZHbUo28*u4>KHI;q{+P92-c&XlA?^-w=Hl<0DhSX!rF#Jfny`N&w45m7`#w(X z9et?9#DM_TM}(dm-(Tc^SGS&3jRQMUfB)R;sb{T&EEzzBkX;X#+Ho1OeucgFs3Qk-^T3_@;LVK{YmMa@>;Pjb0zr@(2Q~j?t z9>aka>HH39yrVAJBP+&%0gV6c{-(L)-ZrJphxloe=1;3`$=;TCnSvljGx_JtFRj_? z$_mTuTQpz;@zF-L)#OBh&yfmdQLONO6`b#k|D5^$nwhm+=&{ig2TaoXPAp(qg}q^H z$H6?(`py_+C*Q~mp~3y&e8GokQZfIs@tFDaL$*{LXh8pO?`H*;ArW2Qsm4YaGxZUd zNa6oiz?kQw5{=V1;FIEe%Qn5_XXWN59O#qoPkq$V2i4~;zQ%zrslFjoW#F3q`gZs} z%+ya5smR}5^6=(!WIY=P4ATAiBvJAnG#qlk0lYsX`#n|IkQ7D#$q@&(r1;8~x3!On z#(Z#KNQzHvSh!S#z;TN9ao;F!J*JGxmk5=oLgj!a=M@%~moak@lx@1f8TGlW7T%!rBa z&RkLX-<1(UsAXsjg<3|&Vx3p*xiK}ZBsFA>^*plQxBeN4>ZxOTYH;b@cQ6!($FW1X zCOi{6R&V@~!=neeIgO%RbIcqTX@OZl z%E$PS3NQha{jS5H?{sK^$_zflQ~NbaU*nKdT^p{uthWWz7|=GB?`oSWkM6aD}R=nYo3PB@dV?lps{HHH!F4s8Oj(h$=;08ADb6 E2g)!bng9R* diff --git a/.clangd/index/send_test.cpp.40B395064D1F6AC5.idx b/.clangd/index/send_test.cpp.40B395064D1F6AC5.idx deleted file mode 100644 index c8c0a48fbb2a833af4e1d3d5675ad730f9e99b3d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3134 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 Date: Mon, 20 Oct 2025 23:53:02 +0800 Subject: [PATCH 5/9] description of queue operation logic --- csrc/engine/rdma/rdma_endpoint.h | 111 +++++++++++++++++++++++++------ 1 file changed, 89 insertions(+), 22 deletions(-) diff --git a/csrc/engine/rdma/rdma_endpoint.h b/csrc/engine/rdma/rdma_endpoint.h index 402613b..bcb0153 100644 --- a/csrc/engine/rdma/rdma_endpoint.h +++ b/csrc/engine/rdma/rdma_endpoint.h @@ -38,15 +38,61 @@ typedef struct MetaData { +// 管理收发两端的RDMABuffer +// Send: 接收RDMABuffer构造一个RDMABufferQueueElement并入队SendWaitQueue。 + +// 查询其对应的meta是否准备好,如果准备好则弹出执行RDMA WRITE WITH IMM DATA + +// 弹出的RDMABuffer入队SendCompletionQueue,等待其回调函数执行完成并修改完成标志位 + + +// Recv: 接收RDMABuffer构造一个RDMABufferQueueElement并入队RecvWaitQueue。 + +// 查询其对应的预提交队列是否已有对应的元素,如果有则弹出,没有则循环等待,同时直接执行meta的 RDMA WRITE WITH IMM DATA + +// 弹出的RDMABuffer入队RecvCompletionQueue,等待其回调函数执行完成并修改完成标志位 + + +class RDMABufferQueueElement +{ +private: + + std::shared_ptr rdma_buffer_{nullptr}; + std::function callback_; + OpCode rdma_opcode_; + uint32_t unique_slot_id_; + std::atomic is_finished_; + + +public: + + RDMABufferQueueElement() = delete; + RDMABufferQueueElement(const RDMABufferQueueElement&) = delete; + RDMABufferQueueElement(uint32_t unique_slot_id, OpCode rdma_opcode); + + +} + + + +// 预提交队列 + +// Send: 生成metaRecvPrePostQueue,并预提交一定数量的RDMAPrePostQueueElement,所有元素使用dummy assignmentbatch +// 检查队头标志位,如果为true,则将该元素弹出并送入metaQueue? 同时补充新的RDMAPrePostQueueElement + +// Recv: 生成dataRecvPrePostQueue,并预提交一定数量的RDMAPrePostQueueElement,所有元素使用dummy assignmentbatch +// 检查队头标志位,如果为true,同时修改RDMABuffer中的标志位,并补充新的RDMAPrePostQueueElement + + /** - * RDMAEndPointTask - RDMA endpoint task management + * RDMAPrePostQueueElement - RDMA endpoint task management * * Manages RDMA "pre" (RECV) operations from submission to completion. * * Workflow: - * 1. RDMAEndPointTask enqueued --> initiates meta_recv or data_recv (based on operation type) + * 1. RDMAPrePostQueueElement enqueued --> initiates meta_recv or data_recv (based on operation type) * 2. Poll queue head's is_finished status repeatedly - * 3. When is_finished == true --> pop RDMAEndPointTask from queue and + * 3. When is_finished == true --> pop RDMAPrePostQueueElement from queue and * forward information to meta queue * * Components: @@ -56,22 +102,43 @@ typedef struct MetaData { * 4. callback - Callback function in RDMAContext for handling is_finished status * 5. is_finished - Flag indicating whether the task is completed */ -struct RDMAEndPointTask +class RDMAPrePostQueueElement { - + +private: + + // set the indicator of the task + void setStatus(bool status); + + + void postRecvAssignment(std::shared_ptr rdma_endpoint); + + + + + std::shared_ptr rdma_endpoint_{nullptr}; // Used for submit the assignment_batch_ to RDMAContext + std::shared_ptr rdma_buffer_{nullptr}; // Used for the data pre post recv + std::function callback_; // Store the callback + OpCode rdma_opcode_; + AssignmentBatch assignment_batch_; + std::atomic is_finished_; // The indicator of the if the recv is finished + mutable std::mutex task_mutex_; + +public: + // Delete default and copy constructors to enforce specific construction - RDMAEndPointTask() = delete; - RDMAEndPointTask(const RDMAEndPointTask&) = delete; - + RDMAPrePostQueueElement() = delete; + RDMAPrePostQueueElement(const RDMAPrePostQueueElement&) = delete; + /** * Constructor for RECV operations * @param task_id Unique task identifier * @param rdma_opcode RDMA operation code * @param rdma_endpoint RDMA endpoint for operation submission **/ - RDMAEndPointTask(uint32_t task_id, - OpCode rdma_opcode = OpCode::RECV, - std::shared_ptr rdma_endpoint = nullptr); + RDMAPrePostQueueElement(uint32_t task_id, + OpCode rdma_opcode = OpCode::RECV, + std::shared_ptr rdma_endpoint = nullptr); /** * Constructor for SEND operations @@ -80,24 +147,18 @@ struct RDMAEndPointTask * @param rdma_endpoint RDMA endpoint for operation submission * @param rdma_buffer RDMA buffer for data transmission */ - RDMAEndPointTask(uint32_t task_id, - OpCode rdma_opcode = OpCode::SEND, - std::shared_ptr rdma_endpoint = nullptr, - std::shared_ptr rdma_buffer = nullptr); + RDMAPrePostQueueElement(uint32_t task_id, + OpCode rdma_opcode = OpCode::SEND, + std::shared_ptr rdma_endpoint = nullptr, + std::shared_ptr rdma_buffer = nullptr); ~RDMAEndPointTask(); +} - std::shared_ptr rdma_endpoint_{nullptr}; // Used for submit the assignment_batch_ to RDMAContext - std::shared_ptr rdma_buffer_{nullptr}; // Used for the data pre post recv - std::function callback_; // Store the callback - AssignmentBatch assignment_batch_; - std::atomic is_finished_; // The indicator of the if the recv is finished -}; - @@ -197,6 +258,8 @@ class ProxyQueue + + class RDMASendRecvTaskPool { public: @@ -291,6 +354,10 @@ class RDMAEndpoint: public std::enable_shared_from_this { void asyncSendData(std::shared_ptr task); + ProxyQueue meta_recv_queue_; + + + std::queue meta_recv_queue; std::queue meta_queue; std::queue send_queue; From 4379982c9336ef91045b6ad0810fbb438cc2a376 Mon Sep 17 00:00:00 2001 From: HaoLiuuu Date: Tue, 21 Oct 2025 00:03:26 +0800 Subject: [PATCH 6/9] add some additional tips --- csrc/engine/rdma/rdma_endpoint.h | 38 ++++++++++++++++---------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/csrc/engine/rdma/rdma_endpoint.h b/csrc/engine/rdma/rdma_endpoint.h index bcb0153..b5514f6 100644 --- a/csrc/engine/rdma/rdma_endpoint.h +++ b/csrc/engine/rdma/rdma_endpoint.h @@ -70,6 +70,7 @@ class RDMABufferQueueElement RDMABufferQueueElement(const RDMABufferQueueElement&) = delete; RDMABufferQueueElement(uint32_t unique_slot_id, OpCode rdma_opcode); + bool isFinished(); } @@ -110,11 +111,7 @@ class RDMAPrePostQueueElement // set the indicator of the task void setStatus(bool status); - - void postRecvAssignment(std::shared_ptr rdma_endpoint); - - - + void postRecvAssignment(); std::shared_ptr rdma_endpoint_{nullptr}; // Used for submit the assignment_batch_ to RDMAContext std::shared_ptr rdma_buffer_{nullptr}; // Used for the data pre post recv @@ -149,26 +146,13 @@ class RDMAPrePostQueueElement */ RDMAPrePostQueueElement(uint32_t task_id, OpCode rdma_opcode = OpCode::SEND, - std::shared_ptr rdma_endpoint = nullptr, - std::shared_ptr rdma_buffer = nullptr); + std::shared_ptr rdma_endpoint = nullptr); ~RDMAEndPointTask(); } - - - - - - - - - - - - /** * ProxyQueue - The Queue template which is used in the proxy * @@ -367,8 +351,24 @@ class RDMAEndpoint: public std::enable_shared_from_this { + ProxyQueue send_wait_queue_; + ProxyQueue send_completion_queue_; + + ProxyQueue recv_wait_queue_; + ProxyQueue recv_completion_queue_; + + ProxyQueue meta_recv_pre_post_queue_; + ProxyQueue data_recv_pre_post_queue_; + std::thread send_prepost_proxy_; + std::thread send_buffer_proxy_; + std::thread send_completion_proxy_; + std::thread recv_prepost_proxy_; + std::thread recv_buffer_proxy_; + std::thread recv_completion_proxy_; + + //meta 和 data 都用LRU实现 std::shared_ptr rdma_task_pool_; From 3049bed146d53b4c221057855f000bcbb04bc5c2 Mon Sep 17 00:00:00 2001 From: HaoLiuuu Date: Sun, 26 Oct 2025 14:55:20 +0800 Subject: [PATCH 7/9] renew the queue element --- csrc/engine/rdma/rdma_endpoint.cpp | 908 ++++++++++++++++------------- csrc/engine/rdma/rdma_endpoint.h | 372 +++++------- 2 files changed, 646 insertions(+), 634 deletions(-) diff --git a/csrc/engine/rdma/rdma_endpoint.cpp b/csrc/engine/rdma/rdma_endpoint.cpp index 8acabbf..f0a89c6 100644 --- a/csrc/engine/rdma/rdma_endpoint.cpp +++ b/csrc/engine/rdma/rdma_endpoint.cpp @@ -14,412 +14,510 @@ namespace slime { -RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, uint32_t task_id) -{ - rdma_endpoint_ = rdma_endpoint; - task_id_ = task_id; - makeDummyAssignmentBatch(); -} - -RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rmda_opcode, uint32_t task_id): - rdma_endpoint_(rdma_endpoint), rdma_operation_(rmda_opcode), task_id_(task_id) -{ - unique_slot_id_ = -1; - rdma_buffer_ = nullptr; - makeDummyAssignmentBatch(); -} - -RDMASendRecvTask::~RDMASendRecvTask() -{ - SLIME_LOG_INFO("Destroy the RDMASendRecvTask: ", task_id_); -} - -// 生成meta Assignment 和 data Assignment -int RDMASendRecvTask::makeAssignmentBatch() -{ - size_t meta_buffer_idx = unique_slot_id_ % MAX_META_BUFFER_SIZE; - if (rdma_operation_ == OpCode::SEND) { - AssignmentBatch batch; - std::string prefix = "DATA_SEND_"; - std::cout << "rdma_buffer_->batch_size()" << rdma_buffer_->batch_size() << std::endl; - for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { - batch.push_back(Assignment(prefix + std::to_string(unique_slot_id_) + "_" + std::to_string(i), - 0, - 0, - rdma_buffer_->view_batch()[i].length)); - } - data_assignment_batch_ = batch; - } - if (rdma_operation_ == OpCode::RECV) { - meta_assignment_batch_ = AssignmentBatch{ - Assignment("meta_buffer", - meta_buffer_idx * sizeof(meta_data_t), - MAX_META_BUFFER_SIZE * sizeof(meta_data_t) + meta_buffer_idx * sizeof(meta_data_t), - sizeof(meta_data_t))}; - } - return 0; -} - -int RDMASendRecvTask::makeDummyAssignmentBatch() -{ - - dum_meta_assignment_batch_ = AssignmentBatch{Assignment("dum_meta_buffer_", 0, 0, 16 * sizeof(uint32_t))}; - dum_data_assignment_batch_ = AssignmentBatch{Assignment("dum_data_buffer_", 0, 0, 16 * sizeof(uint32_t))}; - - return 0; -} - -int RDMASendRecvTask::makeMetaMR() -{ - auto& meta_buffer = rdma_endpoint_->metaBuffer(); - - std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; - for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { - auto mr = rdma_endpoint_->dataCtx()->get_mr(prefix + std::to_string(unique_slot_id_) + "_" + std::to_string(i)); - if (rdma_operation_ == OpCode::RECV) { - meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i] = - reinterpret_cast(mr->addr); - meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; - meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i] = mr->length; - meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_slot = unique_slot_id_; - } - } - - return 0; -} - -int RDMASendRecvTask::makeDataMR() -{ - - std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; - std::string MR_KEY = prefix + std::to_string(unique_slot_id_) + "_"; - auto mr = rdma_endpoint_->dataCtx()->get_mr(MR_KEY + std::to_string(0)); - if (mr != nullptr) { - SLIME_LOG_DEBUG("The DATA MR has been REGISTERED! The SLOT_ID is: ", unique_slot_id_); - } - else { - auto view_batch = rdma_buffer_->view_batch(); - for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { - std::cout << view_batch[i].data_ptr << std::endl; - rdma_endpoint_->dataCtx()->register_memory_region( - MR_KEY + std::to_string(i), view_batch[i].data_ptr, view_batch[i].length); - } - } - - return 0; -} -int RDMASendRecvTask::makeRemoteDataMR() -{ - if (!rdma_buffer_ || !rdma_endpoint_) { - SLIME_LOG_ERROR("Null pointer detected: rdma_buffer_=" << rdma_buffer_ - << ", rdma_endpoint_=" << rdma_endpoint_); - return -1; - } - - std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; - std::string MR_KEY = prefix + std::to_string(unique_slot_id_) + "_"; - auto mr = rdma_endpoint_->dataCtx()->get_remote_mr(MR_KEY + std::to_string(0)); - - if (mr.addr == 0) { - auto& meta_buffer = rdma_endpoint_->metaBuffer(); - for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { - uint64_t addr = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i]; - uint32_t size = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i]; - uint32_t rkey = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i]; - rdma_endpoint_->dataCtx()->register_remote_memory_region(MR_KEY + std::to_string(i), addr, size, rkey); - } - } - - else { - SLIME_LOG_DEBUG("The REMOTE DATA MR has been REGISTERED! The SLOT_ID is: ", unique_slot_id_); - } - - return 0; -} - -void RDMASendRecvTask::configurationTask(std::shared_ptr rdma_buffer, - uint32_t unique_slot_id, - OpCode rmda_opcode) -{ - rdma_buffer_ = rdma_buffer; - unique_slot_id_ = unique_slot_id; - rdma_operation_ = rmda_opcode; - makeAssignmentBatch(); - makeDataMR(); - makeMetaMR(); -} - -RDMASendRecvTaskPool::RDMASendRecvTaskPool(std::shared_ptr rdma_endpoint) -{ - rdma_endpoint_ = rdma_endpoint; - pool_size_ = 128; - - for (size_t i = 0; i < pool_size_; ++i) { - auto task = std::make_shared(rdma_endpoint_, i); - rdma_send_task_pool_.push_back(task); - // rdma_endpoint_->postSendTask(task); - rdma_send_task_in_use_.push_back(false); - } - - for (size_t i = 0; i < pool_size_; ++i) { - rdma_recv_task_pool_.push_back(std::make_shared(rdma_endpoint_, i)); - rdma_recv_task_in_use_.push_back(false); - } -} - -RDMASendRecvTaskPool::~RDMASendRecvTaskPool() -{ - SLIME_LOG_INFO("Destroy the RDMASendRecvTaskPool"); -} - -std::shared_ptr RDMASendRecvTaskPool::fetchSendRecvTask(std::shared_ptr rdma_buffer, - uint32_t unique_slot_id, - OpCode rdma_operation) -{ - if (rdma_operation == OpCode::SEND) { - - std::unique_lock lock(pool_mutex_); - - task_available_cv_.wait(lock, [this]() { - return std::find(this->rdma_send_task_in_use_.begin(), this->rdma_send_task_in_use_.end(), false) - != this->rdma_send_task_in_use_.end(); - }); - - for (size_t i = 0; i < rdma_send_task_in_use_.size(); ++i) { - if (!rdma_send_task_in_use_[i]) { - rdma_send_task_in_use_[i] = true; - auto task = rdma_send_task_pool_[i]; - task->configurationTask(rdma_buffer, unique_slot_id, rdma_operation); - rdma_endpoint_->postSendTask(task); - return task; - } - } - } - else if (rdma_operation == OpCode::RECV) { - - std::unique_lock lock(pool_mutex_); - - task_available_cv_.wait(lock, [this]() { - return std::find(this->rdma_recv_task_in_use_.begin(), this->rdma_recv_task_in_use_.end(), false) - != this->rdma_recv_task_in_use_.end(); - }); - - for (size_t i = 0; i < rdma_recv_task_in_use_.size(); ++i) { - if (!rdma_recv_task_in_use_[i]) { - rdma_recv_task_in_use_[i] = true; - auto task = rdma_recv_task_pool_[i]; - task->configurationTask(rdma_buffer, unique_slot_id, rdma_operation); - return task; - } - } - } - else { - SLIME_LOG_ERROR("Unsupported RDMA operation in fetchSendRecvTask!"); - return nullptr; - } - - return nullptr; -} - -int RDMASendRecvTaskPool::releaseSendRecvTask(std::shared_ptr rdma_task) -{ - - if (rdma_task->rdma_operation_ == OpCode::SEND) { - std::unique_lock lock(pool_mutex_); - rdma_send_task_in_use_[rdma_task->task_id_] = false; - rdma_endpoint_->postSendTask(rdma_task); - task_available_cv_.notify_one(); - return 0; - } - else if (rdma_task->rdma_operation_ == OpCode::RECV) { - std::unique_lock lock(pool_mutex_); - rdma_recv_task_in_use_[rdma_task->task_id_] = false; - task_available_cv_.notify_one(); - return 0; - } - else { - SLIME_LOG_ERROR("Unsupported RDMA operation in releaseSendRecvTask!"); - return -1; - } -} - -RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, uint8_t ib_port, const std::string& link_type, size_t qp_num) -{ - SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); - - data_ctx_ = std::make_shared(qp_num, 0); - meta_ctx_ = std::make_shared(1, 0); - - data_ctx_->init(dev_name, ib_port, link_type); - meta_ctx_->init(dev_name, ib_port, link_type); - - data_ctx_qp_num_ = data_ctx_->qp_list_len_; - meta_ctx_qp_num_ = meta_ctx_->qp_list_len_; - SLIME_LOG_INFO("The QP number of data plane is: ", data_ctx_qp_num_); - SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); - SLIME_LOG_INFO("RDMA Endpoint Init Success and Launch the RDMA Endpoint Task Threads..."); - - const size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; - meta_buffer_.reserve(max_meta_buffer_size); - memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); - meta_ctx_->register_memory_region( - "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); - - std::cout << meta_buffer_[0].mr_addr[0] << std::endl; - - dum_meta_buffer_.reserve(16); - memset(dum_meta_buffer_.data(), 0, dum_meta_buffer_.size() * sizeof(uint32_t)); - meta_ctx_->register_memory_region("dum_meta_buffer_", - reinterpret_cast(dum_meta_buffer_.data()), - sizeof(uint32_t) * dum_meta_buffer_.size()); - - dum_data_buffer_.reserve(16); - memset(dum_data_buffer_.data(), 1048, dum_data_buffer_.size() * sizeof(uint32_t)); - data_ctx_->register_memory_region("dum_data_buffer_", - reinterpret_cast(dum_data_buffer_.data()), - sizeof(uint32_t) * dum_data_buffer_.size()); -} - -RDMAEndpoint::~RDMAEndpoint() -{ - { - std::unique_lock lock(rdma_tasks_mutex_); - RDMA_tasks_threads_running_ = false; - } - - rdma_tasks_cv_.notify_all(); - - if (rdma_tasks_threads_.joinable()) - rdma_tasks_threads_.join(); -} - -void RDMAEndpoint::connect(const json& data_ctx_info, const json& meta_ctx_info) -{ - std::cout << "DDD" << std::endl; - data_ctx_->connect(data_ctx_info); - meta_ctx_->connect(meta_ctx_info); - std::cout << "DDDDDD" << std::endl; - data_ctx_->launch_future(); - meta_ctx_->launch_future(); - std::cout << "DDDDDDDDDDDD" << std::endl; - RDMA_tasks_threads_running_ = true; - rdma_tasks_threads_ = std::thread([this] { this->mainQueueThread(std::chrono::milliseconds(100)); }); - std::cout << "DDDDDDDDDDDDDDDDDD" << std::endl; - rdma_task_pool_ = std::make_shared(shared_from_this()); - std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; -} - -void RDMAEndpoint::addSendTask(std::shared_ptr rdma_buffer) -{ - std::unique_lock lock(rdma_tasks_mutex_); - ++unique_SEND_SLOT_ID_; - auto task = rdma_task_pool_->fetchSendRecvTask(rdma_buffer, unique_SEND_SLOT_ID_, OpCode::SEND); - send_task_[unique_SEND_SLOT_ID_] = task; - rdma_tasks_queue_.push(task); - rdma_tasks_cv_.notify_one(); -} - -void RDMAEndpoint::addRecvTask(std::shared_ptr rdma_buffer) -{ - std::unique_lock lock(rdma_tasks_mutex_); - ++unique_RECV_SLOT_ID_; - auto task = rdma_task_pool_->fetchSendRecvTask(rdma_buffer, unique_RECV_SLOT_ID_, OpCode::RECV); - recv_task_[unique_RECV_SLOT_ID_] = task; - rdma_tasks_queue_.push(task); - rdma_tasks_cv_.notify_one(); -} - -void RDMAEndpoint::mainQueueThread(std::chrono::milliseconds timeout) -{ - while (RDMA_tasks_threads_running_) { - std::shared_ptr task; - - { - std::unique_lock lock(rdma_tasks_mutex_); - - bool has_task = rdma_tasks_cv_.wait_for( - lock, timeout, [this] { return !rdma_tasks_queue_.empty() || !RDMA_tasks_threads_running_; }); - - if (!RDMA_tasks_threads_running_) - break; - if (!has_task) - continue; - - task = std::move(rdma_tasks_queue_.front()); - rdma_tasks_queue_.pop(); - } - - if (task) { - switch (task->rdma_operation_) { - case OpCode::SEND: - asyncSendData(task); - break; - case OpCode::RECV: - asyncRecvData(task); - break; - default: - SLIME_LOG_ERROR("Unknown OpCode in WaitandPopTask"); - break; - } - } - } -} - -void RDMAEndpoint::postSendTask(std::shared_ptr task) -{ - auto data_callback = [this, task](int status, int _) mutable { - this->rdma_task_pool_->releaseSendRecvTask(task); - task->rdma_buffer_->send_done_callback(); - }; - - auto meta_callback = [this, task, data_callback](int status, int slot_id) mutable { - task->makeRemoteDataMR(); - AssignmentBatch data_assign_batch = task->data_assignment_batch_; - auto data_atx = this->data_ctx_->submit(OpCode::WRITE_WITH_IMM, - data_assign_batch, - data_callback, - RDMAContext::UNDEFINED_QPI, - task->unique_slot_id_); - }; - - { - AssignmentBatch meta_assignment_batch = task->dum_meta_assignment_batch_; - meta_ctx_->submit(OpCode::RECV, meta_assignment_batch, meta_callback); - } -} - -void RDMAEndpoint::asyncSendData(std::shared_ptr task) -{ - std::cout << "The send task has been pre post" << std::endl; -} - -void RDMAEndpoint::asyncRecvData(std::shared_ptr task) -{ - auto data_callback = [this, task](int status, int slot_id) mutable { - this->rdma_task_pool_->releaseSendRecvTask(task); - task->rdma_buffer_->recv_done_callback(); - }; - - auto meta_callback = [this, task](int status, int slot_id) mutable { - std::cout << "The recv task has been pre post" << std::endl; - }; - - { - auto data_atx = this->data_ctx_->submit(OpCode::RECV, - task->dum_data_assignment_batch_, - data_callback, - RDMAContext::UNDEFINED_QPI, - task->unique_slot_id_); - - AssignmentBatch meta_assignment_batch = task->meta_assignment_batch_; - meta_ctx_->submit(OpCode::WRITE_WITH_IMM, - meta_assignment_batch, - meta_callback, - RDMAContext::UNDEFINED_QPI, - task->unique_slot_id_); - } -} + +// // RDMAPrePostQueueElement构造函数 +// // 对于发送端,RDMAPrePostQueueElement表示一个预提交的meta recv assig + + +// RDMAPrePostQueueElement::RDMAPrePostQueueElement(uint32_t task_id, OpCode rdma_opcode, std::shared_ptr rdma_endpoint) +// { +// task_id_ = task_id; +// rdma_opcode_ = rdma_opcode; + +// if (rdma_opcode_ == OpCode::SEND) +// { +// assignment_batch_ = AssignmentBatch{Assignment("dum_meta_buffer_", 0, 0, 16 * sizeof(uint32_t))}; + +// auto callback = [this] (int status, int slot_id) mutable +// { +// this->is_finished_ = true; +// } +// }; +// } +// else if (rdma_opcode_ == OpCode::RECV) +// { +// assignment_batch_ = AssignmentBatch{Assignment("dum_data_buffer_", 0, 0, 16 * sizeof(uint32_t))}; +// auto callback = [this] (int status, int slot_id) mutable +// { +// this->is_finished_ = true; +// } +// } + + + + +// } + +// RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, uint32_t task_id) +// { +// rdma_endpoint_ = rdma_endpoint; +// task_id_ = task_id; +// makeDummyAssignmentBatch(); +// } + +// RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rmda_opcode, uint32_t task_id): +// rdma_endpoint_(rdma_endpoint), rdma_operation_(rmda_opcode), task_id_(task_id) +// { +// unique_slot_id_ = -1; +// rdma_buffer_ = nullptr; +// makeDummyAssignmentBatch(); +// } + +// RDMASendRecvTask::~RDMASendRecvTask() +// { +// SLIME_LOG_INFO("Destroy the RDMASendRecvTask: ", task_id_); +// } + +// // 生成meta Assignment 和 data Assignment +// int RDMASendRecvTask::makeAssignmentBatch() +// { +// size_t meta_buffer_idx = unique_slot_id_ % MAX_META_BUFFER_SIZE; +// if (rdma_operation_ == OpCode::SEND) { +// AssignmentBatch batch; +// std::string prefix = "DATA_SEND_"; +// std::cout << "rdma_buffer_->batch_size()" << rdma_buffer_->batch_size() << std::endl; +// for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { +// batch.push_back(Assignment(prefix + std::to_string(unique_slot_id_) + "_" + std::to_string(i), +// 0, +// 0, +// rdma_buffer_->view_batch()[i].length)); +// } +// data_assignment_batch_ = batch; +// } +// if (rdma_operation_ == OpCode::RECV) { +// meta_assignment_batch_ = AssignmentBatch{ +// Assignment("meta_buffer", +// meta_buffer_idx * sizeof(meta_data_t), +// MAX_META_BUFFER_SIZE * sizeof(meta_data_t) + meta_buffer_idx * sizeof(meta_data_t), +// sizeof(meta_data_t))}; +// } +// return 0; +// } + +// int RDMASendRecvTask::makeDummyAssignmentBatch() +// { + +// dum_meta_assignment_batch_ = AssignmentBatch{Assignment("dum_meta_buffer_", 0, 0, 16 * sizeof(uint32_t))}; +// dum_data_assignment_batch_ = AssignmentBatch{Assignment("dum_data_buffer_", 0, 0, 16 * sizeof(uint32_t))}; + +// return 0; +// } + +// int RDMASendRecvTask::makeMetaMR() +// { +// auto& meta_buffer = rdma_endpoint_->metaBuffer(); + +// std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; +// for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { +// auto mr = rdma_endpoint_->dataCtx()->get_mr(prefix + std::to_string(unique_slot_id_) + "_" + std::to_string(i)); +// if (rdma_operation_ == OpCode::RECV) { +// meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i] = +// reinterpret_cast(mr->addr); +// meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; +// meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i] = mr->length; +// meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_slot = unique_slot_id_; +// } +// } + +// return 0; +// } + +// int RDMASendRecvTask::makeDataMR() +// { + +// std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; +// std::string MR_KEY = prefix + std::to_string(unique_slot_id_) + "_"; +// auto mr = rdma_endpoint_->dataCtx()->get_mr(MR_KEY + std::to_string(0)); +// if (mr != nullptr) { +// SLIME_LOG_DEBUG("The DATA MR has been REGISTERED! The SLOT_ID is: ", unique_slot_id_); +// } +// else { +// auto view_batch = rdma_buffer_->view_batch(); +// for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { +// std::cout << view_batch[i].data_ptr << std::endl; +// rdma_endpoint_->dataCtx()->register_memory_region( +// MR_KEY + std::to_string(i), view_batch[i].data_ptr, view_batch[i].length); +// } +// } + +// return 0; +// } +// int RDMASendRecvTask::makeRemoteDataMR() +// { +// if (!rdma_buffer_ || !rdma_endpoint_) { +// SLIME_LOG_ERROR("Null pointer detected: rdma_buffer_=" << rdma_buffer_ +// << ", rdma_endpoint_=" << rdma_endpoint_); +// return -1; +// } + +// std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; +// std::string MR_KEY = prefix + std::to_string(unique_slot_id_) + "_"; +// auto mr = rdma_endpoint_->dataCtx()->get_remote_mr(MR_KEY + std::to_string(0)); + +// if (mr.addr == 0) { +// auto& meta_buffer = rdma_endpoint_->metaBuffer(); +// for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { +// uint64_t addr = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i]; +// uint32_t size = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i]; +// uint32_t rkey = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i]; +// rdma_endpoint_->dataCtx()->register_remote_memory_region(MR_KEY + std::to_string(i), addr, size, rkey); +// } +// } + +// else { +// SLIME_LOG_DEBUG("The REMOTE DATA MR has been REGISTERED! The SLOT_ID is: ", unique_slot_id_); +// } + +// return 0; +// } + +// void RDMASendRecvTask::configurationTask(std::shared_ptr rdma_buffer, +// uint32_t unique_slot_id, +// OpCode rmda_opcode) +// { +// rdma_buffer_ = rdma_buffer; +// unique_slot_id_ = unique_slot_id; +// rdma_operation_ = rmda_opcode; +// makeAssignmentBatch(); +// makeDataMR(); +// makeMetaMR(); +// } + +// RDMASendRecvTaskPool::RDMASendRecvTaskPool(std::shared_ptr rdma_endpoint) +// { +// rdma_endpoint_ = rdma_endpoint; +// pool_size_ = 128; + +// for (size_t i = 0; i < pool_size_; ++i) { +// auto task = std::make_shared(rdma_endpoint_, i); +// rdma_send_task_pool_.push_back(task); +// // rdma_endpoint_->postSendTask(task); +// rdma_send_task_in_use_.push_back(false); +// } + +// for (size_t i = 0; i < pool_size_; ++i) { +// rdma_recv_task_pool_.push_back(std::make_shared(rdma_endpoint_, i)); +// rdma_recv_task_in_use_.push_back(false); +// } +// } + +// RDMASendRecvTaskPool::~RDMASendRecvTaskPool() +// { +// SLIME_LOG_INFO("Destroy the RDMASendRecvTaskPool"); +// } + +// std::shared_ptr RDMASendRecvTaskPool::fetchSendRecvTask(std::shared_ptr rdma_buffer, +// uint32_t unique_slot_id, +// OpCode rdma_operation) +// { +// if (rdma_operation == OpCode::SEND) { + +// std::unique_lock lock(pool_mutex_); + +// task_available_cv_.wait(lock, [this]() { +// return std::find(this->rdma_send_task_in_use_.begin(), this->rdma_send_task_in_use_.end(), false) +// != this->rdma_send_task_in_use_.end(); +// }); + +// for (size_t i = 0; i < rdma_send_task_in_use_.size(); ++i) { +// if (!rdma_send_task_in_use_[i]) { +// rdma_send_task_in_use_[i] = true; +// auto task = rdma_send_task_pool_[i]; +// task->configurationTask(rdma_buffer, unique_slot_id, rdma_operation); +// rdma_endpoint_->postSendTask(task); +// return task; +// } +// } +// } +// else if (rdma_operation == OpCode::RECV) { + +// std::unique_lock lock(pool_mutex_); + +// task_available_cv_.wait(lock, [this]() { +// return std::find(this->rdma_recv_task_in_use_.begin(), this->rdma_recv_task_in_use_.end(), false) +// != this->rdma_recv_task_in_use_.end(); +// }); + +// for (size_t i = 0; i < rdma_recv_task_in_use_.size(); ++i) { +// if (!rdma_recv_task_in_use_[i]) { +// rdma_recv_task_in_use_[i] = true; +// auto task = rdma_recv_task_pool_[i]; +// task->configurationTask(rdma_buffer, unique_slot_id, rdma_operation); +// return task; +// } +// } +// } +// else { +// SLIME_LOG_ERROR("Unsupported RDMA operation in fetchSendRecvTask!"); +// return nullptr; +// } + +// return nullptr; +// } + +// int RDMASendRecvTaskPool::releaseSendRecvTask(std::shared_ptr rdma_task) +// { + +// if (rdma_task->rdma_operation_ == OpCode::SEND) { +// std::unique_lock lock(pool_mutex_); +// rdma_send_task_in_use_[rdma_task->task_id_] = false; +// rdma_endpoint_->postSendTask(rdma_task); +// task_available_cv_.notify_one(); +// return 0; +// } +// else if (rdma_task->rdma_operation_ == OpCode::RECV) { +// std::unique_lock lock(pool_mutex_); +// rdma_recv_task_in_use_[rdma_task->task_id_] = false; +// task_available_cv_.notify_one(); +// return 0; +// } +// else { +// SLIME_LOG_ERROR("Unsupported RDMA operation in releaseSendRecvTask!"); +// return -1; +// } +// } + +// RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, uint8_t ib_port, const std::string& link_type, size_t qp_num) +// { +// SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); + +// data_ctx_ = std::make_shared(qp_num, 0); +// meta_ctx_ = std::make_shared(1, 0); + +// data_ctx_->init(dev_name, ib_port, link_type); +// meta_ctx_->init(dev_name, ib_port, link_type); + +// data_ctx_qp_num_ = data_ctx_->qp_list_len_; +// meta_ctx_qp_num_ = meta_ctx_->qp_list_len_; +// SLIME_LOG_INFO("The QP number of data plane is: ", data_ctx_qp_num_); +// SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); +// SLIME_LOG_INFO("RDMA Endpoint Init Success and Launch the RDMA Endpoint Task Threads..."); + +// const size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; +// meta_buffer_.reserve(max_meta_buffer_size); +// memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); +// meta_ctx_->register_memory_region( +// "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); + +// std::cout << meta_buffer_[0].mr_addr[0] << std::endl; + +// dum_meta_buffer_.reserve(16); +// memset(dum_meta_buffer_.data(), 0, dum_meta_buffer_.size() * sizeof(uint32_t)); +// meta_ctx_->register_memory_region("dum_meta_buffer_", +// reinterpret_cast(dum_meta_buffer_.data()), +// sizeof(uint32_t) * dum_meta_buffer_.size()); + +// dum_data_buffer_.reserve(16); +// memset(dum_data_buffer_.data(), 1048, dum_data_buffer_.size() * sizeof(uint32_t)); +// data_ctx_->register_memory_region("dum_data_buffer_", +// reinterpret_cast(dum_data_buffer_.data()), +// sizeof(uint32_t) * dum_data_buffer_.size()); +// } + +// RDMAEndpoint::~RDMAEndpoint() +// { +// { +// std::unique_lock lock(rdma_tasks_mutex_); +// RDMA_tasks_threads_running_ = false; +// } + +// rdma_tasks_cv_.notify_all(); + +// if (rdma_tasks_threads_.joinable()) +// rdma_tasks_threads_.join(); +// } + +// void RDMAEndpoint::connect(const json& data_ctx_info, const json& meta_ctx_info) +// { +// std::cout << "DDD" << std::endl; +// data_ctx_->connect(data_ctx_info); +// meta_ctx_->connect(meta_ctx_info); +// std::cout << "DDDDDD" << std::endl; +// data_ctx_->launch_future(); +// meta_ctx_->launch_future(); +// std::cout << "DDDDDDDDDDDD" << std::endl; +// RDMA_tasks_threads_running_ = true; +// rdma_tasks_threads_ = std::thread([this] { this->mainQueueThread(std::chrono::milliseconds(100)); }); +// std::cout << "DDDDDDDDDDDDDDDDDD" << std::endl; +// rdma_task_pool_ = std::make_shared(shared_from_this()); +// std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; +// } + +// void RDMAEndpoint::addSendTask(std::shared_ptr rdma_buffer) +// { +// std::unique_lock lock(rdma_tasks_mutex_); +// ++unique_SEND_SLOT_ID_; +// auto task = rdma_task_pool_->fetchSendRecvTask(rdma_buffer, unique_SEND_SLOT_ID_, OpCode::SEND); +// send_task_[unique_SEND_SLOT_ID_] = task; +// rdma_tasks_queue_.push(task); +// rdma_tasks_cv_.notify_one(); +// } + +// void RDMAEndpoint::addRecvTask(std::shared_ptr rdma_buffer) +// { +// std::unique_lock lock(rdma_tasks_mutex_); +// ++unique_RECV_SLOT_ID_; +// auto task = rdma_task_pool_->fetchSendRecvTask(rdma_buffer, unique_RECV_SLOT_ID_, OpCode::RECV); +// recv_task_[unique_RECV_SLOT_ID_] = task; +// rdma_tasks_queue_.push(task); +// rdma_tasks_cv_.notify_one(); +// } + +// void RDMAEndpoint::mainQueueThread(std::chrono::milliseconds timeout) +// { +// while (RDMA_tasks_threads_running_) { +// std::shared_ptr task; + +// { +// std::unique_lock lock(rdma_tasks_mutex_); + +// bool has_task = rdma_tasks_cv_.wait_for( +// lock, timeout, [this] { return !rdma_tasks_queue_.empty() || !RDMA_tasks_threads_running_; }); + +// if (!RDMA_tasks_threads_running_) +// break; +// if (!has_task) +// continue; + +// task = std::move(rdma_tasks_queue_.front()); +// rdma_tasks_queue_.pop(); +// } + +// if (task) { +// switch (task->rdma_operation_) { +// case OpCode::SEND: +// asyncSendData(task); +// break; +// case OpCode::RECV: +// asyncRecvData(task); +// break; +// default: +// SLIME_LOG_ERROR("Unknown OpCode in WaitandPopTask"); +// break; +// } +// } +// } +// } + +// void RDMAEndpoint::postSendTask(std::shared_ptr task) +// { +// auto data_callback = [this, task](int status, int _) mutable { +// this->rdma_task_pool_->releaseSendRecvTask(task); +// task->rdma_buffer_->send_done_callback(); +// }; + +// auto meta_callback = [this, task, data_callback](int status, int slot_id) mutable { +// task->makeRemoteDataMR(); +// AssignmentBatch data_assign_batch = task->data_assignment_batch_; +// auto data_atx = this->data_ctx_->submit(OpCode::WRITE_WITH_IMM, +// data_assign_batch, +// data_callback, +// RDMAContext::UNDEFINED_QPI, +// task->unique_slot_id_); +// }; + +// { +// AssignmentBatch meta_assignment_batch = task->dum_meta_assignment_batch_; +// meta_ctx_->submit(OpCode::RECV, meta_assignment_batch, meta_callback); +// } +// } + +// void RDMAEndpoint::asyncSendData(std::shared_ptr task) +// { +// std::cout << "The send task has been pre post" << std::endl; +// } + +// void RDMAEndpoint::asyncRecvData(std::shared_ptr task) +// { +// auto data_callback = [this, task](int status, int slot_id) mutable { +// this->rdma_task_pool_->releaseSendRecvTask(task); +// task->rdma_buffer_->recv_done_callback(); +// }; + +// auto meta_callback = [this, task](int status, int slot_id) mutable { +// std::cout << "The recv task has been pre post" << std::endl; +// }; + +// { +// auto data_atx = this->data_ctx_->submit(OpCode::RECV, +// task->dum_data_assignment_batch_, +// data_callback, +// RDMAContext::UNDEFINED_QPI, +// task->unique_slot_id_); + +// AssignmentBatch meta_assignment_batch = task->meta_assignment_batch_; +// meta_ctx_->submit(OpCode::WRITE_WITH_IMM, +// meta_assignment_batch, +// meta_callback, +// RDMAContext::UNDEFINED_QPI, +// task->unique_slot_id_); +// } +// } + + +// void RDMAEndpoint::QueueThread_v0(std::chrono::milliseconds timeout) +// { + +// while (rdma_send_thread_running_) +// { +// { +// std::unique_lock lock(queueA_mutex_); + +// if (queueA_cv_.wait_for(lock, timeout, +// [this]() { return !queueA_.empty() || !running_; })) { + +// while (!queueA_.empty() && running_) { +// auto& frontElement = queueA_.front(); +// int elementId = frontElement->getId(); +// bool flagChanged = circularBuffer_.getFlag(elementId); + +// if (flagChanged) { + +// { +// std::lock_guard bLock(queueB_mutex_); +// queueB_.push(frontElement); + +// } +// queueB_cv_.notify_one(); +// queueA_.pop(); +// } else { + +// break; +// } +// } +// } + +// } + + +// { +// std::unique_lock lock(queueB_mutex_); + +// if (!queueB_.empty()) { +// auto& frontElement = queueB_.front(); + +// if (!frontElement->isCompleted()) { + +// frontElement->executeCallback(); +// } + +// if (frontElement->isCompleted()) { + +// queueB_.pop(); +// } +// } else { +// queueB_cv_.wait_for(lock, std::chrono::milliseconds(10)); +// } +// } + +// if (queueA_.empty() && queueB_.empty()) { +// std::this_thread::sleep_for(std::chrono::milliseconds(1)); +// } +// } + + +// } // void RDMATask::fillBuffer() // { // if (opcode_ == OpCode::SEND) { diff --git a/csrc/engine/rdma/rdma_endpoint.h b/csrc/engine/rdma/rdma_endpoint.h index b5514f6..d1da7e1 100644 --- a/csrc/engine/rdma/rdma_endpoint.h +++ b/csrc/engine/rdma/rdma_endpoint.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -18,6 +19,8 @@ #include "rdma_common.h" +using JSON = nlohmann::json; + #define MAX_META_BATCH_SIZE 64 #define MAX_META_BUFFER_SIZE 64 @@ -26,141 +29,104 @@ namespace slime { class RDMABuffer; class RDMAEndpoint; -typedef struct MetaData { +// typedef struct MetaData { - uint64_t mr_addr[MAX_META_BATCH_SIZE]; - uint32_t mr_rkey[MAX_META_BATCH_SIZE]; - uint32_t mr_size[MAX_META_BATCH_SIZE]; - uint32_t mr_slot; - uint32_t mr_qpidx; +// uint64_t mr_addr[MAX_META_BATCH_SIZE]; +// uint32_t mr_rkey[MAX_META_BATCH_SIZE]; +// uint32_t mr_size[MAX_META_BATCH_SIZE]; +// uint32_t mr_slot; +// uint32_t mr_qpidx; -} __attribute__((packed)) meta_data_t; +// } meta_data_t; -// 管理收发两端的RDMABuffer -// Send: 接收RDMABuffer构造一个RDMABufferQueueElement并入队SendWaitQueue。 -// 查询其对应的meta是否准备好,如果准备好则弹出执行RDMA WRITE WITH IMM DATA - -// 弹出的RDMABuffer入队SendCompletionQueue,等待其回调函数执行完成并修改完成标志位 +struct alignas(64) MetaElement +{ + uint64_t mr_addr[MAX_META_BATCH_SIZE]; + uint32_t mr_rkey[MAX_META_BATCH_SIZE]; + uint32_t mr_size[MAX_META_BATCH_SIZE]; + uint64_t mr_slot; + + MetaElement() + { + std::memset(mr_addr, 0, sizeof(mr_addr)); + std::memset(mr_rkey, 0, sizeof(mr_rkey)); + std::memset(mr_size, 0, sizeof(mr_size)); + mr_slot = 0; + } +}; -// Recv: 接收RDMABuffer构造一个RDMABufferQueueElement并入队RecvWaitQueue。 -// 查询其对应的预提交队列是否已有对应的元素,如果有则弹出,没有则循环等待,同时直接执行meta的 RDMA WRITE WITH IMM DATA - -// 弹出的RDMABuffer入队RecvCompletionQueue,等待其回调函数执行完成并修改完成标志位 +template +class MetaBuffer +{ +public: + MetaBuffer(size_t size) : size_(size), storage_(size) {} + + void setMeta(int id, const T& meta) + { + std::unique_lock lock(mutexes_[id % size_]); + storage_[id % size_] = meta; + } + + + T getMeta(int id) const + { + std::shared_lock lock(mutexes_[id % size_]); + return storage_[id % size_]; + } + + + size_t getSize() const { return size_; } + -class RDMABufferQueueElement -{ + const T* data() const { return storage_.data(); } + T* data() { return storage_.data(); } + private: + std::vector storage_; + mutable std::vector mutexes_; + size_t size_; +}; - std::shared_ptr rdma_buffer_{nullptr}; - std::function callback_; - OpCode rdma_opcode_; - uint32_t unique_slot_id_; - std::atomic is_finished_; -public: - +struct RDMABufferQueueElement +{ RDMABufferQueueElement() = delete; RDMABufferQueueElement(const RDMABufferQueueElement&) = delete; - RDMABufferQueueElement(uint32_t unique_slot_id, OpCode rdma_opcode); - - bool isFinished(); - -} - - + RDMABufferQueueElement(uint32_t unique_slot_id, rdma_opcode, std::shared_ptr rdma_buffer_ = nullptr); -// 预提交队列 - -// Send: 生成metaRecvPrePostQueue,并预提交一定数量的RDMAPrePostQueueElement,所有元素使用dummy assignmentbatch -// 检查队头标志位,如果为true,则将该元素弹出并送入metaQueue? 同时补充新的RDMAPrePostQueueElement - -// Recv: 生成dataRecvPrePostQueue,并预提交一定数量的RDMAPrePostQueueElement,所有元素使用dummy assignmentbatch -// 检查队头标志位,如果为true,同时修改RDMABuffer中的标志位,并补充新的RDMAPrePostQueueElement + uint32_t unique_slot_id_; + OpCode rdma_opcode_; + std::shared_ptr rdma_buffer_; + std::atomic is_finished_; + std::function callback_; +}; -/** - * RDMAPrePostQueueElement - RDMA endpoint task management - * - * Manages RDMA "pre" (RECV) operations from submission to completion. - * - * Workflow: - * 1. RDMAPrePostQueueElement enqueued --> initiates meta_recv or data_recv (based on operation type) - * 2. Poll queue head's is_finished status repeatedly - * 3. When is_finished == true --> pop RDMAPrePostQueueElement from queue and - * forward information to meta queue - * - * Components: - * 1. rdma_endpoint - For submit operations - * 2. rdma_buffer - For DATA RECV pre post - * 3. AssignmentBatch - - * 4. callback - Callback function in RDMAContext for handling is_finished status - * 5. is_finished - Flag indicating whether the task is completed - */ -class RDMAPrePostQueueElement +struct RDMAPrePostQueueElement { - -private: - - // set the indicator of the task - void setStatus(bool status); - - void postRecvAssignment(); - - std::shared_ptr rdma_endpoint_{nullptr}; // Used for submit the assignment_batch_ to RDMAContext - std::shared_ptr rdma_buffer_{nullptr}; // Used for the data pre post recv - std::function callback_; // Store the callback - OpCode rdma_opcode_; - AssignmentBatch assignment_batch_; - std::atomic is_finished_; // The indicator of the if the recv is finished - mutable std::mutex task_mutex_; - -public: - - // Delete default and copy constructors to enforce specific construction RDMAPrePostQueueElement() = delete; RDMAPrePostQueueElement(const RDMAPrePostQueueElement&) = delete; + RDMAPrePostQueueElement(uint32_t task_id, OpCode rdma_opcode, std::shared_ptr rdma_endpoint = nullptr); - /** - * Constructor for RECV operations - * @param task_id Unique task identifier - * @param rdma_opcode RDMA operation code - * @param rdma_endpoint RDMA endpoint for operation submission - **/ - RDMAPrePostQueueElement(uint32_t task_id, - OpCode rdma_opcode = OpCode::RECV, - std::shared_ptr rdma_endpoint = nullptr); - - /** - * Constructor for SEND operations - * @param task_id Unique task identifier - * @param rdma_opcode RDMA operation code (default: SEND) - * @param rdma_endpoint RDMA endpoint for operation submission - * @param rdma_buffer RDMA buffer for data transmission - */ - RDMAPrePostQueueElement(uint32_t task_id, - OpCode rdma_opcode = OpCode::SEND, - std::shared_ptr rdma_endpoint = nullptr); - - ~RDMAEndPointTask(); + uint32_t task_id_; + OpCode rdma_opcode_; + AssignmentBatch assignment_batch_; + + + std::shared_ptr rdma_endpoint_; + std::atomic is_finished_; + std::function callback_; } -/** - * ProxyQueue - The Queue template which is used in the proxy - * - * Workflow: - * - * Components: - * - */ template class ProxyQueue { @@ -196,6 +162,19 @@ class ProxyQueue } + template + bool peekQueue(T& element, M&& m) + { + std::lock_guard lock(mutex_); + if (!queue_.empty() && m(queue_.front())) { + element = std::move(queue_.front()); + queue_.pop(); + return true; + } + return false; + } + + bool fetchQueue(T& element) { std::lock_guard lock(mutex_); @@ -237,140 +216,49 @@ class ProxyQueue - - - - - - - -class RDMASendRecvTaskPool { - -public: - RDMASendRecvTaskPool(std::shared_ptr rdma_endpoint); - ~RDMASendRecvTaskPool(); - - std::shared_ptr - fetchSendRecvTask(std::shared_ptr rdma_buffer, uint32_t unique_slot_id, OpCode rdma_operation); - - int releaseSendRecvTask(std::shared_ptr rdma_task); - -private: - std::shared_ptr rdma_endpoint_; - std::vector> rdma_send_task_pool_; - std::vector> rdma_recv_task_pool_; - std::vector rdma_send_task_in_use_; - std::vector rdma_recv_task_in_use_; - - mutable std::mutex pool_mutex_; - std::condition_variable task_available_cv_; - - size_t pool_size_; -}; - class RDMAEndpoint: public std::enable_shared_from_this { friend class RDMABuffer; public: - explicit RDMAEndpoint(const std::string& dev_name, - uint8_t ib_port, - const std::string& link_type, - size_t qp_num = 1); - - explicit RDMAEndpoint(const std::string& data_dev_name, - const std::string& meta_dev_name, - uint8_t ib_port, - const std::string& link_type, - size_t qp_num); + RDMAEndpoint(const std::string& dev_name, size_t ib_port, + const std::string& link_type, size_t qp_nums); // TODO: 设计聚合多网卡传输的Send Recv ~RDMAEndpoint(); - void connect(const json& data_ctx_info, const json& meta_ctx_info); - - void addRecvTask(std::shared_ptr rdma_buffer); - void addSendTask(std::shared_ptr rdma_buffer); - - void postSendTask(std::shared_ptr task); - - json dataCtxInfo() const - { - return data_ctx_->endpoint_info(); - } - - json metaCtxInfo() const - { - return meta_ctx_->endpoint_info(); - } - - std::shared_ptr dataCtx() - { - return data_ctx_; - } - - std::shared_ptr metaCtx() - { - return meta_ctx_; - } - - std::vector& metaBuffer() - { - return meta_buffer_; - } - - int metaCtxQPNum() - { - return meta_ctx_qp_num_; - } - int dataCtxQPNum() - { - return data_ctx_qp_num_; - } - + void addRDMABuffer(std::shared_ptr rdma_buffer) private: - void mainQueueThread(std::chrono::milliseconds timeout); - - void asyncRecvData(std::shared_ptr task); - void asyncSendData(std::shared_ptr task); - - ProxyQueue meta_recv_queue_; - - - - std::queue meta_recv_queue; - std::queue meta_queue; - std::queue send_queue; + void SendMetaQueueThread(std::chrono::milliseconds timeout); + void RecvDataQueueThread(std::chrono::milliseconds timeout); + + void SendBufferQueueThread(std::chrono::milliseconds timeout); + + void WaitSendFinishQueueThread(std::chrono::milliseconds timeout); + void WaitRecvFinishQueueThread(std::chrono::milliseconds timeout); - mutable std::mutex mutex_proxy - + void postRDMAAssignment(Opcode rdma_opcode); + ProxyQueue meta_recv_queue_; + ProxyQueue data_recv_queue_; - ProxyQueue send_wait_queue_; - ProxyQueue send_completion_queue_; - - ProxyQueue recv_wait_queue_; - ProxyQueue recv_completion_queue_; - - ProxyQueue meta_recv_pre_post_queue_; - ProxyQueue data_recv_pre_post_queue_; + ProxyQueue send_buffer_queue_; - std::thread send_prepost_proxy_; - std::thread send_buffer_proxy_; - std::thread send_completion_proxy_; + ProxyQueue send_finish_queue_; + ProxyQueue recv_finish_queue_; - std::thread recv_prepost_proxy_; - std::thread recv_buffer_proxy_; - std::thread recv_completion_proxy_; - - //meta 和 data 都用LRU实现 + std::thread meta_recv_thread_; + std::thread data_recv_thread_; + + std::thread send_buffer_thread_; - std::shared_ptr rdma_task_pool_; + std::thread send_finish_thread_; + std::thread recv_finish_thread_; size_t data_ctx_qp_num_; size_t meta_ctx_qp_num_; @@ -380,21 +268,13 @@ class RDMAEndpoint: public std::enable_shared_from_this { std::vector dum_meta_buffer_; std::vector dum_data_buffer_; - std::vector meta_buffer_; - std::unordered_map> send_task_; - std::unordered_map> recv_task_; + std::vector meta_buffer_; std::shared_ptr data_ctx_; std::shared_ptr meta_ctx_; - std::queue> rdma_tasks_queue_; - std::thread rdma_tasks_threads_; - - std::condition_variable rdma_tasks_cv_; - std::mutex rdma_tasks_mutex_; - bool RDMA_tasks_threads_running_; }; @@ -426,4 +306,38 @@ class RDMAEndpoint: public std::enable_shared_from_this { // }; + // json dataCtxInfo() const + // { + // return data_ctx_->endpoint_info(); + // } + + // json metaCtxInfo() const + // { + // return meta_ctx_->endpoint_info(); + // } + + // std::shared_ptr dataCtx() + // { + // return data_ctx_; + // } + + // std::shared_ptr metaCtx() + // { + // return meta_ctx_; + // } + + // std::vector& metaBuffer() + // { + // return meta_buffer_; + // } + + // int metaCtxQPNum() + // { + // return meta_ctx_qp_num_; + // } + // int dataCtxQPNum() + // { + // return data_ctx_qp_num_; + // } + } // namespace slime From fe551e2cb0433b1321e66a171cdcc24b9bef416c Mon Sep 17 00:00:00 2001 From: HaoLiuuu Date: Sun, 26 Oct 2025 20:55:50 +0800 Subject: [PATCH 8/9] new feature: implement the send/recv by proxy --- csrc/engine/rdma/rdma_endpoint.cpp | 965 +++++++++++++++-------------- csrc/engine/rdma/rdma_endpoint.h | 435 ++++++++----- 2 files changed, 792 insertions(+), 608 deletions(-) diff --git a/csrc/engine/rdma/rdma_endpoint.cpp b/csrc/engine/rdma/rdma_endpoint.cpp index f0a89c6..b152c98 100644 --- a/csrc/engine/rdma/rdma_endpoint.cpp +++ b/csrc/engine/rdma/rdma_endpoint.cpp @@ -8,45 +8,532 @@ #include "logging.h" #include +#include +#include +#include #include #include #include namespace slime { +RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, size_t ib_port, const std::string& link_type, size_t qp_nums) +{ + SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); + data_ctx_ = std::make_shared(qp_nums, 0); + meta_ctx_ = std::make_shared(1, 0); + data_ctx_->init(dev_name, ib_port, link_type); + meta_ctx_->init(dev_name, ib_port, link_type); -// // RDMAPrePostQueueElement构造函数 -// // 对于发送端,RDMAPrePostQueueElement表示一个预提交的meta recv assig + data_ctx_qp_num_ = data_ctx_->qp_list_len_; + meta_ctx_qp_num_ = meta_ctx_->qp_list_len_; + SLIME_LOG_INFO("The QP number of data plane is: ", data_ctx_qp_num_); + SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); + SLIME_LOG_INFO("RDMA Endpoint Init Success..."); -// RDMAPrePostQueueElement::RDMAPrePostQueueElement(uint32_t task_id, OpCode rdma_opcode, std::shared_ptr rdma_endpoint) + SLIME_LOG_INFO("Allocate The MR Buffer and Dummy Buffer"); + constexpr size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; + meta_buffer_.reserve(max_meta_buffer_size); + memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); + meta_ctx_->register_memory_region( + "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); + + constexpr size_t max_dum_buffer_size = 16; + dum_meta_buffer_.reserve(max_dum_buffer_size); + meta_ctx_->register_memory_region("dum_meta_buffer_", + reinterpret_cast(dum_meta_buffer_.data()), + sizeof(uint32_t) * dum_meta_buffer_.size()); + dum_data_buffer_.reserve(max_dum_buffer_size); + data_ctx_->register_memory_region("dum_data_buffer_", + reinterpret_cast(dum_data_buffer_.data()), + sizeof(uint32_t) * dum_data_buffer_.size()); + + SLIME_LOG_INFO("The MR Buffer and Dummy Buffer Construct Success..."); + + SLIME_LOG_INFO("Construct the pre-submit queue"); + + for (size_t i = 0; i < MAX_QUEUE_SIZE; i += 1) { + addPreQueueElement(OpCode::SEND); + addPreQueueElement(OpCode::RECV); + } + + meta_buffer_manager_ = std::make_unique(MAX_META_BUFFER_SIZE); + data_buffer_manager_ = std::make_unique(MAX_META_BUFFER_SIZE); +} + +void RDMAEndpoint::connect(JSON& data_ctx_info, JSON& meta_ctx_info) +{ + + data_ctx_->connect(data_ctx_info); + meta_ctx_->connect(meta_ctx_info); + + data_ctx_->launch_future(); + meta_ctx_->launch_future(); +} + +void RDMAEndpoint::startAllThreads() +{ + SLIME_LOG_INFO("Starting all RDMA endpoint threads..."); + + stop_SendMetaQueueThread_.store(false, std::memory_order_release); + stop_dataRecvQueueThread_.store(false, std::memory_order_release); + stop_SendBufferQueueThread_.store(false, std::memory_order_release); + stop_WaitSendFinishQueueThread_.store(false, std::memory_order_release); + stop_WaitRecvFinishQueueThread_.store(false, std::memory_order_release); + + // 时间根据需要再调整 + meta_recv_thread_ = std::thread([this]() { this->metaRecvQueueThread(std::chrono::milliseconds(1)); }); + + data_recv_thread_ = std::thread([this]() { this->dataRecvQueueThread(std::chrono::milliseconds(1)); }); + + send_buffer_thread_ = std::thread([this]() { this->SendBufferQueueThread(std::chrono::milliseconds(1)); }); + + send_finish_thread_ = std::thread([this]() { this->WaitSendFinishQueueThread(std::chrono::milliseconds(1)); }); + + recv_finish_thread_ = std::thread([this]() { this->WaitRecvFinishQueueThread(std::chrono::milliseconds(1)); }); + + SLIME_LOG_INFO("All 5 RDMA endpoint threads started successfully"); +} + +void RDMAEndpoint::stopAllThreads() +{ + SLIME_LOG_INFO("Stopping all RDMA endpoint threads..."); + + stop_SendMetaQueueThread_.store(true, std::memory_order_release); + stop_dataRecvQueueThread_.store(true, std::memory_order_release); + stop_SendBufferQueueThread_.store(true, std::memory_order_release); + stop_WaitSendFinishQueueThread_.store(true, std::memory_order_release); + stop_WaitRecvFinishQueueThread_.store(true, std::memory_order_release); + + meta_recv_queue_.notifyAll(); + data_recv_queue_.notifyAll(); + send_buffer_queue_.notifyAll(); + send_finish_queue_.notifyAll(); + recv_finish_queue_.notifyAll(); + + if (meta_recv_thread_.joinable()) { + meta_recv_thread_.join(); + SLIME_LOG_DEBUG("Meta recv thread stopped"); + } + + if (data_recv_thread_.joinable()) { + data_recv_thread_.join(); + SLIME_LOG_DEBUG("Data recv thread stopped"); + } + + if (send_buffer_thread_.joinable()) { + send_buffer_thread_.join(); + SLIME_LOG_DEBUG("Send buffer thread stopped"); + } + + if (send_finish_thread_.joinable()) { + send_finish_thread_.join(); + SLIME_LOG_DEBUG("Send finish thread stopped"); + } + + if (recv_finish_thread_.joinable()) { + recv_finish_thread_.join(); + SLIME_LOG_DEBUG("Recv finish thread stopped"); + } + + SLIME_LOG_INFO("All RDMA endpoint threads stopped successfully"); +} + +RDMAEndpoint::~RDMAEndpoint() +{ + try { + stopAllThreads(); + SLIME_LOG_INFO("RDMAEndpoint destroyed successfully"); + } + catch (const std::exception& e) { + SLIME_LOG_ERROR("Exception in RDMAEndpoint destructor: ", e.what()); + } +} + +void RDMAEndpoint::addPreQueueElement(OpCode rdma_opcode) +{ + if (rdma_opcode == OpCode::SEND) { + + RDMAPrePostQueueElement meta_recv_queue_element = RDMAPrePostQueueElement( + unique_meta_recv_id_.fetch_add(1, std::memory_order_relaxed), OpCode::SEND, shared_from_this()); + + meta_ctx_->submit(OpCode::RECV, + meta_recv_queue_element.assignment_batch_, + meta_recv_queue_element.callback_, + RDMAContext::UNDEFINED_QPI, + meta_recv_queue_element.unique_task_id_); + + meta_recv_queue_.enqueue(std::move(meta_recv_queue_element)); + } + else if (rdma_opcode == OpCode::RECV) { + + RDMAPrePostQueueElement data_recv_queue_element = RDMAPrePostQueueElement( + unique_data_recv_id_.fetch_add(1, std::memory_order_relaxed), OpCode::RECV, shared_from_this()); + + data_ctx_->submit(OpCode::RECV, + data_recv_queue_element.assignment_batch_, + data_recv_queue_element.callback_, + RDMAContext::UNDEFINED_QPI, + data_recv_queue_element.unique_task_id_); + + data_recv_queue_.enqueue(std::move(data_recv_queue_element)); + } + else { + SLIME_LOG_ERROR("Undefined OPCODE IN RDMAEndpoint::addPreQueueElement"); + } +} + +void RDMAEndpoint::addRDMABuffer(OpCode rdma_opcode, std::shared_ptr rdma_buffer) +{ + if (rdma_opcode == OpCode::SEND) { + RDMABufferQueueElement buffer_element = RDMABufferQueueElement( + unique_SEND_SLOT_ID_.fetch_add(1, std::memory_order_relaxed), OpCode::SEND, rdma_buffer); + + send_buffer_queue_.enqueue(std::move(buffer_element)); + } + else if (rdma_opcode == OpCode::RECV) { + postMetaWrite(rdma_buffer); + + RDMABufferQueueElement buffer_element = RDMABufferQueueElement( + unique_RECV_SLOT_ID_.fetch_add(1, std::memory_order_relaxed), OpCode::RECV, rdma_buffer); + + recv_finish_queue_.enqueue(std::move(buffer_element)); + } + else { + SLIME_LOG_ERROR("Undefined OPCODE IN RDMAEndpoint::addRDMABuffer"); + } +} + +void RDMAEndpoint::postMetaWrite(std::shared_ptr rdma_buffer) +{ + + std::string prefix = "DATA_RECV_"; + std::string MR_KEY = prefix + std::to_string(unique_RECV_SLOT_ID_) + "_"; + auto mr = data_ctx_->get_mr(MR_KEY + std::to_string(0)); + + if (mr != nullptr) { + SLIME_LOG_DEBUG("The DATA MR has been REGISTERED! The SLOT_ID is: ", unique_RECV_SLOT_ID_); + } + else { + auto view_batch = rdma_buffer->view_batch(); + for (size_t i = 0; i < rdma_buffer->batch_size(); ++i) { + data_ctx_->register_memory_region(MR_KEY + std::to_string(i), view_batch[i].data_ptr, view_batch[i].length); + } + } + + for (size_t i = 0; i < rdma_buffer->batch_size(); i += 1) { + auto mr = data_ctx_->get_mr(MR_KEY + std::to_string(i)); + meta_buffer_[MAX_META_BUFFER_SIZE + unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_addr[i] = + reinterpret_cast(mr->addr); + meta_buffer_[MAX_META_BUFFER_SIZE + unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; + meta_buffer_[MAX_META_BUFFER_SIZE + unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_size[i] = mr->length; + meta_buffer_[MAX_META_BUFFER_SIZE + unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_slot = unique_RECV_SLOT_ID_; + } + + size_t meta_buffer_idx = unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE; + AssignmentBatch meta_assignment_batch_ = + AssignmentBatch{Assignment("meta_buffer", + meta_buffer_idx * sizeof(meta_data_t), + MAX_META_BUFFER_SIZE * sizeof(meta_data_t) + meta_buffer_idx * sizeof(meta_data_t), + sizeof(meta_data_t))}; + + auto meta_callback = [this](int status, int slot_id) mutable { std::cout << "META WRITE SUCCESS"; }; + meta_ctx_->submit(OpCode::WRITE_WITH_IMM, + meta_assignment_batch_, + meta_callback, + RDMAContext::UNDEFINED_QPI, + unique_RECV_SLOT_ID_); +} + +void RDMAEndpoint::metaRecvQueueThread(std::chrono::milliseconds timeout) +{ + SLIME_LOG_INFO("metaRecvQueueThread started"); + + while (!stop_SendMetaQueueThread_.load(std::memory_order_acquire)) { + + RDMAPrePostQueueElement element; + + bool found = meta_recv_queue_.peekQueue(element, [](const RDMAPrePostQueueElement& elem) -> bool { + return elem.is_finished_.load(std::memory_order_acquire); + }); + + if (found) { + if (meta_buffer_manager_->setSlotTrueWithWait(element.unique_task_id_)) { + SLIME_LOG_DEBUG("Set slot {} to true for task {}", + element.unique_task_id_ % MAX_META_BUFFER_SIZE, + element.unique_task_id_); + } + else { + SLIME_LOG_WARN("Failed to set slot for task {} - timeout", element.unique_task_id_); + } + } + else { + if (timeout.count() > 0) { + std::this_thread::sleep_for(timeout); + } + else { + std::this_thread::yield(); + } + } + } + + SLIME_LOG_INFO("metaRecvQueueThread stopped"); +} + +void RDMAEndpoint::dataRecvQueueThread(std::chrono::milliseconds timeout) +{ + SLIME_LOG_INFO("RecvDataQueueThread started"); + while (!stop_dataRecvQueueThread_.load(std::memory_order_acquire)) { + + RDMAPrePostQueueElement element; + + bool found = data_recv_queue_.peekQueue(element, [](const RDMAPrePostQueueElement& elem) -> bool { + return elem.is_finished_.load(std::memory_order_acquire); + }); + + if (found) { + + if (data_buffer_manager_->setSlotTrueWithWait(element.unique_task_id_)) { + SLIME_LOG_DEBUG("Set slot {} to true for task {}", + element.unique_task_id_ % MAX_META_BUFFER_SIZE, + element.unique_task_id_); + } + else { + SLIME_LOG_WARN("Failed to set slot for task {} - timeout", element.unique_task_id_); + } + } + else { + if (timeout.count() > 0) { + std::this_thread::sleep_for(timeout); + } + else { + std::this_thread::yield(); + } + } + } + SLIME_LOG_INFO("RecvDataQueueThread stopped"); +} + +void RDMAEndpoint::SendBufferQueueThread(std::chrono::milliseconds timeout) +{ + SLIME_LOG_INFO("SendBufferQueueThread started"); + + while (!stop_SendBufferQueueThread_.load(std::memory_order_acquire)) { + RDMABufferQueueElement element; + + bool has_element = send_buffer_queue_.fetchQueue(element); + + if (has_element) { + + bool is_ready = meta_buffer_manager_->readSlot(element.unique_slot_id_); + + if (is_ready) { + + postDataWrite(element, element.rdma_buffer_); + send_finish_queue_.enqueue(std::move(element)); + SLIME_LOG_DEBUG("Processing send buffer element {}, meta buffer is ready", element.unique_slot_id_); + } + else { + + SLIME_LOG_DEBUG("Meta buffer not ready for element {}, re-enqueueing", element.unique_slot_id_); + + send_buffer_queue_.enqueue(std::move(element)); + + if (timeout.count() > 0) { + std::this_thread::sleep_for(timeout); + } + else { + std::this_thread::yield(); + } + } + } + else { + if (timeout.count() > 0) { + std::this_thread::sleep_for(timeout); + } + else { + std::this_thread::yield(); + } + } + } + + SLIME_LOG_INFO("SendBufferQueueThread stopped"); +} + +void RDMAEndpoint::postDataWrite(RDMABufferQueueElement& element, std::shared_ptr rdma_buffer) +{ + std::string prefix = "DATA_SEND_"; + std::string MR_KEY = prefix + std::to_string(unique_SEND_SLOT_ID_) + "_"; + auto mr = data_ctx_->get_mr(MR_KEY + std::to_string(0)); + if (mr != nullptr) { + SLIME_LOG_DEBUG("The DATA MR has been REGISTERED! The SLOT_ID is: ", unique_SEND_SLOT_ID_); + } + else { + auto view_batch = rdma_buffer->view_batch(); + for (size_t i = 0; i < rdma_buffer->batch_size(); ++i) { + std::cout << view_batch[i].data_ptr << std::endl; + data_ctx_->register_memory_region(MR_KEY + std::to_string(i), view_batch[i].data_ptr, view_batch[i].length); + } + } + + auto mr_remote = data_ctx_->get_remote_mr(MR_KEY + std::to_string(0)); + if (mr_remote.addr == 0) { + + for (size_t i = 0; i < rdma_buffer->batch_size(); ++i) { + uint64_t addr = meta_buffer_[unique_SEND_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_addr[i]; + uint32_t size = meta_buffer_[unique_SEND_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_size[i]; + uint32_t rkey = meta_buffer_[unique_SEND_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_rkey[i]; + data_ctx_->register_remote_memory_region(MR_KEY + std::to_string(i), addr, size, rkey); + } + } + + else { + SLIME_LOG_DEBUG("The REMOTE DATA MR has been REGISTERED! The SLOT_ID is: ", unique_SEND_SLOT_ID_); + } + + AssignmentBatch batch; + + for (size_t i = 0; i < rdma_buffer->batch_size(); ++i) { + batch.push_back(Assignment(MR_KEY + std::to_string(i), 0, 0, rdma_buffer->view_batch()[i].length)); + } + + auto data_atx = this->data_ctx_->submit( + OpCode::WRITE_WITH_IMM, batch, element.callback_, RDMAContext::UNDEFINED_QPI, unique_SEND_SLOT_ID_); +} + +void RDMAEndpoint::WaitSendFinishQueueThread(std::chrono::milliseconds timeout) +{ + SLIME_LOG_INFO("WaitSendFinishQueueThread started"); + + while (!stop_WaitSendFinishQueueThread_.load(std::memory_order_acquire)) { + bool processed_any = false; + + while (true) { + RDMABufferQueueElement element; + + bool found = send_finish_queue_.peekQueue(element, [](const RDMABufferQueueElement& elem) -> bool { + return elem.is_finished_.load(std::memory_order_acquire); + }); + + if (found) { + SLIME_LOG_DEBUG("Processing completed send element {}", element.unique_slot_id_); + + processed_any = true; + } + else { + break; + } + } + + if (!processed_any) { + if (timeout.count() > 0) { + std::this_thread::sleep_for(timeout); + } + else { + std::this_thread::yield(); + } + } + } + + SLIME_LOG_INFO("WaitSendFinishQueueThread stopped"); +} + +void RDMAEndpoint::WaitRecvFinishQueueThread(std::chrono::milliseconds timeout) +{ + SLIME_LOG_INFO("WaitRecvFinishQueueThread started"); + + while (!stop_WaitRecvFinishQueueThread_.load(std::memory_order_acquire)) { + bool processed_any = false; + + while (true) { + RDMABufferQueueElement element; + + bool found = recv_finish_queue_.peekQueue(element, [](const RDMABufferQueueElement& elem) -> bool { + return elem.is_finished_.load(std::memory_order_acquire); + }); + + if (found) { + + SLIME_LOG_DEBUG("Processing completed recv element {}", element.unique_slot_id_); + processed_any = true; + } + else { + + break; + } + } + if (!processed_any) { + if (timeout.count() > 0) { + std::this_thread::sleep_for(timeout); + } + else { + std::this_thread::yield(); + } + } + } + + SLIME_LOG_INFO("WaitRecvFinishQueueThread stopped"); +} + +// int RDMASendRecvTask::makeRemoteDataMR() // { -// task_id_ = task_id; -// rdma_opcode_ = rdma_opcode; +// if (!rdma_buffer_ || !rdma_endpoint_) { +// SLIME_LOG_ERROR("Null pointer detected: rdma_buffer_=" << rdma_buffer_ +// << ", rdma_endpoint_=" << rdma_endpoint_); +// return -1; +// } -// if (rdma_opcode_ == OpCode::SEND) -// { -// assignment_batch_ = AssignmentBatch{Assignment("dum_meta_buffer_", 0, 0, 16 * sizeof(uint32_t))}; +// std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; +// std::string MR_KEY = prefix + std::to_string(unique_slot_id_) + "_"; +// auto mr = rdma_endpoint_->dataCtx()->get_remote_mr(MR_KEY + std::to_string(0)); -// auto callback = [this] (int status, int slot_id) mutable -// { -// this->is_finished_ = true; +// if (mr.addr == 0) { +// auto& meta_buffer = rdma_endpoint_->metaBuffer(); +// for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { +// uint64_t addr = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i]; +// uint32_t size = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i]; +// uint32_t rkey = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i]; +// rdma_endpoint_->dataCtx()->register_remote_memory_region(MR_KEY + std::to_string(i), addr, size, +// rkey); // } -// }; // } -// else if (rdma_opcode_ == OpCode::RECV) -// { -// assignment_batch_ = AssignmentBatch{Assignment("dum_data_buffer_", 0, 0, 16 * sizeof(uint32_t))}; -// auto callback = [this] (int status, int slot_id) mutable -// { -// this->is_finished_ = true; -// } + +// else { +// SLIME_LOG_DEBUG("The REMOTE DATA MR has been REGISTERED! The SLOT_ID is: ", unique_slot_id_); // } +// return 0; +// } +// RDMAEndpoint::~RDMAEndpoint() +// { +// { +// std::unique_lock lock(rdma_tasks_mutex_); +// RDMA_tasks_threads_running_ = false; +// } +// rdma_tasks_cv_.notify_all(); +// if (rdma_tasks_threads_.joinable()) +// rdma_tasks_threads_.join(); +// } + +// void RDMAEndpoint::connect(const json& data_ctx_info, const json& meta_ctx_info) +// { +// std::cout << "DDD" << std::endl; +// data_ctx_->connect(data_ctx_info); +// meta_ctx_->connect(meta_ctx_info); +// std::cout << "DDDDDD" << std::endl; +// data_ctx_->launch_future(); +// meta_ctx_->launch_future(); +// std::cout << "DDDDDDDDDDDD" << std::endl; +// RDMA_tasks_threads_running_ = true; +// rdma_tasks_threads_ = std::thread([this] { this->mainQueueThread(std::chrono::milliseconds(100)); }); +// std::cout << "DDDDDDDDDDDDDDDDDD" << std::endl; +// rdma_task_pool_ = std::make_shared(shared_from_this()); +// std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; // } // RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, uint32_t task_id) @@ -56,7 +543,8 @@ namespace slime { // makeDummyAssignmentBatch(); // } -// RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rmda_opcode, uint32_t task_id): +// RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rmda_opcode, uint32_t +// task_id): // rdma_endpoint_(rdma_endpoint), rdma_operation_(rmda_opcode), task_id_(task_id) // { // unique_slot_id_ = -1; @@ -110,8 +598,8 @@ namespace slime { // std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; // for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { -// auto mr = rdma_endpoint_->dataCtx()->get_mr(prefix + std::to_string(unique_slot_id_) + "_" + std::to_string(i)); -// if (rdma_operation_ == OpCode::RECV) { +// auto mr = rdma_endpoint_->dataCtx()->get_mr(prefix + std::to_string(unique_slot_id_) + "_" + +// std::to_string(i)); if (rdma_operation_ == OpCode::RECV) { // meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i] = // reinterpret_cast(mr->addr); // meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; @@ -123,247 +611,6 @@ namespace slime { // return 0; // } -// int RDMASendRecvTask::makeDataMR() -// { - -// std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; -// std::string MR_KEY = prefix + std::to_string(unique_slot_id_) + "_"; -// auto mr = rdma_endpoint_->dataCtx()->get_mr(MR_KEY + std::to_string(0)); -// if (mr != nullptr) { -// SLIME_LOG_DEBUG("The DATA MR has been REGISTERED! The SLOT_ID is: ", unique_slot_id_); -// } -// else { -// auto view_batch = rdma_buffer_->view_batch(); -// for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { -// std::cout << view_batch[i].data_ptr << std::endl; -// rdma_endpoint_->dataCtx()->register_memory_region( -// MR_KEY + std::to_string(i), view_batch[i].data_ptr, view_batch[i].length); -// } -// } - -// return 0; -// } -// int RDMASendRecvTask::makeRemoteDataMR() -// { -// if (!rdma_buffer_ || !rdma_endpoint_) { -// SLIME_LOG_ERROR("Null pointer detected: rdma_buffer_=" << rdma_buffer_ -// << ", rdma_endpoint_=" << rdma_endpoint_); -// return -1; -// } - -// std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; -// std::string MR_KEY = prefix + std::to_string(unique_slot_id_) + "_"; -// auto mr = rdma_endpoint_->dataCtx()->get_remote_mr(MR_KEY + std::to_string(0)); - -// if (mr.addr == 0) { -// auto& meta_buffer = rdma_endpoint_->metaBuffer(); -// for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { -// uint64_t addr = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i]; -// uint32_t size = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i]; -// uint32_t rkey = meta_buffer[unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i]; -// rdma_endpoint_->dataCtx()->register_remote_memory_region(MR_KEY + std::to_string(i), addr, size, rkey); -// } -// } - -// else { -// SLIME_LOG_DEBUG("The REMOTE DATA MR has been REGISTERED! The SLOT_ID is: ", unique_slot_id_); -// } - -// return 0; -// } - -// void RDMASendRecvTask::configurationTask(std::shared_ptr rdma_buffer, -// uint32_t unique_slot_id, -// OpCode rmda_opcode) -// { -// rdma_buffer_ = rdma_buffer; -// unique_slot_id_ = unique_slot_id; -// rdma_operation_ = rmda_opcode; -// makeAssignmentBatch(); -// makeDataMR(); -// makeMetaMR(); -// } - -// RDMASendRecvTaskPool::RDMASendRecvTaskPool(std::shared_ptr rdma_endpoint) -// { -// rdma_endpoint_ = rdma_endpoint; -// pool_size_ = 128; - -// for (size_t i = 0; i < pool_size_; ++i) { -// auto task = std::make_shared(rdma_endpoint_, i); -// rdma_send_task_pool_.push_back(task); -// // rdma_endpoint_->postSendTask(task); -// rdma_send_task_in_use_.push_back(false); -// } - -// for (size_t i = 0; i < pool_size_; ++i) { -// rdma_recv_task_pool_.push_back(std::make_shared(rdma_endpoint_, i)); -// rdma_recv_task_in_use_.push_back(false); -// } -// } - -// RDMASendRecvTaskPool::~RDMASendRecvTaskPool() -// { -// SLIME_LOG_INFO("Destroy the RDMASendRecvTaskPool"); -// } - -// std::shared_ptr RDMASendRecvTaskPool::fetchSendRecvTask(std::shared_ptr rdma_buffer, -// uint32_t unique_slot_id, -// OpCode rdma_operation) -// { -// if (rdma_operation == OpCode::SEND) { - -// std::unique_lock lock(pool_mutex_); - -// task_available_cv_.wait(lock, [this]() { -// return std::find(this->rdma_send_task_in_use_.begin(), this->rdma_send_task_in_use_.end(), false) -// != this->rdma_send_task_in_use_.end(); -// }); - -// for (size_t i = 0; i < rdma_send_task_in_use_.size(); ++i) { -// if (!rdma_send_task_in_use_[i]) { -// rdma_send_task_in_use_[i] = true; -// auto task = rdma_send_task_pool_[i]; -// task->configurationTask(rdma_buffer, unique_slot_id, rdma_operation); -// rdma_endpoint_->postSendTask(task); -// return task; -// } -// } -// } -// else if (rdma_operation == OpCode::RECV) { - -// std::unique_lock lock(pool_mutex_); - -// task_available_cv_.wait(lock, [this]() { -// return std::find(this->rdma_recv_task_in_use_.begin(), this->rdma_recv_task_in_use_.end(), false) -// != this->rdma_recv_task_in_use_.end(); -// }); - -// for (size_t i = 0; i < rdma_recv_task_in_use_.size(); ++i) { -// if (!rdma_recv_task_in_use_[i]) { -// rdma_recv_task_in_use_[i] = true; -// auto task = rdma_recv_task_pool_[i]; -// task->configurationTask(rdma_buffer, unique_slot_id, rdma_operation); -// return task; -// } -// } -// } -// else { -// SLIME_LOG_ERROR("Unsupported RDMA operation in fetchSendRecvTask!"); -// return nullptr; -// } - -// return nullptr; -// } - -// int RDMASendRecvTaskPool::releaseSendRecvTask(std::shared_ptr rdma_task) -// { - -// if (rdma_task->rdma_operation_ == OpCode::SEND) { -// std::unique_lock lock(pool_mutex_); -// rdma_send_task_in_use_[rdma_task->task_id_] = false; -// rdma_endpoint_->postSendTask(rdma_task); -// task_available_cv_.notify_one(); -// return 0; -// } -// else if (rdma_task->rdma_operation_ == OpCode::RECV) { -// std::unique_lock lock(pool_mutex_); -// rdma_recv_task_in_use_[rdma_task->task_id_] = false; -// task_available_cv_.notify_one(); -// return 0; -// } -// else { -// SLIME_LOG_ERROR("Unsupported RDMA operation in releaseSendRecvTask!"); -// return -1; -// } -// } - -// RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, uint8_t ib_port, const std::string& link_type, size_t qp_num) -// { -// SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); - -// data_ctx_ = std::make_shared(qp_num, 0); -// meta_ctx_ = std::make_shared(1, 0); - -// data_ctx_->init(dev_name, ib_port, link_type); -// meta_ctx_->init(dev_name, ib_port, link_type); - -// data_ctx_qp_num_ = data_ctx_->qp_list_len_; -// meta_ctx_qp_num_ = meta_ctx_->qp_list_len_; -// SLIME_LOG_INFO("The QP number of data plane is: ", data_ctx_qp_num_); -// SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); -// SLIME_LOG_INFO("RDMA Endpoint Init Success and Launch the RDMA Endpoint Task Threads..."); - -// const size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; -// meta_buffer_.reserve(max_meta_buffer_size); -// memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); -// meta_ctx_->register_memory_region( -// "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); - -// std::cout << meta_buffer_[0].mr_addr[0] << std::endl; - -// dum_meta_buffer_.reserve(16); -// memset(dum_meta_buffer_.data(), 0, dum_meta_buffer_.size() * sizeof(uint32_t)); -// meta_ctx_->register_memory_region("dum_meta_buffer_", -// reinterpret_cast(dum_meta_buffer_.data()), -// sizeof(uint32_t) * dum_meta_buffer_.size()); - -// dum_data_buffer_.reserve(16); -// memset(dum_data_buffer_.data(), 1048, dum_data_buffer_.size() * sizeof(uint32_t)); -// data_ctx_->register_memory_region("dum_data_buffer_", -// reinterpret_cast(dum_data_buffer_.data()), -// sizeof(uint32_t) * dum_data_buffer_.size()); -// } - -// RDMAEndpoint::~RDMAEndpoint() -// { -// { -// std::unique_lock lock(rdma_tasks_mutex_); -// RDMA_tasks_threads_running_ = false; -// } - -// rdma_tasks_cv_.notify_all(); - -// if (rdma_tasks_threads_.joinable()) -// rdma_tasks_threads_.join(); -// } - -// void RDMAEndpoint::connect(const json& data_ctx_info, const json& meta_ctx_info) -// { -// std::cout << "DDD" << std::endl; -// data_ctx_->connect(data_ctx_info); -// meta_ctx_->connect(meta_ctx_info); -// std::cout << "DDDDDD" << std::endl; -// data_ctx_->launch_future(); -// meta_ctx_->launch_future(); -// std::cout << "DDDDDDDDDDDD" << std::endl; -// RDMA_tasks_threads_running_ = true; -// rdma_tasks_threads_ = std::thread([this] { this->mainQueueThread(std::chrono::milliseconds(100)); }); -// std::cout << "DDDDDDDDDDDDDDDDDD" << std::endl; -// rdma_task_pool_ = std::make_shared(shared_from_this()); -// std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; -// } - -// void RDMAEndpoint::addSendTask(std::shared_ptr rdma_buffer) -// { -// std::unique_lock lock(rdma_tasks_mutex_); -// ++unique_SEND_SLOT_ID_; -// auto task = rdma_task_pool_->fetchSendRecvTask(rdma_buffer, unique_SEND_SLOT_ID_, OpCode::SEND); -// send_task_[unique_SEND_SLOT_ID_] = task; -// rdma_tasks_queue_.push(task); -// rdma_tasks_cv_.notify_one(); -// } - -// void RDMAEndpoint::addRecvTask(std::shared_ptr rdma_buffer) -// { -// std::unique_lock lock(rdma_tasks_mutex_); -// ++unique_RECV_SLOT_ID_; -// auto task = rdma_task_pool_->fetchSendRecvTask(rdma_buffer, unique_RECV_SLOT_ID_, OpCode::RECV); -// recv_task_[unique_RECV_SLOT_ID_] = task; -// rdma_tasks_queue_.push(task); -// rdma_tasks_cv_.notify_one(); -// } - // void RDMAEndpoint::mainQueueThread(std::chrono::milliseconds timeout) // { // while (RDMA_tasks_threads_running_) { @@ -400,29 +647,6 @@ namespace slime { // } // } -// void RDMAEndpoint::postSendTask(std::shared_ptr task) -// { -// auto data_callback = [this, task](int status, int _) mutable { -// this->rdma_task_pool_->releaseSendRecvTask(task); -// task->rdma_buffer_->send_done_callback(); -// }; - -// auto meta_callback = [this, task, data_callback](int status, int slot_id) mutable { -// task->makeRemoteDataMR(); -// AssignmentBatch data_assign_batch = task->data_assignment_batch_; -// auto data_atx = this->data_ctx_->submit(OpCode::WRITE_WITH_IMM, -// data_assign_batch, -// data_callback, -// RDMAContext::UNDEFINED_QPI, -// task->unique_slot_id_); -// }; - -// { -// AssignmentBatch meta_assignment_batch = task->dum_meta_assignment_batch_; -// meta_ctx_->submit(OpCode::RECV, meta_assignment_batch, meta_callback); -// } -// } - // void RDMAEndpoint::asyncSendData(std::shared_ptr task) // { // std::cout << "The send task has been pre post" << std::endl; @@ -455,68 +679,6 @@ namespace slime { // } // } - -// void RDMAEndpoint::QueueThread_v0(std::chrono::milliseconds timeout) -// { - -// while (rdma_send_thread_running_) -// { -// { -// std::unique_lock lock(queueA_mutex_); - -// if (queueA_cv_.wait_for(lock, timeout, -// [this]() { return !queueA_.empty() || !running_; })) { - -// while (!queueA_.empty() && running_) { -// auto& frontElement = queueA_.front(); -// int elementId = frontElement->getId(); -// bool flagChanged = circularBuffer_.getFlag(elementId); - -// if (flagChanged) { - -// { -// std::lock_guard bLock(queueB_mutex_); -// queueB_.push(frontElement); - -// } -// queueB_cv_.notify_one(); -// queueA_.pop(); -// } else { - -// break; -// } -// } -// } - -// } - - -// { -// std::unique_lock lock(queueB_mutex_); - -// if (!queueB_.empty()) { -// auto& frontElement = queueB_.front(); - -// if (!frontElement->isCompleted()) { - -// frontElement->executeCallback(); -// } - -// if (frontElement->isCompleted()) { - -// queueB_.pop(); -// } -// } else { -// queueB_cv_.wait_for(lock, std::chrono::milliseconds(10)); -// } -// } - -// if (queueA_.empty() && queueB_.empty()) { -// std::this_thread::sleep_for(std::chrono::milliseconds(1)); -// } -// } - - // } // void RDMATask::fillBuffer() // { @@ -541,29 +703,6 @@ namespace slime { // } // } -// RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, uint8_t ib_port, const std::string& link_type, size_t qp_num) -// { -// SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); - -// data_ctx_ = std::make_shared(qp_num, 0); -// meta_ctx_ = std::make_shared(1, 0); - -// data_ctx_->init(dev_name, ib_port, link_type); -// meta_ctx_->init(dev_name, ib_port, link_type); - -// data_ctx_qp_num_ = data_ctx_->qp_list_len_; -// meta_ctx_qp_num_ = meta_ctx_->qp_list_len_; -// SLIME_LOG_INFO("The QP number of data plane is: ", data_ctx_qp_num_); -// SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); -// SLIME_LOG_INFO("RDMA Endpoint Init Success and Launch the RDMA Endpoint Task Threads..."); - -// const size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; -// meta_buffer_.reserve(max_meta_buffer_size); -// memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); -// meta_ctx_->register_memory_region( -// "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); -// } - // RDMAEndpoint::RDMAEndpoint(const std::string& data_dev_name, // const std::string& meta_dev_name, // uint8_t ib_port, @@ -587,7 +726,8 @@ namespace slime { // meta_buffer_.reserve(max_meta_buffer_size); // memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); // meta_ctx_->register_memory_region( -// "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); +// "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * +// max_meta_buffer_size); // } // RDMAEndpoint::~RDMAEndpoint() @@ -603,85 +743,4 @@ namespace slime { // rdma_tasks_threads_.join(); // } -// void RDMAEndpoint::connect(const json& data_ctx_info, const json& meta_ctx_info) -// { -// data_ctx_->connect(data_ctx_info); -// meta_ctx_->connect(meta_ctx_info); - -// data_ctx_->launch_future(); -// meta_ctx_->launch_future(); - -// RDMA_tasks_threads_running_ = true; -// rdma_tasks_threads_ = std::thread([this] { this->waitandPopTask(std::chrono::milliseconds(100)); }); -// } - -// void RDMAEndpoint::addSendTask(std::shared_ptr buffer) -// { -// std::unique_lock lock(rdma_tasks_mutex_); -// ++send_slot_id_; -// auto task = std::make_shared(shared_from_this(), send_slot_id_, OpCode::SEND, buffer); -// send_batch_slot_[send_slot_id_] = task; -// rdma_tasks_queue_.push(task); -// rdma_tasks_cv_.notify_one(); -// } - -// void RDMAEndpoint::addRecvTask(std::shared_ptr buffer) -// { -// std::unique_lock lock(rdma_tasks_mutex_); -// ++recv_slot_id_; -// auto task = std::make_shared(shared_from_this(), recv_slot_id_, OpCode::RECV, buffer); -// recv_batch_slot_[recv_slot_id_] = task; -// rdma_tasks_queue_.push(task); -// rdma_tasks_cv_.notify_one(); -// } - -// void RDMAEndpoint::asyncSendData(std::shared_ptr task) -// { -// // size_t batch_size = task->buffer_->batchSize(); -// // uint32_t slot_id = task->slot_id_; - -// auto data_callback = [this, task](int status, int _) mutable { -// std::unique_lock lock(rdma_tasks_mutex_); - -// task->buffer_->send_done_callback(); -// }; - -// auto meta_callback = [this, task, data_callback](int status, int slot_id) mutable { -// std::unique_lock lock(this->rdma_tasks_mutex_); -// // Assert -// task->registerRemoteDataMemoryRegion(); -// AssignmentBatch data_assign_batch = task->getDataAssignmentBatch(); -// auto data_atx = this->data_ctx_->submit( -// OpCode::WRITE_WITH_IMM, data_assign_batch, data_callback, RDMAContext::UNDEFINED_QPI, slot_id); -// }; - -// { -// AssignmentBatch meta_data = task->getMetaAssignmentBatch(); -// meta_ctx_->submit(OpCode::RECV, meta_data, meta_callback); -// } -// } - -// void RDMAEndpoint::asyncRecvData(std::shared_ptr task) -// { -// auto data_callback = [this, task](int status, int slot_id) mutable { -// std::unique_lock lock(rdma_tasks_mutex_); -// recv_batch_slot_[slot_id]->buffer_->recv_done_callback(); -// }; - -// auto meta_callback = [this, task, data_callback](int status, int _) mutable { -// std::unique_lock lock(this->rdma_tasks_mutex_); -// AssignmentBatch assign_batch = task->getDataAssignmentBatch(); - -// }; - -// { -// // post recv -// auto data_atx = this->data_ctx_->submit(OpCode::RECV, assign_batch, data_callback, -// RDMAContext::UNDEFINED_QPI); -// AssignmentBatch meta_data = task->getMetaAssignmentBatch(); -// meta_ctx_->submit(OpCode::WRITE_WITH_IMM, meta_data, meta_callback, RDMAContext::UNDEFINED_QPI, -// task->slot_id_); -// } -// } - } // namespace slime diff --git a/csrc/engine/rdma/rdma_endpoint.h b/csrc/engine/rdma/rdma_endpoint.h index d1da7e1..1ae034b 100644 --- a/csrc/engine/rdma/rdma_endpoint.h +++ b/csrc/engine/rdma/rdma_endpoint.h @@ -8,49 +8,46 @@ #include #include #include -#include #include #include #include +#include #include #include #include #include +#include "logging.h" #include "rdma_common.h" -using JSON = nlohmann::json; +using JSON = const nlohmann::json; #define MAX_META_BATCH_SIZE 64 #define MAX_META_BUFFER_SIZE 64 +#define MAX_QUEUE_SIZE 64 namespace slime { class RDMABuffer; class RDMAEndpoint; -// typedef struct MetaData { - -// uint64_t mr_addr[MAX_META_BATCH_SIZE]; -// uint32_t mr_rkey[MAX_META_BATCH_SIZE]; -// uint32_t mr_size[MAX_META_BATCH_SIZE]; -// uint32_t mr_slot; -// uint32_t mr_qpidx; - -// } meta_data_t; - - +typedef struct MetaData { + uint64_t mr_addr[MAX_META_BATCH_SIZE]; + uint32_t mr_rkey[MAX_META_BATCH_SIZE]; + uint32_t mr_size[MAX_META_BATCH_SIZE]; + uint32_t mr_slot; + uint32_t mr_qpidx; +} meta_data_t; -struct alignas(64) MetaElement -{ +struct alignas(64) MetaElement { uint64_t mr_addr[MAX_META_BATCH_SIZE]; uint32_t mr_rkey[MAX_META_BATCH_SIZE]; uint32_t mr_size[MAX_META_BATCH_SIZE]; uint64_t mr_slot; - - MetaElement() + + MetaElement() { std::memset(mr_addr, 0, sizeof(mr_addr)); std::memset(mr_rkey, 0, sizeof(mr_rkey)); @@ -59,90 +56,240 @@ struct alignas(64) MetaElement } }; - template -class MetaBuffer -{ +class MetaBuffer { public: - MetaBuffer(size_t size) : size_(size), storage_(size) {} - + MetaBuffer(size_t size): size_(size), storage_(size) {} - void setMeta(int id, const T& meta) + void setMeta(int id, const T& meta) { std::unique_lock lock(mutexes_[id % size_]); storage_[id % size_] = meta; } - - - T getMeta(int id) const + + T getMeta(int id) const { std::shared_lock lock(mutexes_[id % size_]); return storage_[id % size_]; } - - - size_t getSize() const { return size_; } - - - const T* data() const { return storage_.data(); } - T* data() { return storage_.data(); } - + + size_t getSize() const + { + return size_; + } + + const T* data() const + { + return storage_.data(); + } + T* data() + { + return storage_.data(); + } + private: - std::vector storage_; - mutable std::vector mutexes_; - size_t size_; + std::vector storage_; + mutable std::vector mutexes_; + size_t size_; +}; + +struct RDMABufferQueueElement { + + RDMABufferQueueElement(); + RDMABufferQueueElement(uint32_t unique_slot_id, + OpCode rdma_opcode, + std::shared_ptr rdma_buffer = nullptr): + unique_slot_id_(unique_slot_id), rdma_opcode_(rdma_opcode), rdma_buffer_(rdma_buffer) + + { + is_finished_ = false; + auto callback = [this](int status, int slot_id) mutable { this->is_finished_ = true; }; + callback_ = std::move(callback); + } + + RDMABufferQueueElement& operator=(RDMABufferQueueElement&& other) noexcept + { + if (this != &other) { + unique_slot_id_ = other.unique_slot_id_; + rdma_opcode_ = other.rdma_opcode_; + rdma_buffer_ = std::move(other.rdma_buffer_); + is_finished_.store(other.is_finished_.load(std::memory_order_relaxed)); + callback_ = std::move(other.callback_); + } + return *this; + } + + uint32_t unique_slot_id_; + OpCode rdma_opcode_; + std::shared_ptr rdma_buffer_; + std::atomic is_finished_; + std::function callback_; }; +struct RDMAPrePostQueueElement { + + RDMAPrePostQueueElement(); + RDMAPrePostQueueElement(uint32_t unique_task_id, + OpCode rdma_opcode, + std::shared_ptr rdma_endpoint = nullptr): + unique_task_id_(unique_task_id), rdma_opcode_(rdma_opcode), rdma_endpoint_(rdma_endpoint) + { + assignment_batch_ = rdma_opcode_ == OpCode::SEND ? + AssignmentBatch{Assignment("dum_meta_buffer_", 0, 0, 16 * sizeof(uint32_t))} : + AssignmentBatch{Assignment("dum_data_buffer_", 0, 0, 16 * sizeof(uint32_t))}; -struct RDMABufferQueueElement -{ - RDMABufferQueueElement() = delete; - RDMABufferQueueElement(const RDMABufferQueueElement&) = delete; - RDMABufferQueueElement(uint32_t unique_slot_id, rdma_opcode, std::shared_ptr rdma_buffer_ = nullptr); + is_finished_ = false; + auto callback = [this](int status, int slot_id) mutable { this->is_finished_ = true; }; + callback_ = std::move(callback); + } - uint32_t unique_slot_id_; - OpCode rdma_opcode_; - std::shared_ptr rdma_buffer_; - std::atomic is_finished_; - std::function callback_; + RDMAPrePostQueueElement& operator=(RDMAPrePostQueueElement&& other) noexcept + { + if (this != &other) { + unique_task_id_ = other.unique_task_id_; + rdma_opcode_ = other.rdma_opcode_; + assignment_batch_ = std::move(other.assignment_batch_); + rdma_endpoint_ = std::move(other.rdma_endpoint_); + is_finished_.store(other.is_finished_.load(std::memory_order_relaxed)); + callback_ = std::move(other.callback_); + } + return *this; + } + + uint32_t unique_task_id_; + OpCode rdma_opcode_; + AssignmentBatch assignment_batch_; + + std::shared_ptr rdma_endpoint_; + std::atomic is_finished_; + std::function callback_; }; +class RingBufferReadyManager { +public: + explicit RingBufferReadyManager(size_t size): buffer_size_(size), slots_(size) + { + + for (size_t i = 0; i < buffer_size_; ++i) { + slots_[i].store(false, std::memory_order_relaxed); + } + } + + ~RingBufferReadyManager() = default; + + bool writeSlot(uint32_t idx, bool value, bool wait_if_false = false, int max_wait_attempts = 100) + { + size_t index = idx % buffer_size_; + + if (wait_if_false) { + return writeWithWait(index, value, max_wait_attempts); + } + else { + slots_[index].store(value, std::memory_order_release); + return true; + } + } + + bool readSlot(uint32_t idx) const + { + size_t index = idx % buffer_size_; + return slots_[index].load(std::memory_order_acquire); + } + + bool modifySlot(uint32_t idx, bool new_value, bool expected_value) + { + size_t index = idx % buffer_size_; + + bool expected = expected_value; + return slots_[index].compare_exchange_strong(expected, new_value, std::memory_order_acq_rel); + } + + bool waitForSlot(uint32_t idx, bool desired_value, int max_attempts = 100, int sleep_ms = 1) + { + size_t index = idx % buffer_size_; + + for (int attempt = 0; attempt < max_attempts; ++attempt) { + if (slots_[index].load(std::memory_order_acquire) == desired_value) { + return true; + } + std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); + } + return false; + } + + bool setSlotTrueWithWait(uint32_t idx, int max_wait_attempts = 10) + { + return writeSlot(idx, true, true, max_wait_attempts); + } -struct RDMAPrePostQueueElement -{ - RDMAPrePostQueueElement() = delete; - RDMAPrePostQueueElement(const RDMAPrePostQueueElement&) = delete; - RDMAPrePostQueueElement(uint32_t task_id, OpCode rdma_opcode, std::shared_ptr rdma_endpoint = nullptr); + bool setSlotFalseWithWait(uint32_t task_id, int max_wait_attempts = 10) + { + return writeSlot(task_id, false, true, max_wait_attempts); + } - uint32_t task_id_; - OpCode rdma_opcode_; - AssignmentBatch assignment_batch_; + size_t getBufferSize() const + { + return buffer_size_; + } + size_t getTrueCount() const + { + size_t count = 0; + for (size_t i = 0; i < buffer_size_; ++i) { + if (slots_[i].load(std::memory_order_acquire)) { + ++count; + } + } + return count; + } - std::shared_ptr rdma_endpoint_; - std::atomic is_finished_; - std::function callback_; + void resetAll() + { + for (size_t i = 0; i < buffer_size_; ++i) { + slots_[i].store(false, std::memory_order_relaxed); + } + } -} +private: + bool writeWithWait(size_t index, bool new_value, int max_attempts) + { + bool opposite_value = !new_value; + + for (int attempt = 0; attempt < max_attempts; ++attempt) { + bool current_value = slots_[index].load(std::memory_order_acquire); + + if (current_value == opposite_value) { + slots_[index].store(new_value, std::memory_order_release); + return true; + } + else { + std::this_thread::yield(); + } + } + return false; + } +private: + const size_t buffer_size_; + std::vector> slots_; +}; template -class ProxyQueue -{ +class ProxyQueue { private: - std::queue queue_; - mutable std::mutex mutex_; + std::queue queue_; + mutable std::mutex mutex_; std::condition_variable cv_; -public: + std::atomic stop_flag_{false}; - ProxyQueue() = default; - ProxyQueue(const ProxyQueue&) = delete; +public: + ProxyQueue() = default; + ProxyQueue(const ProxyQueue&) = delete; ProxyQueue& operator=(const ProxyQueue&) = delete; - - void enqueue(T element) + void enqueue(T&& element) { { std::lock_guard lock(mutex_); @@ -154,14 +301,17 @@ class ProxyQueue T dequeue() { std::unique_lock lock(mutex_); - cv_.wait(lock, [this] { return !queue_.empty();}); + cv_.wait(lock, [this] { return !queue_.empty() || stop_flag_.load(); }); + + if (stop_flag_.load()) { + SLIME_LOG_ERROR("STOP"); + } T element = std::move(queue_.front()); queue_.pop(); return element; } - template bool peekQueue(T& element, M&& m) { @@ -174,27 +324,29 @@ class ProxyQueue return false; } - bool fetchQueue(T& element) { std::lock_guard lock(mutex_); if (queue_.empty()) return false; - + element = std::move(queue_.front()); queue_.pop(); return true; } - template - bool fetchQueue(T& element, const std::chrono::duration &time_out) + bool fetchQueue(T& element, const std::chrono::duration& time_out) { std::unique_lock lock(mutex_); - if (!cv_.wait_for(lock, time_out, [this] { return !queue_.empty(); })) - { + if (!cv_.wait_for(lock, time_out, [this] { return !queue_.empty() || stop_flag_.load(); })) { + return false; + } + + if (stop_flag_.load()) { return false; } + element = std::move(queue_.front()); queue_.pop(); return true; @@ -206,52 +358,76 @@ class ProxyQueue return queue_.empty(); } - size_t size() const + size_t size() const { std::lock_guard lock(mutex_); return queue_.size(); } -}; + void notifyAll() + { + { + std::lock_guard lock(mutex_); + stop_flag_.store(true); + } + cv_.notify_all(); + } + void stop() + { + stop_flag_.store(true); + cv_.notify_all(); + } + void restart() + { + stop_flag_.store(false); + } +}; class RDMAEndpoint: public std::enable_shared_from_this { friend class RDMABuffer; public: - RDMAEndpoint(const std::string& dev_name, size_t ib_port, - const std::string& link_type, size_t qp_nums); + RDMAEndpoint(const std::string& dev_name, size_t ib_port, const std::string& link_type, size_t qp_nums); // TODO: 设计聚合多网卡传输的Send Recv + void connect(JSON& data_ctx_info, JSON& meta_ctx_info); + ~RDMAEndpoint(); - void addRDMABuffer(std::shared_ptr rdma_buffer) + void startAllThreads(); + void stopAllThreads(); + void addRDMABuffer(std::shared_ptr rdma_buffer); private: + void metaRecvQueueThread(std::chrono::milliseconds timeout); + void dataRecvQueueThread(std::chrono::milliseconds timeout); - void SendMetaQueueThread(std::chrono::milliseconds timeout); - void RecvDataQueueThread(std::chrono::milliseconds timeout); - void SendBufferQueueThread(std::chrono::milliseconds timeout); - + void WaitSendFinishQueueThread(std::chrono::milliseconds timeout); void WaitRecvFinishQueueThread(std::chrono::milliseconds timeout); - - void postRDMAAssignment(Opcode rdma_opcode); + void addPreQueueElement(OpCode rdma_opcode); + void addRDMABuffer(OpCode rdma_opcode, std::shared_ptr rdma_buffer); + void postMetaWrite(std::shared_ptr rdma_buffer); + void postDataWrite(RDMABufferQueueElement& element, std::shared_ptr rdma_buffer); + void postRDMAAssignment(OpCode rdma_opcode); ProxyQueue meta_recv_queue_; ProxyQueue data_recv_queue_; - + ProxyQueue send_buffer_queue_; ProxyQueue send_finish_queue_; ProxyQueue recv_finish_queue_; + ProxyQueue meta_recv_store_queue_; + std::thread meta_recv_thread_; std::thread data_recv_thread_; @@ -263,81 +439,30 @@ class RDMAEndpoint: public std::enable_shared_from_this { size_t data_ctx_qp_num_; size_t meta_ctx_qp_num_; - std::atomic unique_SEND_SLOT_ID_{RDMAContext::UNDEFINED_IMM_DATA}; - std::atomic unique_RECV_SLOT_ID_{RDMAContext::UNDEFINED_IMM_DATA}; + std::atomic unique_SEND_SLOT_ID_{0}; + std::atomic unique_RECV_SLOT_ID_{0}; - std::vector dum_meta_buffer_; - std::vector dum_data_buffer_; + std::vector dum_meta_buffer_; + std::vector dum_data_buffer_; std::vector meta_buffer_; + std::unordered_map meta_buffer_is_ready_; + mutable std::shared_mutex meta_buffer_mutex_; + std::shared_ptr data_ctx_; std::shared_ptr meta_ctx_; + std::atomic unique_meta_recv_id_{0}; + std::atomic unique_data_recv_id_{0}; + std::atomic stop_SendMetaQueueThread_{false}; + std::atomic stop_dataRecvQueueThread_{false}; + std::atomic stop_SendBufferQueueThread_{false}; + std::atomic stop_WaitSendFinishQueueThread_{false}; + std::atomic stop_WaitRecvFinishQueueThread_{false}; + std::unique_ptr meta_buffer_manager_; + std::unique_ptr data_buffer_manager_; }; - -// struct RDMASendRecvTask { - -// RDMASendRecvTask(std::shared_ptr rdma_endpoint, uint32_t task_id); -// RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rdma_opcode, uint32_t task_id); - -// ~RDMASendRecvTask(); - -// int makeAssignmentBatch(); -// int makeMetaMR(); -// int makeDataMR(); -// int makeRemoteDataMR(); -// int makeDummyAssignmentBatch(); - -// void configurationTask(std::shared_ptr rdma_buffer, uint32_t unique_slot_id, OpCode rmda_opcode); - -// uint32_t task_id_; -// uint32_t unique_slot_id_; -// OpCode rdma_operation_; - -// std::shared_ptr rdma_buffer_; -// std::shared_ptr rdma_endpoint_; -// AssignmentBatch meta_assignment_batch_; -// AssignmentBatch dum_meta_assignment_batch_; -// AssignmentBatch data_assignment_batch_; -// AssignmentBatch dum_data_assignment_batch_; - -// }; - - // json dataCtxInfo() const - // { - // return data_ctx_->endpoint_info(); - // } - - // json metaCtxInfo() const - // { - // return meta_ctx_->endpoint_info(); - // } - - // std::shared_ptr dataCtx() - // { - // return data_ctx_; - // } - - // std::shared_ptr metaCtx() - // { - // return meta_ctx_; - // } - - // std::vector& metaBuffer() - // { - // return meta_buffer_; - // } - - // int metaCtxQPNum() - // { - // return meta_ctx_qp_num_; - // } - // int dataCtxQPNum() - // { - // return data_ctx_qp_num_; - // } - } // namespace slime From b32b1acfd83776acb6dd1e95ec9ca53328cde418 Mon Sep 17 00:00:00 2001 From: HaoLiuuu Date: Wed, 5 Nov 2025 22:53:53 +0800 Subject: [PATCH 9/9] preliminary complete implementation --- csrc/engine/rdma/memory_pool.cpp | 5 +- csrc/engine/rdma/memory_pool.h | 4 +- csrc/engine/rdma/rdma_assignment.h | 3 +- csrc/engine/rdma/rdma_buffer.cpp | 9 +- csrc/engine/rdma/rdma_buffer.h | 63 ++- csrc/engine/rdma/rdma_context.cpp | 2 - csrc/engine/rdma/rdma_endpoint.cpp | 762 ++++++++++++----------------- csrc/engine/rdma/rdma_endpoint.h | 446 +++++++---------- csrc/engine/rdma/rdma_env.h | 4 +- tests/cpp/recv_test.cpp | 168 +++++-- tests/cpp/send_test.cpp | 136 ++++- 11 files changed, 781 insertions(+), 821 deletions(-) diff --git a/csrc/engine/rdma/memory_pool.cpp b/csrc/engine/rdma/memory_pool.cpp index f78d494..a78d436 100644 --- a/csrc/engine/rdma/memory_pool.cpp +++ b/csrc/engine/rdma/memory_pool.cpp @@ -20,7 +20,10 @@ int RDMAMemoryPool::register_memory_region(const std::string& mr_key, uintptr_t /* MemoryRegion Access Right = 777 */ const static int access_rights = IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE | IBV_ACCESS_REMOTE_READ; ibv_mr* mr = ibv_reg_mr(pd_, (void*)data_ptr, length, access_rights); - + // std::cout << "length: " << length << std::endl; + // std::cout << "data_ptr " << data_ptr << std::endl; + // std::cout << "mr_key " << mr_key << std::endl; + // std::cout << "mr:" << mr << std::endl; SLIME_ASSERT(mr, " Failed to register memory " << data_ptr); SLIME_LOG_DEBUG("Memory region: " << mr_key << ", " << (void*)data_ptr << " -- " << (void*)(data_ptr + length) diff --git a/csrc/engine/rdma/memory_pool.h b/csrc/engine/rdma/memory_pool.h index 1419038..e2e73b2 100644 --- a/csrc/engine/rdma/memory_pool.h +++ b/csrc/engine/rdma/memory_pool.h @@ -23,8 +23,8 @@ typedef struct remote_mr { remote_mr(uintptr_t addr, size_t length, uint32_t rkey): addr(addr), length(length), rkey(rkey) {} uintptr_t addr{(uintptr_t) nullptr}; - size_t length{}; - uint32_t rkey{}; + size_t length{0}; + uint32_t rkey{0}; } remote_mr_t; class RDMAMemoryPool { diff --git a/csrc/engine/rdma/rdma_assignment.h b/csrc/engine/rdma/rdma_assignment.h index e443ac0..0ec943c 100644 --- a/csrc/engine/rdma/rdma_assignment.h +++ b/csrc/engine/rdma/rdma_assignment.h @@ -43,8 +43,9 @@ typedef struct callback_info { callback_info() = default; callback_info(OpCode opcode, size_t batch_size, callback_fn_t callback): opcode_(opcode), batch_size_(batch_size) { - if (callback) + if (callback) { callback_ = std::move(callback); + } } callback_fn_t callback_{[this](int code, int imm_data) { diff --git a/csrc/engine/rdma/rdma_buffer.cpp b/csrc/engine/rdma/rdma_buffer.cpp index ae0b532..2154eeb 100644 --- a/csrc/engine/rdma/rdma_buffer.cpp +++ b/csrc/engine/rdma/rdma_buffer.cpp @@ -1,26 +1,27 @@ #include "engine/rdma/rdma_buffer.h" +#include "engine/assignment.h" namespace slime { void RDMABuffer::send() { - endpoint_->addSendTask(shared_from_this()); + endpoint_->addRDMABuffer(OpCode::SEND, shared_from_this()); } void RDMABuffer::recv() { - endpoint_->addRecvTask(shared_from_this()); + endpoint_->addRDMABuffer(OpCode::RECV, shared_from_this()); } -void RDMABuffer::send_done_callback() +void RDMABuffer::sendDoneCallback() { std::unique_lock lock(send_mutex_); ++send_completed_; send_cv_.notify_all(); } -void RDMABuffer::recv_done_callback() +void RDMABuffer::recvDoneCallback() { std::unique_lock lock(recv_mutex_); ++recv_completed_; diff --git a/csrc/engine/rdma/rdma_buffer.h b/csrc/engine/rdma/rdma_buffer.h index f90482a..a67c4ba 100644 --- a/csrc/engine/rdma/rdma_buffer.h +++ b/csrc/engine/rdma/rdma_buffer.h @@ -4,6 +4,7 @@ #include "engine/rdma/rdma_endpoint.h" #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include +#include "logging.h" #include "rdma_common.h" namespace slime { @@ -23,28 +25,35 @@ class RDMABuffer: public std::enable_shared_from_this { friend class RDMAEndpoint; public: - RDMABuffer(std::shared_ptr endpoint, storage_view_batch_t& batch): - endpoint_(endpoint), storage_view_batch_(std::move(batch)) + RDMABuffer(std::shared_ptr endpoint, uintptr_t ptr, size_t offset, size_t data_size): + endpoint_(endpoint), ptr_(ptr), offset_(offset), data_size_(data_size) { + SLIME_LOG_DEBUG("New RDMABuffer and the Index of Current Buffer is: ", buffer_counter_); + buffer_counter_ += 1; } - RDMABuffer(std::shared_ptr endpoint, - std::vector ptrs, - std::vector offset, - std::vector data_size) + RDMABuffer(std::shared_ptr endpoint, storage_view_batch_t& batch): + endpoint_(endpoint), storage_view_batch_(std::move(batch)) { - - batch_size_ = ptrs.size(); - ptrs_ = ptrs; - offset_ = offset; - data_size_ = data_size; - for (uint32_t i = 0; i < batch_size_; ++i) { - storage_view_t view{.data_ptr = ptrs[i], .storage_offset = offset[i], .length = data_size[i]}; - storage_view_batch_.push_back(view); - } - endpoint_ = endpoint; } + // RDMABuffer(std::shared_ptr endpoint, + // std::vector ptrs, + // std::vector offset, + // std::vector data_size) + // { + + // batch_size_ = ptrs.size(); + // ptrs_ = ptrs; + // offset_ = offset; + // data_size_ = data_size; + // for (uint32_t i = 0; i < batch_size_; ++i) { + // storage_view_t view{.data_ptr = ptrs[i], .storage_offset = offset[i], .length = data_size[i]}; + // storage_view_batch_.push_back(view); + // } + // endpoint_ = endpoint; + // } + ~RDMABuffer() = default; const size_t batch_size() @@ -63,20 +72,20 @@ class RDMABuffer: public std::enable_shared_from_this { bool waitSend(); bool waitRecv(); - void send_done_callback(); - void recv_done_callback(); - - void get_time(); + void sendDoneCallback(); + void recvDoneCallback(); private: - storage_view_batch_t storage_view_batch_; - std::shared_ptr endpoint_; - std::vector ptrs_; - std::vector offset_; - std::vector data_size_; + uintptr_t ptr_; + size_t offset_; + size_t data_size_; - size_t batch_size_; + std::vector ptrs_batch_; + std::vector offset_batch_; + std::vector data_size_batch_; + + storage_view_batch_t storage_view_batch_; std::atomic send_pending_{0}; std::atomic recv_pending_{0}; @@ -89,6 +98,8 @@ class RDMABuffer: public std::enable_shared_from_this { std::mutex send_mutex_; std::mutex recv_mutex_; + + static inline size_t buffer_counter_{0}; }; } // namespace slime diff --git a/csrc/engine/rdma/rdma_context.cpp b/csrc/engine/rdma/rdma_context.cpp index f60357e..4c80f30 100644 --- a/csrc/engine/rdma/rdma_context.cpp +++ b/csrc/engine/rdma/rdma_context.cpp @@ -389,7 +389,6 @@ void split_assign_by_max_length(OpCode opcode, AssignmentBatch& batch_split_after_max_length, size_t max_length) { - // split assignment by length for (size_t i = 0; i < batch.size(); ++i) { if (batch[i].length < max_length) { batch_split_after_max_length.push_back(std::move(batch[i])); @@ -434,7 +433,6 @@ RDMAContext::submit(OpCode opcode, AssignmentBatch& batch, callback_fn_t callbac size_t length = SLIME_MAX_LENGTH_PER_ASSIGNMENT; AssignmentBatch batch_split; split_assign_by_max_length(opcode, batch, batch_split, length); - AssignmentBatch batch_after_agg_qp; while (batch_split.size() < SLIME_AGG_QP_NUM) { length = length / 2; diff --git a/csrc/engine/rdma/rdma_endpoint.cpp b/csrc/engine/rdma/rdma_endpoint.cpp index b152c98..c7bd198 100644 --- a/csrc/engine/rdma/rdma_endpoint.cpp +++ b/csrc/engine/rdma/rdma_endpoint.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -19,6 +20,8 @@ namespace slime { RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, size_t ib_port, const std::string& link_type, size_t qp_nums) { + + // RDMAContext: submit the RDMA assignment SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); data_ctx_ = std::make_shared(qp_nums, 0); meta_ctx_ = std::make_shared(1, 0); @@ -33,119 +36,120 @@ RDMAEndpoint::RDMAEndpoint(const std::string& dev_name, size_t ib_port, const st SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); SLIME_LOG_INFO("RDMA Endpoint Init Success..."); - SLIME_LOG_INFO("Allocate The MR Buffer and Dummy Buffer"); - constexpr size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; - meta_buffer_.reserve(max_meta_buffer_size); + SLIME_LOG_INFO("Allocate MR Buffer and Dummy Buffer"); + meta_buffer_.resize(SLIME_META_BUFFER_SIZE * 2); memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); meta_ctx_->register_memory_region( - "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * max_meta_buffer_size); + "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * meta_buffer_.size()); - constexpr size_t max_dum_buffer_size = 16; - dum_meta_buffer_.reserve(max_dum_buffer_size); - meta_ctx_->register_memory_region("dum_meta_buffer_", + dum_meta_buffer_.resize(SLIME_DUMMY_BUFFER_SIZE); + memset(dum_meta_buffer_.data(), 0, dum_meta_buffer_.size() * sizeof(uint32_t)); + meta_ctx_->register_memory_region("dum_meta_buffer", reinterpret_cast(dum_meta_buffer_.data()), sizeof(uint32_t) * dum_meta_buffer_.size()); - dum_data_buffer_.reserve(max_dum_buffer_size); - data_ctx_->register_memory_region("dum_data_buffer_", + + dum_data_buffer_.resize(SLIME_DUMMY_BUFFER_SIZE); + memset(dum_data_buffer_.data(), 0, dum_data_buffer_.size() * sizeof(uint32_t)); + data_ctx_->register_memory_region("dum_data_buffer", reinterpret_cast(dum_data_buffer_.data()), sizeof(uint32_t) * dum_data_buffer_.size()); - SLIME_LOG_INFO("The MR Buffer and Dummy Buffer Construct Success..."); + SLIME_LOG_INFO("The MR Buffer and Dummy Buffer Allocate Success..."); SLIME_LOG_INFO("Construct the pre-submit queue"); - - for (size_t i = 0; i < MAX_QUEUE_SIZE; i += 1) { - addPreQueueElement(OpCode::SEND); - addPreQueueElement(OpCode::RECV); - } - - meta_buffer_manager_ = std::make_unique(MAX_META_BUFFER_SIZE); - data_buffer_manager_ = std::make_unique(MAX_META_BUFFER_SIZE); + meta_slots_manager_ = std::make_unique(SLIME_STATUS_SLOT_SIZE); + data_slots_manager_ = std::make_unique(SLIME_STATUS_SLOT_SIZE); } -void RDMAEndpoint::connect(JSON& data_ctx_info, JSON& meta_ctx_info) +void RDMAEndpoint::connect(const json& data_ctx_info, const json& meta_ctx_info) { + SLIME_LOG_INFO("Lauch the RDMAConstex for DATA and META") data_ctx_->connect(data_ctx_info); meta_ctx_->connect(meta_ctx_info); data_ctx_->launch_future(); meta_ctx_->launch_future(); + + for (size_t i = 0; i < SLIME_STATUS_SLOT_SIZE; i += 1) { + addPreQueueElement(OpCode::SEND); + addPreQueueElement(OpCode::RECV); + } + proxyInit(); } -void RDMAEndpoint::startAllThreads() +void RDMAEndpoint::proxyInit() { SLIME_LOG_INFO("Starting all RDMA endpoint threads..."); - stop_SendMetaQueueThread_.store(false, std::memory_order_release); - stop_dataRecvQueueThread_.store(false, std::memory_order_release); - stop_SendBufferQueueThread_.store(false, std::memory_order_release); - stop_WaitSendFinishQueueThread_.store(false, std::memory_order_release); - stop_WaitRecvFinishQueueThread_.store(false, std::memory_order_release); - - // 时间根据需要再调整 - meta_recv_thread_ = std::thread([this]() { this->metaRecvQueueThread(std::chrono::milliseconds(1)); }); - - data_recv_thread_ = std::thread([this]() { this->dataRecvQueueThread(std::chrono::milliseconds(1)); }); - - send_buffer_thread_ = std::thread([this]() { this->SendBufferQueueThread(std::chrono::milliseconds(1)); }); - - send_finish_thread_ = std::thread([this]() { this->WaitSendFinishQueueThread(std::chrono::milliseconds(1)); }); - - recv_finish_thread_ = std::thread([this]() { this->WaitRecvFinishQueueThread(std::chrono::milliseconds(1)); }); - - SLIME_LOG_INFO("All 5 RDMA endpoint threads started successfully"); + stop_meta_recv_queue_thread_.store(false, std::memory_order_release); + stop_data_recv_queue_thread_.store(false, std::memory_order_release); + stop_send_buffer_queue_thread_.store(false, std::memory_order_release); + stop_recv_buffer_queue_thread_.store(false, std::memory_order_release); + stop_wait_send_finish_queue_thread_.store(false, std::memory_order_release); + // stop_wait_recv_finish_queue_thread_.store(false, std::memory_order_release); + + meta_recv_thread_ = std::thread([this]() { this->metaRecvQueueThread(std::chrono::milliseconds(0)); }); + data_recv_thread_ = std::thread([this]() { this->dataRecvQueueThread(std::chrono::milliseconds(0)); }); + send_buffer_thread_ = std::thread([this]() { this->SendBufferQueueThread(std::chrono::milliseconds(0)); }); + recv_buffer_thread_ = std::thread([this]() { this->RecvBufferQueueThread(std::chrono::milliseconds(0)); }); + send_finish_thread_ = std::thread([this]() { this->SendFinishQueueThread(std::chrono::milliseconds(0)); }); + // recv_finish_thread_ = std::thread([this]() { this->RecvFinishQueueThread(std::chrono::milliseconds(0)); }); + + SLIME_LOG_INFO("All Proxy Threads Started Successfully"); } -void RDMAEndpoint::stopAllThreads() +void RDMAEndpoint::proxyDestroy() { SLIME_LOG_INFO("Stopping all RDMA endpoint threads..."); - stop_SendMetaQueueThread_.store(true, std::memory_order_release); - stop_dataRecvQueueThread_.store(true, std::memory_order_release); - stop_SendBufferQueueThread_.store(true, std::memory_order_release); - stop_WaitSendFinishQueueThread_.store(true, std::memory_order_release); - stop_WaitRecvFinishQueueThread_.store(true, std::memory_order_release); - - meta_recv_queue_.notifyAll(); - data_recv_queue_.notifyAll(); - send_buffer_queue_.notifyAll(); - send_finish_queue_.notifyAll(); - recv_finish_queue_.notifyAll(); + stop_meta_recv_queue_thread_.store(true, std::memory_order_release); + stop_data_recv_queue_thread_.store(true, std::memory_order_release); + stop_send_buffer_queue_thread_.store(true, std::memory_order_release); + stop_recv_buffer_queue_thread_.store(true, std::memory_order_release); + stop_wait_send_finish_queue_thread_.store(true, std::memory_order_release); + // stop_wait_recv_finish_queue_thread_.store(true, std::memory_order_release); if (meta_recv_thread_.joinable()) { meta_recv_thread_.join(); - SLIME_LOG_DEBUG("Meta recv thread stopped"); + SLIME_LOG_INFO("Meta recv thread stopped"); } if (data_recv_thread_.joinable()) { data_recv_thread_.join(); - SLIME_LOG_DEBUG("Data recv thread stopped"); + SLIME_LOG_INFO("Data recv thread stopped"); } if (send_buffer_thread_.joinable()) { send_buffer_thread_.join(); - SLIME_LOG_DEBUG("Send buffer thread stopped"); + SLIME_LOG_INFO("Send buffer thread stopped"); + } + + if (recv_buffer_thread_.joinable()) { + recv_buffer_thread_.join(); + SLIME_LOG_INFO("Recv buffer thread stopped"); } if (send_finish_thread_.joinable()) { send_finish_thread_.join(); - SLIME_LOG_DEBUG("Send finish thread stopped"); + SLIME_LOG_INFO("Send finish thread stopped"); } - if (recv_finish_thread_.joinable()) { - recv_finish_thread_.join(); - SLIME_LOG_DEBUG("Recv finish thread stopped"); - } + // if (recv_finish_thread_.joinable()) { + // recv_finish_thread_.join(); + // SLIME_LOG_INFO("Recv finish thread stopped"); + // } - SLIME_LOG_INFO("All RDMA endpoint threads stopped successfully"); + SLIME_LOG_INFO("All Proxy Threads Stopped Successfully"); } RDMAEndpoint::~RDMAEndpoint() { try { - stopAllThreads(); + proxyDestroy(); SLIME_LOG_INFO("RDMAEndpoint destroyed successfully"); + data_ctx_->stop_future(); + meta_ctx_->stop_future(); } catch (const std::exception& e) { SLIME_LOG_ERROR("Exception in RDMAEndpoint destructor: ", e.what()); @@ -155,30 +159,37 @@ RDMAEndpoint::~RDMAEndpoint() void RDMAEndpoint::addPreQueueElement(OpCode rdma_opcode) { if (rdma_opcode == OpCode::SEND) { + RDMAPrePostQueueElement meta_recv_queue_element = + RDMAPrePostQueueElement(unique_meta_recv_id_.load(std::memory_order_relaxed), OpCode::SEND); + uint32_t idx = meta_recv_queue_element.unique_id_; + auto is_finish_ptr = meta_recv_queue_element.is_finished_ptr_; - RDMAPrePostQueueElement meta_recv_queue_element = RDMAPrePostQueueElement( - unique_meta_recv_id_.fetch_add(1, std::memory_order_relaxed), OpCode::SEND, shared_from_this()); + auto meta_recv_callback = [idx, is_finish_ptr](int status, int slot_id) mutable { + is_finish_ptr->store(true, std::memory_order_release); + }; - meta_ctx_->submit(OpCode::RECV, - meta_recv_queue_element.assignment_batch_, - meta_recv_queue_element.callback_, - RDMAContext::UNDEFINED_QPI, - meta_recv_queue_element.unique_task_id_); + AssignmentBatch assignment_batch_ = AssignmentBatch{Assignment("dum_meta_buffer", 0, 0, 16 * sizeof(uint32_t))}; + meta_ctx_->submit(OpCode::RECV, assignment_batch_, meta_recv_callback, RDMAContext::UNDEFINED_QPI, idx); - meta_recv_queue_.enqueue(std::move(meta_recv_queue_element)); + meta_recv_queue_.enqueue(meta_recv_queue_element); + unique_meta_recv_id_.fetch_add(1, std::memory_order_relaxed); } else if (rdma_opcode == OpCode::RECV) { - RDMAPrePostQueueElement data_recv_queue_element = RDMAPrePostQueueElement( - unique_data_recv_id_.fetch_add(1, std::memory_order_relaxed), OpCode::RECV, shared_from_this()); + RDMAPrePostQueueElement data_recv_queue_element = + RDMAPrePostQueueElement(unique_data_recv_id_.load(std::memory_order_relaxed), OpCode::RECV); - data_ctx_->submit(OpCode::RECV, - data_recv_queue_element.assignment_batch_, - data_recv_queue_element.callback_, - RDMAContext::UNDEFINED_QPI, - data_recv_queue_element.unique_task_id_); + uint32_t idx = data_recv_queue_element.unique_id_; + auto is_finish_ptr = data_recv_queue_element.is_finished_ptr_; + auto data_recv_callback = [idx, is_finish_ptr](int status, int slot_id) mutable { + is_finish_ptr->store(true, std::memory_order_release); + }; + AssignmentBatch assignment_batch_ = AssignmentBatch{Assignment("dum_data_buffer", 0, 0, 16 * sizeof(uint32_t))}; + data_ctx_->submit(OpCode::RECV, assignment_batch_, data_recv_callback, RDMAContext::UNDEFINED_QPI, idx); - data_recv_queue_.enqueue(std::move(data_recv_queue_element)); + data_recv_queue_.enqueue(data_recv_queue_element); + data_slots_manager_->acquireSlot(idx); + unique_data_recv_id_.fetch_add(1, std::memory_order_relaxed); } else { SLIME_LOG_ERROR("Undefined OPCODE IN RDMAEndpoint::addPreQueueElement"); @@ -187,86 +198,132 @@ void RDMAEndpoint::addPreQueueElement(OpCode rdma_opcode) void RDMAEndpoint::addRDMABuffer(OpCode rdma_opcode, std::shared_ptr rdma_buffer) { - if (rdma_opcode == OpCode::SEND) { - RDMABufferQueueElement buffer_element = RDMABufferQueueElement( - unique_SEND_SLOT_ID_.fetch_add(1, std::memory_order_relaxed), OpCode::SEND, rdma_buffer); - send_buffer_queue_.enqueue(std::move(buffer_element)); + if (rdma_opcode == OpCode::SEND) { + uint32_t cur_idx = unique_SEND_SLOT_ID_.load(std::memory_order_relaxed); + RDMABufferQueueElement buffer_element = RDMABufferQueueElement(cur_idx, OpCode::SEND, rdma_buffer); + send_buffer_queue_.enqueue(buffer_element); + unique_SEND_SLOT_ID_.fetch_add(1, std::memory_order_relaxed); } else if (rdma_opcode == OpCode::RECV) { - postMetaWrite(rdma_buffer); - - RDMABufferQueueElement buffer_element = RDMABufferQueueElement( - unique_RECV_SLOT_ID_.fetch_add(1, std::memory_order_relaxed), OpCode::RECV, rdma_buffer); - - recv_finish_queue_.enqueue(std::move(buffer_element)); + uint32_t cur_idx = unique_RECV_SLOT_ID_.load(std::memory_order_relaxed); + RDMABufferQueueElement buffer_element = RDMABufferQueueElement(cur_idx, OpCode::RECV, rdma_buffer); + recv_buffer_queue_.enqueue(buffer_element); + unique_RECV_SLOT_ID_.fetch_add(1, std::memory_order_relaxed); } else { SLIME_LOG_ERROR("Undefined OPCODE IN RDMAEndpoint::addRDMABuffer"); } } -void RDMAEndpoint::postMetaWrite(std::shared_ptr rdma_buffer) +void RDMAEndpoint::postMetaWrite(uint32_t idx, std::shared_ptr rdma_buffer) { - std::string prefix = "DATA_RECV_"; - std::string MR_KEY = prefix + std::to_string(unique_RECV_SLOT_ID_) + "_"; - auto mr = data_ctx_->get_mr(MR_KEY + std::to_string(0)); + std::string prefix = "DATA_RECV_"; + std::string MR_KEY = prefix + std::to_string(idx); + auto mr_is_exist = data_ctx_->get_mr(MR_KEY); - if (mr != nullptr) { - SLIME_LOG_DEBUG("The DATA MR has been REGISTERED! The SLOT_ID is: ", unique_RECV_SLOT_ID_); + if (mr_is_exist != nullptr) { + SLIME_LOG_DEBUG("The RECV DATA MR has been REGISTERED! The SLOT_ID is: ", idx); } else { - auto view_batch = rdma_buffer->view_batch(); - for (size_t i = 0; i < rdma_buffer->batch_size(); ++i) { - data_ctx_->register_memory_region(MR_KEY + std::to_string(i), view_batch[i].data_ptr, view_batch[i].length); - } + data_ctx_->register_memory_region(MR_KEY, rdma_buffer->ptr_, rdma_buffer->data_size_); } - for (size_t i = 0; i < rdma_buffer->batch_size(); i += 1) { - auto mr = data_ctx_->get_mr(MR_KEY + std::to_string(i)); - meta_buffer_[MAX_META_BUFFER_SIZE + unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_addr[i] = - reinterpret_cast(mr->addr); - meta_buffer_[MAX_META_BUFFER_SIZE + unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; - meta_buffer_[MAX_META_BUFFER_SIZE + unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_size[i] = mr->length; - meta_buffer_[MAX_META_BUFFER_SIZE + unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_slot = unique_RECV_SLOT_ID_; - } + auto mr = data_ctx_->get_mr(MR_KEY); - size_t meta_buffer_idx = unique_RECV_SLOT_ID_ % MAX_META_BUFFER_SIZE; + meta_buffer_[SLIME_META_BUFFER_SIZE + idx % SLIME_META_BUFFER_SIZE].mr_addr = reinterpret_cast(mr->addr); + meta_buffer_[SLIME_META_BUFFER_SIZE + idx % SLIME_META_BUFFER_SIZE].mr_rkey = mr->rkey; + meta_buffer_[SLIME_META_BUFFER_SIZE + idx % SLIME_META_BUFFER_SIZE].mr_size = mr->length; + meta_buffer_[SLIME_META_BUFFER_SIZE + idx % SLIME_META_BUFFER_SIZE].mr_slot = idx; + + size_t meta_buffer_idx = idx % SLIME_META_BUFFER_SIZE; AssignmentBatch meta_assignment_batch_ = AssignmentBatch{Assignment("meta_buffer", meta_buffer_idx * sizeof(meta_data_t), - MAX_META_BUFFER_SIZE * sizeof(meta_data_t) + meta_buffer_idx * sizeof(meta_data_t), + SLIME_META_BUFFER_SIZE * sizeof(meta_data_t) + meta_buffer_idx * sizeof(meta_data_t), sizeof(meta_data_t))}; - auto meta_callback = [this](int status, int slot_id) mutable { std::cout << "META WRITE SUCCESS"; }; - meta_ctx_->submit(OpCode::WRITE_WITH_IMM, - meta_assignment_batch_, - meta_callback, - RDMAContext::UNDEFINED_QPI, - unique_RECV_SLOT_ID_); + auto meta_callback = [idx](int status, int slot_id) { + SLIME_LOG_DEBUG("The META RECV IS SUCCESS FOR SLOT: ", idx); + }; + meta_ctx_->submit(OpCode::WRITE_WITH_IMM, meta_assignment_batch_, meta_callback, RDMAContext::UNDEFINED_QPI, idx); } -void RDMAEndpoint::metaRecvQueueThread(std::chrono::milliseconds timeout) +void RDMAEndpoint::postDataWrite(RDMABufferQueueElement& element, std::shared_ptr rdma_buffer) { - SLIME_LOG_INFO("metaRecvQueueThread started"); + uint64_t addr; + uint32_t size; + uint32_t rkey; + uint32_t idx = element.unique_id_; + std::string prefix = "DATA_SEND_"; + std::string MR_KEY = prefix + std::to_string(idx); + auto mr_is_exist = data_ctx_->get_mr(MR_KEY); + if (mr_is_exist != nullptr) { + SLIME_LOG_DEBUG("The SEND DATA MR has been REGISTERED! The SLOT_ID is: ", unique_SEND_SLOT_ID_); + } + else { + data_ctx_->register_memory_region(MR_KEY, rdma_buffer->ptr_, rdma_buffer->data_size_); + } + + auto mr_remote = data_ctx_->get_remote_mr(MR_KEY); + if (mr_remote.rkey == 0) { + + addr = meta_buffer_[idx % SLIME_META_BUFFER_SIZE].mr_addr; + size = meta_buffer_[idx % SLIME_META_BUFFER_SIZE].mr_size; + rkey = meta_buffer_[idx % SLIME_META_BUFFER_SIZE].mr_rkey; + data_ctx_->register_remote_memory_region(MR_KEY, addr, size, rkey); + } - while (!stop_SendMetaQueueThread_.load(std::memory_order_acquire)) { + else { + SLIME_LOG_DEBUG("The REMOTE DATA MR has been REGISTERED! The SLOT_ID is: ", unique_SEND_SLOT_ID_); + } - RDMAPrePostQueueElement element; + AssignmentBatch batch = AssignmentBatch{Assignment(MR_KEY, 0, 0, size)}; - bool found = meta_recv_queue_.peekQueue(element, [](const RDMAPrePostQueueElement& elem) -> bool { - return elem.is_finished_.load(std::memory_order_acquire); - }); + std::cout << "POST DATA WRITE" << std::endl; + std::cout << "addr: " << addr << std::endl; + std::cout << "size: " << size << std::endl; + std::cout << "rkey: " << rkey << std::endl; - if (found) { - if (meta_buffer_manager_->setSlotTrueWithWait(element.unique_task_id_)) { - SLIME_LOG_DEBUG("Set slot {} to true for task {}", - element.unique_task_id_ % MAX_META_BUFFER_SIZE, - element.unique_task_id_); - } - else { - SLIME_LOG_WARN("Failed to set slot for task {} - timeout", element.unique_task_id_); + auto is_finish_ptr = element.is_finished_ptr_; + is_finish_ptr->store(false, std::memory_order_release); + auto data_write_callback = [idx, is_finish_ptr](int status, int slot_id) mutable { + is_finish_ptr->store(true, std::memory_order_release); + }; + + data_ctx_->submit(OpCode::WRITE_WITH_IMM, batch, data_write_callback, RDMAContext::UNDEFINED_QPI, idx); +} + +void RDMAEndpoint::metaRecvQueueThread(std::chrono::milliseconds timeout) +{ + SLIME_LOG_INFO("metaRecvQueueThread started (timeout={}ms)", timeout.count()); + + while (!stop_meta_recv_queue_thread_.load(std::memory_order_acquire)) { + uint32_t idx; + if (meta_recv_queue_.getFrontTaskId(idx)) { + if (meta_slots_manager_->checkSlotAvailable(idx)) { + bool found = meta_recv_queue_.peekQueue( + idx, [](const auto& e) { return e.is_finished_ptr_->load(std::memory_order_acquire); }); + if (found) { + if (meta_slots_manager_->acquireSlot(idx)) { + if (meta_recv_queue_.popQueue()) { + SLIME_LOG_DEBUG("SUCCESS to set META RECV RING SLOT status of task id ", + idx, + " and the slot id ", + idx % SLIME_STATUS_SLOT_SIZE); + } + else { + SLIME_LOG_ERROR("The META Queue Has no element"); + } + } + else { + SLIME_LOG_ERROR("FAIL to set META RECV RING SLOT status of task id ", + idx, + " and the slot id ", + idx % SLIME_STATUS_SLOT_SIZE); + } + } } } else { @@ -278,30 +335,34 @@ void RDMAEndpoint::metaRecvQueueThread(std::chrono::milliseconds timeout) } } } - SLIME_LOG_INFO("metaRecvQueueThread stopped"); } void RDMAEndpoint::dataRecvQueueThread(std::chrono::milliseconds timeout) { - SLIME_LOG_INFO("RecvDataQueueThread started"); - while (!stop_dataRecvQueueThread_.load(std::memory_order_acquire)) { + SLIME_LOG_INFO("RecvDataQueueThread started (timeout={}ms)", timeout.count()); - RDMAPrePostQueueElement element; + while (!stop_data_recv_queue_thread_.load(std::memory_order_acquire)) { + uint32_t idx; + if (data_recv_queue_.getFrontTaskId(idx)) { - bool found = data_recv_queue_.peekQueue(element, [](const RDMAPrePostQueueElement& elem) -> bool { - return elem.is_finished_.load(std::memory_order_acquire); - }); - - if (found) { - - if (data_buffer_manager_->setSlotTrueWithWait(element.unique_task_id_)) { - SLIME_LOG_DEBUG("Set slot {} to true for task {}", - element.unique_task_id_ % MAX_META_BUFFER_SIZE, - element.unique_task_id_); - } - else { - SLIME_LOG_WARN("Failed to set slot for task {} - timeout", element.unique_task_id_); + bool found = data_recv_queue_.peekQueue( + idx, [](const auto& e) { return e.is_finished_ptr_->load(std::memory_order_acquire); }); + if (found) { + RDMABufferQueueElement element = recv_buffer_mapping_[idx]; + element.rdma_buffer_->recvDoneCallback(); + + if (data_recv_queue_.popQueue()) { + recv_buffer_mapping_.erase(idx); + addPreQueueElement(OpCode::RECV); + SLIME_LOG_DEBUG("SUCCESS to set DATA RECV RING SLOT status of task id ", + idx, + " and the slot id ", + idx % SLIME_STATUS_SLOT_SIZE); + } + else { + SLIME_LOG_ERROR("The DATA RECV Queue Has no element"); + } } } else { @@ -318,34 +379,26 @@ void RDMAEndpoint::dataRecvQueueThread(std::chrono::milliseconds timeout) void RDMAEndpoint::SendBufferQueueThread(std::chrono::milliseconds timeout) { - SLIME_LOG_INFO("SendBufferQueueThread started"); - - while (!stop_SendBufferQueueThread_.load(std::memory_order_acquire)) { - RDMABufferQueueElement element; - - bool has_element = send_buffer_queue_.fetchQueue(element); - - if (has_element) { - - bool is_ready = meta_buffer_manager_->readSlot(element.unique_slot_id_); - - if (is_ready) { - - postDataWrite(element, element.rdma_buffer_); - send_finish_queue_.enqueue(std::move(element)); - SLIME_LOG_DEBUG("Processing send buffer element {}, meta buffer is ready", element.unique_slot_id_); - } - else { - - SLIME_LOG_DEBUG("Meta buffer not ready for element {}, re-enqueueing", element.unique_slot_id_); - - send_buffer_queue_.enqueue(std::move(element)); - - if (timeout.count() > 0) { - std::this_thread::sleep_for(timeout); + SLIME_LOG_INFO("SendBufferQueueThread started (timeout={}ms)", timeout.count()); + + while (!stop_send_buffer_queue_thread_.load(std::memory_order_acquire)) { + + uint32_t idx; + if (send_buffer_queue_.getFrontTaskId(idx)) { + if (meta_slots_manager_->checkSlotReady(idx)) { + RDMABufferQueueElement element; + if (send_buffer_queue_.fetchQueue(element)) { + postDataWrite(element, element.rdma_buffer_); + send_finish_queue_.enqueue(element); + if (meta_slots_manager_->releaseSlot(idx)) { + addPreQueueElement(OpCode::SEND); + } + else { + SLIME_LOG_ERROR("FAIL to release meta_slots_manager_"); + } } else { - std::this_thread::yield(); + SLIME_LOG_ERROR("FAIL to fetch the element in send_buffer_queue_"); } } } @@ -358,76 +411,31 @@ void RDMAEndpoint::SendBufferQueueThread(std::chrono::milliseconds timeout) } } } - SLIME_LOG_INFO("SendBufferQueueThread stopped"); } -void RDMAEndpoint::postDataWrite(RDMABufferQueueElement& element, std::shared_ptr rdma_buffer) -{ - std::string prefix = "DATA_SEND_"; - std::string MR_KEY = prefix + std::to_string(unique_SEND_SLOT_ID_) + "_"; - auto mr = data_ctx_->get_mr(MR_KEY + std::to_string(0)); - if (mr != nullptr) { - SLIME_LOG_DEBUG("The DATA MR has been REGISTERED! The SLOT_ID is: ", unique_SEND_SLOT_ID_); - } - else { - auto view_batch = rdma_buffer->view_batch(); - for (size_t i = 0; i < rdma_buffer->batch_size(); ++i) { - std::cout << view_batch[i].data_ptr << std::endl; - data_ctx_->register_memory_region(MR_KEY + std::to_string(i), view_batch[i].data_ptr, view_batch[i].length); - } - } - - auto mr_remote = data_ctx_->get_remote_mr(MR_KEY + std::to_string(0)); - if (mr_remote.addr == 0) { - - for (size_t i = 0; i < rdma_buffer->batch_size(); ++i) { - uint64_t addr = meta_buffer_[unique_SEND_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_addr[i]; - uint32_t size = meta_buffer_[unique_SEND_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_size[i]; - uint32_t rkey = meta_buffer_[unique_SEND_SLOT_ID_ % MAX_META_BUFFER_SIZE].mr_rkey[i]; - data_ctx_->register_remote_memory_region(MR_KEY + std::to_string(i), addr, size, rkey); - } - } - - else { - SLIME_LOG_DEBUG("The REMOTE DATA MR has been REGISTERED! The SLOT_ID is: ", unique_SEND_SLOT_ID_); - } - - AssignmentBatch batch; - - for (size_t i = 0; i < rdma_buffer->batch_size(); ++i) { - batch.push_back(Assignment(MR_KEY + std::to_string(i), 0, 0, rdma_buffer->view_batch()[i].length)); - } - - auto data_atx = this->data_ctx_->submit( - OpCode::WRITE_WITH_IMM, batch, element.callback_, RDMAContext::UNDEFINED_QPI, unique_SEND_SLOT_ID_); -} - -void RDMAEndpoint::WaitSendFinishQueueThread(std::chrono::milliseconds timeout) +void RDMAEndpoint::RecvBufferQueueThread(std::chrono::milliseconds timeout) { - SLIME_LOG_INFO("WaitSendFinishQueueThread started"); - - while (!stop_WaitSendFinishQueueThread_.load(std::memory_order_acquire)) { - bool processed_any = false; - - while (true) { - RDMABufferQueueElement element; - - bool found = send_finish_queue_.peekQueue(element, [](const RDMABufferQueueElement& elem) -> bool { - return elem.is_finished_.load(std::memory_order_acquire); - }); - - if (found) { - SLIME_LOG_DEBUG("Processing completed send element {}", element.unique_slot_id_); - - processed_any = true; - } - else { - break; + SLIME_LOG_INFO("RecvBufferQueueThread started (timeout={}ms)", timeout.count()); + + while (!stop_recv_buffer_queue_thread_.load(std::memory_order_acquire)) { + uint32_t idx; + if (recv_buffer_queue_.getFrontTaskId(idx)) { + std::cout << "data_slots_manager_" << std::endl; + data_slots_manager_->printSlots(); + if (data_slots_manager_->checkSlotReady(idx)) { + RDMABufferQueueElement element; + if (recv_buffer_queue_.fetchQueue(element)) { + postMetaWrite(element.unique_id_, element.rdma_buffer_); + recv_buffer_mapping_.emplace(idx, element); + data_slots_manager_->releaseSlot(idx); + } + else { + SLIME_LOG_ERROR("FAIL to fetchQueue recv_buffer_queue_"); + } } } - - if (!processed_any) { + else { if (timeout.count() > 0) { std::this_thread::sleep_for(timeout); } @@ -436,35 +444,32 @@ void RDMAEndpoint::WaitSendFinishQueueThread(std::chrono::milliseconds timeout) } } } - - SLIME_LOG_INFO("WaitSendFinishQueueThread stopped"); + SLIME_LOG_INFO("RecvBufferQueueThread stopped"); } -void RDMAEndpoint::WaitRecvFinishQueueThread(std::chrono::milliseconds timeout) +void RDMAEndpoint::SendFinishQueueThread(std::chrono::milliseconds timeout) { - SLIME_LOG_INFO("WaitRecvFinishQueueThread started"); - - while (!stop_WaitRecvFinishQueueThread_.load(std::memory_order_acquire)) { - bool processed_any = false; - - while (true) { - RDMABufferQueueElement element; - - bool found = recv_finish_queue_.peekQueue(element, [](const RDMABufferQueueElement& elem) -> bool { - return elem.is_finished_.load(std::memory_order_acquire); - }); + SLIME_LOG_INFO("SendFinishQueueThread started (timeout={}ms)", timeout.count()); + while (!stop_wait_send_finish_queue_thread_.load(std::memory_order_acquire)) { + uint32_t idx; + if (send_finish_queue_.getFrontTaskId(idx)) { + bool found = send_finish_queue_.peekQueue( + idx, [](const auto& e) { return e.is_finished_ptr_->load(std::memory_order_acquire); }); if (found) { - SLIME_LOG_DEBUG("Processing completed recv element {}", element.unique_slot_id_); - processed_any = true; - } - else { - - break; + RDMABufferQueueElement element; + if (send_finish_queue_.fetchQueue(element)) { + element.rdma_buffer_->sendDoneCallback(); + SLIME_LOG_DEBUG( + "SUCCESS to send_finish_queue_ ", idx, " and the slot id ", idx % SLIME_STATUS_SLOT_SIZE); + } + else { + SLIME_LOG_ERROR("The Msend_finish_queue_ no element"); + } } } - if (!processed_any) { + else { if (timeout.count() > 0) { std::this_thread::sleep_for(timeout); } @@ -473,10 +478,52 @@ void RDMAEndpoint::WaitRecvFinishQueueThread(std::chrono::milliseconds timeout) } } } - - SLIME_LOG_INFO("WaitRecvFinishQueueThread stopped"); + SLIME_LOG_INFO("SendFinishQueueThread stopped"); } +// void RDMAEndpoint::RecvFinishQueueThread(std::chrono::milliseconds timeout) +// { +// SLIME_LOG_INFO("SendBufferQueueThread started (timeout={}ms)", timeout.count()); + +// while (!stop_wait_recv_finish_queue_thread_.load(std::memory_order_acquire)) { +// uint32_t idx; +// if (send_finish_queue_.getFrontTaskId(idx)) { + +// if (temp_slots_manager_->checkSlotReady(idx)) { +// RDMABufferQueueElement element; +// if (recv_finish_queue_.fetchQueue(element)) { +// element.rdma_buffer_->recvDoneCallback(); +// if (temp_slots_manager_->releaseSlot(idx)) { +// addPreQueueElement(OpCode::RECV); +// } +// else { +// SLIME_LOG_ERROR("FAIL to set META RECV RING SLOT status of task id ", +// idx, +// " and the slot id ", +// idx % SLIME_STATUS_SLOT_SIZE); +// } +// } +// else { +// SLIME_LOG_ERROR("FAIL to set META RECV RING SLOT status of task id ", +// idx, +// " and the slot id ", +// idx % SLIME_STATUS_SLOT_SIZE); +// } +// } +// } +// else { +// if (timeout.count() > 0) { +// std::this_thread::sleep_for(timeout); +// } +// else { +// std::this_thread::yield(); +// } +// } +// } +// SLIME_LOG_INFO("SendBufferQueueThread stopped"); +// } +} // namespace slime + // int RDMASendRecvTask::makeRemoteDataMR() // { // if (!rdma_buffer_ || !rdma_endpoint_) { @@ -507,54 +554,15 @@ void RDMAEndpoint::WaitRecvFinishQueueThread(std::chrono::milliseconds timeout) // return 0; // } -// RDMAEndpoint::~RDMAEndpoint() -// { -// { -// std::unique_lock lock(rdma_tasks_mutex_); -// RDMA_tasks_threads_running_ = false; -// } - -// rdma_tasks_cv_.notify_all(); - -// if (rdma_tasks_threads_.joinable()) -// rdma_tasks_threads_.join(); -// } - // void RDMAEndpoint::connect(const json& data_ctx_info, const json& meta_ctx_info) // { -// std::cout << "DDD" << std::endl; // data_ctx_->connect(data_ctx_info); // meta_ctx_->connect(meta_ctx_info); -// std::cout << "DDDDDD" << std::endl; // data_ctx_->launch_future(); // meta_ctx_->launch_future(); -// std::cout << "DDDDDDDDDDDD" << std::endl; // RDMA_tasks_threads_running_ = true; // rdma_tasks_threads_ = std::thread([this] { this->mainQueueThread(std::chrono::milliseconds(100)); }); -// std::cout << "DDDDDDDDDDDDDDDDDD" << std::endl; // rdma_task_pool_ = std::make_shared(shared_from_this()); -// std::cout << "!!!!!!!!!!!!!!!!!!!!!!!!" << std::endl; -// } - -// RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, uint32_t task_id) -// { -// rdma_endpoint_ = rdma_endpoint; -// task_id_ = task_id; -// makeDummyAssignmentBatch(); -// } - -// RDMASendRecvTask::RDMASendRecvTask(std::shared_ptr rdma_endpoint, OpCode rmda_opcode, uint32_t -// task_id): -// rdma_endpoint_(rdma_endpoint), rdma_operation_(rmda_opcode), task_id_(task_id) -// { -// unique_slot_id_ = -1; -// rdma_buffer_ = nullptr; -// makeDummyAssignmentBatch(); -// } - -// RDMASendRecvTask::~RDMASendRecvTask() -// { -// SLIME_LOG_INFO("Destroy the RDMASendRecvTask: ", task_id_); // } // // 生成meta Assignment 和 data Assignment @@ -582,165 +590,3 @@ void RDMAEndpoint::WaitRecvFinishQueueThread(std::chrono::milliseconds timeout) // } // return 0; // } - -// int RDMASendRecvTask::makeDummyAssignmentBatch() -// { - -// dum_meta_assignment_batch_ = AssignmentBatch{Assignment("dum_meta_buffer_", 0, 0, 16 * sizeof(uint32_t))}; -// dum_data_assignment_batch_ = AssignmentBatch{Assignment("dum_data_buffer_", 0, 0, 16 * sizeof(uint32_t))}; - -// return 0; -// } - -// int RDMASendRecvTask::makeMetaMR() -// { -// auto& meta_buffer = rdma_endpoint_->metaBuffer(); - -// std::string prefix = (rdma_operation_ == OpCode::SEND) ? "DATA_SEND_" : "DATA_RECV_"; -// for (size_t i = 0; i < rdma_buffer_->batch_size(); ++i) { -// auto mr = rdma_endpoint_->dataCtx()->get_mr(prefix + std::to_string(unique_slot_id_) + "_" + -// std::to_string(i)); if (rdma_operation_ == OpCode::RECV) { -// meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i] = -// reinterpret_cast(mr->addr); -// meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; -// meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i] = mr->length; -// meta_buffer[MAX_META_BUFFER_SIZE + unique_slot_id_ % MAX_META_BUFFER_SIZE].mr_slot = unique_slot_id_; -// } -// } - -// return 0; -// } - -// void RDMAEndpoint::mainQueueThread(std::chrono::milliseconds timeout) -// { -// while (RDMA_tasks_threads_running_) { -// std::shared_ptr task; - -// { -// std::unique_lock lock(rdma_tasks_mutex_); - -// bool has_task = rdma_tasks_cv_.wait_for( -// lock, timeout, [this] { return !rdma_tasks_queue_.empty() || !RDMA_tasks_threads_running_; }); - -// if (!RDMA_tasks_threads_running_) -// break; -// if (!has_task) -// continue; - -// task = std::move(rdma_tasks_queue_.front()); -// rdma_tasks_queue_.pop(); -// } - -// if (task) { -// switch (task->rdma_operation_) { -// case OpCode::SEND: -// asyncSendData(task); -// break; -// case OpCode::RECV: -// asyncRecvData(task); -// break; -// default: -// SLIME_LOG_ERROR("Unknown OpCode in WaitandPopTask"); -// break; -// } -// } -// } -// } - -// void RDMAEndpoint::asyncSendData(std::shared_ptr task) -// { -// std::cout << "The send task has been pre post" << std::endl; -// } - -// void RDMAEndpoint::asyncRecvData(std::shared_ptr task) -// { -// auto data_callback = [this, task](int status, int slot_id) mutable { -// this->rdma_task_pool_->releaseSendRecvTask(task); -// task->rdma_buffer_->recv_done_callback(); -// }; - -// auto meta_callback = [this, task](int status, int slot_id) mutable { -// std::cout << "The recv task has been pre post" << std::endl; -// }; - -// { -// auto data_atx = this->data_ctx_->submit(OpCode::RECV, -// task->dum_data_assignment_batch_, -// data_callback, -// RDMAContext::UNDEFINED_QPI, -// task->unique_slot_id_); - -// AssignmentBatch meta_assignment_batch = task->meta_assignment_batch_; -// meta_ctx_->submit(OpCode::WRITE_WITH_IMM, -// meta_assignment_batch, -// meta_callback, -// RDMAContext::UNDEFINED_QPI, -// task->unique_slot_id_); -// } -// } - -// } -// void RDMATask::fillBuffer() -// { -// if (opcode_ == OpCode::SEND) { -// std::vector& meta_buf = endpoint_->getMetaBuffer(); -// } -// else if (opcode_ == OpCode::RECV) { -// std::vector& meta_buf = endpoint_->getMetaBuffer(); -// for (size_t i = 0; i < buffer_->batchSize(); ++i) { -// auto mr = endpoint_->dataCtx()->get_mr(getDataKey(i)); -// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_addr[i] = -// reinterpret_cast(mr->addr); -// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_rkey[i] = mr->rkey; -// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_size[i] = mr->length; -// } -// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_slot = slot_id_; -// meta_buf[MAX_META_BUFFER_SIZE + slot_id_ % MAX_META_BUFFER_SIZE].mr_qpidx = -// slot_id_ % endpoint_->dataCtxQPNum(); -// } -// else { -// SLIME_LOG_ERROR("Unsupported opcode in RDMATask::fillBuffer()"); -// } -// } - -// RDMAEndpoint::RDMAEndpoint(const std::string& data_dev_name, -// const std::string& meta_dev_name, -// uint8_t ib_port, -// const std::string& link_type, -// size_t qp_num) -// { -// SLIME_LOG_INFO("Init the Contexts and RDMA Devices..."); -// data_ctx_ = std::make_shared(qp_num, 0); -// meta_ctx_ = std::make_shared(1, 0); - -// data_ctx_->init(data_dev_name, ib_port, link_type); -// meta_ctx_->init(meta_dev_name, ib_port, link_type); - -// data_ctx_qp_num_ = data_ctx_->qp_list_len_; -// meta_ctx_qp_num_ = meta_ctx_->qp_list_len_; -// SLIME_LOG_INFO("The QP number of data plane is: ", data_ctx_qp_num_); -// SLIME_LOG_INFO("The QP number of control plane is: ", meta_ctx_qp_num_); -// SLIME_LOG_INFO("RDMA Endpoint Init Success and Launch the RDMA Endpoint Task Threads..."); - -// const size_t max_meta_buffer_size = MAX_META_BUFFER_SIZE * 2; -// meta_buffer_.reserve(max_meta_buffer_size); -// memset(meta_buffer_.data(), 0, meta_buffer_.size() * sizeof(meta_data_t)); -// meta_ctx_->register_memory_region( -// "meta_buffer", reinterpret_cast(meta_buffer_.data()), sizeof(meta_data_t) * -// max_meta_buffer_size); -// } - -// RDMAEndpoint::~RDMAEndpoint() -// { -// { -// std::unique_lock lock(rdma_tasks_mutex_); -// RDMA_tasks_threads_running_ = false; -// } - -// rdma_tasks_cv_.notify_all(); - -// if (rdma_tasks_threads_.joinable()) -// rdma_tasks_threads_.join(); -// } - -} // namespace slime diff --git a/csrc/engine/rdma/rdma_endpoint.h b/csrc/engine/rdma/rdma_endpoint.h index 1ae034b..03e3b92 100644 --- a/csrc/engine/rdma/rdma_endpoint.h +++ b/csrc/engine/rdma/rdma_endpoint.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -15,17 +16,12 @@ #include #include #include +#include #include #include "logging.h" #include "rdma_common.h" -using JSON = const nlohmann::json; - -#define MAX_META_BATCH_SIZE 64 -#define MAX_META_BUFFER_SIZE 64 -#define MAX_QUEUE_SIZE 64 - namespace slime { class RDMABuffer; @@ -33,295 +29,201 @@ class RDMAEndpoint; typedef struct MetaData { - uint64_t mr_addr[MAX_META_BATCH_SIZE]; - uint32_t mr_rkey[MAX_META_BATCH_SIZE]; - uint32_t mr_size[MAX_META_BATCH_SIZE]; + uint64_t mr_addr; + uint32_t mr_rkey; + uint32_t mr_size; uint32_t mr_slot; uint32_t mr_qpidx; } meta_data_t; -struct alignas(64) MetaElement { - uint64_t mr_addr[MAX_META_BATCH_SIZE]; - uint32_t mr_rkey[MAX_META_BATCH_SIZE]; - uint32_t mr_size[MAX_META_BATCH_SIZE]; - uint64_t mr_slot; - - MetaElement() - { - std::memset(mr_addr, 0, sizeof(mr_addr)); - std::memset(mr_rkey, 0, sizeof(mr_rkey)); - std::memset(mr_size, 0, sizeof(mr_size)); - mr_slot = 0; - } -}; - -template -class MetaBuffer { -public: - MetaBuffer(size_t size): size_(size), storage_(size) {} - - void setMeta(int id, const T& meta) - { - std::unique_lock lock(mutexes_[id % size_]); - storage_[id % size_] = meta; - } - - T getMeta(int id) const - { - std::shared_lock lock(mutexes_[id % size_]); - return storage_[id % size_]; - } - - size_t getSize() const - { - return size_; - } - - const T* data() const - { - return storage_.data(); - } - T* data() - { - return storage_.data(); - } - -private: - std::vector storage_; - mutable std::vector mutexes_; - size_t size_; -}; - struct RDMABufferQueueElement { - RDMABufferQueueElement(); - RDMABufferQueueElement(uint32_t unique_slot_id, - OpCode rdma_opcode, - std::shared_ptr rdma_buffer = nullptr): - unique_slot_id_(unique_slot_id), rdma_opcode_(rdma_opcode), rdma_buffer_(rdma_buffer) + RDMABufferQueueElement() = default; + RDMABufferQueueElement(uint32_t unique_id, OpCode rdma_opcode, std::shared_ptr rdma_buffer = nullptr): + unique_id_(unique_id), rdma_opcode_(rdma_opcode), rdma_buffer_(rdma_buffer) { - is_finished_ = false; - auto callback = [this](int status, int slot_id) mutable { this->is_finished_ = true; }; - callback_ = std::move(callback); + is_finished_ptr_ = std::make_shared>(false); } + RDMABufferQueueElement(const RDMABufferQueueElement& other) = default; + + RDMABufferQueueElement(RDMABufferQueueElement&& other) noexcept = default; RDMABufferQueueElement& operator=(RDMABufferQueueElement&& other) noexcept { if (this != &other) { - unique_slot_id_ = other.unique_slot_id_; - rdma_opcode_ = other.rdma_opcode_; - rdma_buffer_ = std::move(other.rdma_buffer_); - is_finished_.store(other.is_finished_.load(std::memory_order_relaxed)); - callback_ = std::move(other.callback_); + unique_id_ = other.unique_id_; + rdma_opcode_ = other.rdma_opcode_; + rdma_buffer_ = std::move(other.rdma_buffer_); + is_finished_ptr_ = std::move(other.is_finished_ptr_); + other.rdma_buffer_ = nullptr; + other.is_finished_ptr_ = nullptr; } return *this; } - - uint32_t unique_slot_id_; - OpCode rdma_opcode_; - std::shared_ptr rdma_buffer_; - std::atomic is_finished_; - std::function callback_; + uint32_t unique_id_{0}; + OpCode rdma_opcode_{OpCode::SEND}; + std::shared_ptr rdma_buffer_{nullptr}; + std::shared_ptr> is_finished_ptr_{nullptr}; }; struct RDMAPrePostQueueElement { - RDMAPrePostQueueElement(); - RDMAPrePostQueueElement(uint32_t unique_task_id, - OpCode rdma_opcode, - std::shared_ptr rdma_endpoint = nullptr): - unique_task_id_(unique_task_id), rdma_opcode_(rdma_opcode), rdma_endpoint_(rdma_endpoint) + RDMAPrePostQueueElement() = default; + RDMAPrePostQueueElement(uint32_t unique_id, OpCode rdma_opcode): unique_id_(unique_id), rdma_opcode_(rdma_opcode) { - - assignment_batch_ = rdma_opcode_ == OpCode::SEND ? - AssignmentBatch{Assignment("dum_meta_buffer_", 0, 0, 16 * sizeof(uint32_t))} : - AssignmentBatch{Assignment("dum_data_buffer_", 0, 0, 16 * sizeof(uint32_t))}; - - is_finished_ = false; - auto callback = [this](int status, int slot_id) mutable { this->is_finished_ = true; }; - callback_ = std::move(callback); + is_finished_ptr_ = std::make_shared>(false); } + RDMAPrePostQueueElement(const RDMAPrePostQueueElement& other) = default; + RDMAPrePostQueueElement(RDMAPrePostQueueElement&& other) noexcept = default; + RDMAPrePostQueueElement& operator=(RDMAPrePostQueueElement&& other) noexcept { if (this != &other) { - unique_task_id_ = other.unique_task_id_; - rdma_opcode_ = other.rdma_opcode_; - assignment_batch_ = std::move(other.assignment_batch_); - rdma_endpoint_ = std::move(other.rdma_endpoint_); - is_finished_.store(other.is_finished_.load(std::memory_order_relaxed)); - callback_ = std::move(other.callback_); + unique_id_ = other.unique_id_; + rdma_opcode_ = other.rdma_opcode_; + is_finished_ptr_ = std::move(other.is_finished_ptr_); + other.is_finished_ptr_ = nullptr; } return *this; } + uint32_t unique_id_{0}; + OpCode rdma_opcode_{OpCode::SEND}; + std::shared_ptr> is_finished_ptr_{nullptr}; +}; - uint32_t unique_task_id_; - OpCode rdma_opcode_; - AssignmentBatch assignment_batch_; +class RingSlotsManager { - std::shared_ptr rdma_endpoint_; - std::atomic is_finished_; - std::function callback_; -}; +private: + const size_t buffer_size_; + + struct Slot { + std::atomic status{false}; + std::atomic layers{0}; + }; + + std::vector slots_; -class RingBufferReadyManager { public: - explicit RingBufferReadyManager(size_t size): buffer_size_(size), slots_(size) + explicit RingSlotsManager(size_t size): buffer_size_(size), slots_(size) { for (size_t i = 0; i < buffer_size_; ++i) { - slots_[i].store(false, std::memory_order_relaxed); + slots_[i].status.store(false, std::memory_order_relaxed); + slots_[i].layers.store(0, std::memory_order_relaxed); } } - ~RingBufferReadyManager() = default; + RingSlotsManager(const RingSlotsManager&) = delete; + RingSlotsManager& operator=(const RingSlotsManager&) = delete; + RingSlotsManager(RingSlotsManager&&) = delete; + RingSlotsManager& operator=(RingSlotsManager&&) = delete; - bool writeSlot(uint32_t idx, bool value, bool wait_if_false = false, int max_wait_attempts = 100) + bool releaseSlot(uint32_t idx, int max_wait_attempts = 5) { - size_t index = idx % buffer_size_; + size_t index = idx % buffer_size_; + uint32_t target_layer = idx / buffer_size_; - if (wait_if_false) { - return writeWithWait(index, value, max_wait_attempts); - } - else { - slots_[index].store(value, std::memory_order_release); - return true; - } - } + for (int attempt = 0; attempt < max_wait_attempts; ++attempt) { + bool cur_status = slots_[index].status.load(std::memory_order_acquire); + uint32_t cur_layers = slots_[index].layers.load(std::memory_order_acquire); - bool readSlot(uint32_t idx) const - { - size_t index = idx % buffer_size_; - return slots_[index].load(std::memory_order_acquire); + if (!cur_status || cur_layers != target_layer) { + return false; + } + + bool expected_status = true; + if (slots_[index].status.compare_exchange_strong( + expected_status, false, std::memory_order_release, std::memory_order_relaxed)) { + uint32_t next_layer = cur_layers + 1; + if (slots_[index].layers.compare_exchange_strong( + cur_layers, next_layer, std::memory_order_release, std::memory_order_relaxed)) { + return true; + } + slots_[index].status.store(true, std::memory_order_release); + return false; + } + std::this_thread::yield(); + } + return false; } - bool modifySlot(uint32_t idx, bool new_value, bool expected_value) + bool acquireSlot(uint32_t idx, int max_wait_attempts = 5) { - size_t index = idx % buffer_size_; + size_t index = idx % buffer_size_; + uint32_t target_layers = idx / buffer_size_; - bool expected = expected_value; - return slots_[index].compare_exchange_strong(expected, new_value, std::memory_order_acq_rel); - } + for (int attempt = 0; attempt < max_wait_attempts; ++attempt) { + bool cur_status = slots_[index].status.load(std::memory_order_acquire); + uint32_t cur_layers = slots_[index].layers.load(std::memory_order_acquire); - bool waitForSlot(uint32_t idx, bool desired_value, int max_attempts = 100, int sleep_ms = 1) - { - size_t index = idx % buffer_size_; + if (cur_status || cur_layers != target_layers) { + std::this_thread::yield(); + continue; + } - for (int attempt = 0; attempt < max_attempts; ++attempt) { - if (slots_[index].load(std::memory_order_acquire) == desired_value) { + bool expected = false; + if (slots_[index].status.compare_exchange_strong( + expected, true, std::memory_order_release, std::memory_order_relaxed)) { return true; } - std::this_thread::sleep_for(std::chrono::milliseconds(sleep_ms)); + + std::this_thread::yield(); } + return false; } - bool setSlotTrueWithWait(uint32_t idx, int max_wait_attempts = 10) + bool checkSlotAvailable(uint32_t idx) const { - return writeSlot(idx, true, true, max_wait_attempts); - } + size_t index = idx % buffer_size_; + uint32_t target_layers = idx / buffer_size_; - bool setSlotFalseWithWait(uint32_t task_id, int max_wait_attempts = 10) - { - return writeSlot(task_id, false, true, max_wait_attempts); + bool status = slots_[index].status.load(std::memory_order_acquire); + uint32_t layers = slots_[index].layers.load(std::memory_order_acquire); + return !status && (layers == target_layers); } - size_t getBufferSize() const + bool checkSlotReady(uint32_t idx) const { - return buffer_size_; - } + size_t index = idx % buffer_size_; + uint32_t target_layers = idx / buffer_size_; - size_t getTrueCount() const - { - size_t count = 0; - for (size_t i = 0; i < buffer_size_; ++i) { - if (slots_[i].load(std::memory_order_acquire)) { - ++count; - } - } - return count; - } + bool status = slots_[index].status.load(std::memory_order_acquire); + uint32_t layers = slots_[index].layers.load(std::memory_order_acquire); - void resetAll() - { - for (size_t i = 0; i < buffer_size_; ++i) { - slots_[i].store(false, std::memory_order_relaxed); - } + return status && (layers == target_layers); } -private: - bool writeWithWait(size_t index, bool new_value, int max_attempts) + void printSlots() const { - bool opposite_value = !new_value; + std::cout << "=== RingSlotsManager Status (size=" << buffer_size_ << ") ===" << std::endl; + for (size_t i = 0; i < buffer_size_; ++i) { + bool status = slots_[i].status.load(std::memory_order_relaxed); + uint32_t layers = slots_[i].layers.load(std::memory_order_relaxed); - for (int attempt = 0; attempt < max_attempts; ++attempt) { - bool current_value = slots_[index].load(std::memory_order_acquire); + std::string state = status ? "occupied" : "free"; - if (current_value == opposite_value) { - slots_[index].store(new_value, std::memory_order_release); - return true; - } - else { - std::this_thread::yield(); - } + std::cout << " [" << std::setw(3) << i << "] layer=" << std::setw(2) << layers << " : " << state + << std::endl; } - return false; + std::cout << "========================================" << std::endl; } - -private: - const size_t buffer_size_; - std::vector> slots_; }; template class ProxyQueue { private: - std::queue queue_; - mutable std::mutex mutex_; - std::condition_variable cv_; - - std::atomic stop_flag_{false}; + std::queue queue_; + mutable std::mutex mutex_; public: - ProxyQueue() = default; - ProxyQueue(const ProxyQueue&) = delete; - ProxyQueue& operator=(const ProxyQueue&) = delete; - - void enqueue(T&& element) - { - { - std::lock_guard lock(mutex_); - queue_.push(std::move(element)); - } - cv_.notify_one(); - } - - T dequeue() - { - std::unique_lock lock(mutex_); - cv_.wait(lock, [this] { return !queue_.empty() || stop_flag_.load(); }); - - if (stop_flag_.load()) { - SLIME_LOG_ERROR("STOP"); - } - - T element = std::move(queue_.front()); - queue_.pop(); - return element; - } - - template - bool peekQueue(T& element, M&& m) + void enqueue(T element) { std::lock_guard lock(mutex_); - if (!queue_.empty() && m(queue_.front())) { - element = std::move(queue_.front()); - queue_.pop(); - return true; - } - return false; + queue_.push(std::move(element)); } bool fetchQueue(T& element) @@ -335,53 +237,69 @@ class ProxyQueue { return true; } - template - bool fetchQueue(T& element, const std::chrono::duration& time_out) + bool checkQueue(const T*& element) { - std::unique_lock lock(mutex_); - if (!cv_.wait_for(lock, time_out, [this] { return !queue_.empty() || stop_flag_.load(); })) { + std::lock_guard lock(mutex_); + if (queue_.empty()) return false; - } - if (stop_flag_.load()) { + element = &queue_.front(); + return true; + } + bool popQueue() + { + std::lock_guard lock(mutex_); + if (queue_.empty()) return false; - } - element = std::move(queue_.front()); queue_.pop(); return true; } - bool empty() const + template + bool peekQueue(uint32_t& task_id, M&& matcher) { std::lock_guard lock(mutex_); - return queue_.empty(); + if (!queue_.empty() && std::forward(matcher)(queue_.front())) { + task_id = queue_.front().unique_id_; + // queue_.pop(); + return true; + } + return false; } - size_t size() const + template + bool peekQueue(T& element, M&& matcher) { std::lock_guard lock(mutex_); - return queue_.size(); + if (!queue_.empty() && std::forward(matcher)(queue_.front())) { + element = std::move(queue_.front()); + queue_.pop(); + return true; + } + return false; } - void notifyAll() + bool getFrontTaskId(uint32_t& task_id) { - { - std::lock_guard lock(mutex_); - stop_flag_.store(true); + std::lock_guard lock(mutex_); + if (!queue_.empty()) { + task_id = queue_.front().unique_id_; + return true; } - cv_.notify_all(); + return false; } - void stop() + size_t size() const { - stop_flag_.store(true); - cv_.notify_all(); + std::lock_guard lock(mutex_); + return queue_.size(); } - void restart() + bool empty() const { - stop_flag_.store(false); + std::lock_guard lock(mutex_); + return queue_.empty(); } }; @@ -390,16 +308,26 @@ class RDMAEndpoint: public std::enable_shared_from_this { friend class RDMABuffer; public: - RDMAEndpoint(const std::string& dev_name, size_t ib_port, const std::string& link_type, size_t qp_nums); + explicit RDMAEndpoint(const std::string& dev_name, size_t ib_port, const std::string& link_type, size_t qp_nums); // TODO: 设计聚合多网卡传输的Send Recv - void connect(JSON& data_ctx_info, JSON& meta_ctx_info); + json dataCtxInfo() const + { + return data_ctx_->endpoint_info(); + } + + json metaCtxInfo() const + { + return meta_ctx_->endpoint_info(); + } + + void connect(const json& data_ctx_info, const json& meta_ctx_info); ~RDMAEndpoint(); - void startAllThreads(); - void stopAllThreads(); + void proxyInit(); + void proxyDestroy(); void addRDMABuffer(std::shared_ptr rdma_buffer); @@ -408,13 +336,14 @@ class RDMAEndpoint: public std::enable_shared_from_this { void dataRecvQueueThread(std::chrono::milliseconds timeout); void SendBufferQueueThread(std::chrono::milliseconds timeout); + void RecvBufferQueueThread(std::chrono::milliseconds timeout); - void WaitSendFinishQueueThread(std::chrono::milliseconds timeout); - void WaitRecvFinishQueueThread(std::chrono::milliseconds timeout); + void SendFinishQueueThread(std::chrono::milliseconds timeout); + void RecvFinishQueueThread(std::chrono::milliseconds timeout); void addPreQueueElement(OpCode rdma_opcode); void addRDMABuffer(OpCode rdma_opcode, std::shared_ptr rdma_buffer); - void postMetaWrite(std::shared_ptr rdma_buffer); + void postMetaWrite(uint32_t idx, std::shared_ptr rdma_buffer); void postDataWrite(RDMABufferQueueElement& element, std::shared_ptr rdma_buffer); void postRDMAAssignment(OpCode rdma_opcode); @@ -422,16 +351,16 @@ class RDMAEndpoint: public std::enable_shared_from_this { ProxyQueue data_recv_queue_; ProxyQueue send_buffer_queue_; + ProxyQueue recv_buffer_queue_; ProxyQueue send_finish_queue_; ProxyQueue recv_finish_queue_; - ProxyQueue meta_recv_store_queue_; - std::thread meta_recv_thread_; std::thread data_recv_thread_; std::thread send_buffer_thread_; + std::thread recv_buffer_thread_; std::thread send_finish_thread_; std::thread recv_finish_thread_; @@ -447,22 +376,21 @@ class RDMAEndpoint: public std::enable_shared_from_this { std::vector meta_buffer_; - std::unordered_map meta_buffer_is_ready_; - mutable std::shared_mutex meta_buffer_mutex_; - std::shared_ptr data_ctx_; std::shared_ptr meta_ctx_; std::atomic unique_meta_recv_id_{0}; std::atomic unique_data_recv_id_{0}; - std::atomic stop_SendMetaQueueThread_{false}; - std::atomic stop_dataRecvQueueThread_{false}; - std::atomic stop_SendBufferQueueThread_{false}; - std::atomic stop_WaitSendFinishQueueThread_{false}; - std::atomic stop_WaitRecvFinishQueueThread_{false}; - std::unique_ptr meta_buffer_manager_; - std::unique_ptr data_buffer_manager_; -}; + std::atomic stop_meta_recv_queue_thread_{false}; + std::atomic stop_data_recv_queue_thread_{false}; + std::atomic stop_send_buffer_queue_thread_{false}; + std::atomic stop_recv_buffer_queue_thread_{false}; + std::atomic stop_wait_send_finish_queue_thread_{false}; + std::atomic stop_wait_recv_finish_queue_thread_{false}; + std::unique_ptr meta_slots_manager_; + std::unique_ptr data_slots_manager_; -} // namespace slime + std::unordered_map recv_buffer_mapping_; +}; +} // namespace slime \ No newline at end of file diff --git a/csrc/engine/rdma/rdma_env.h b/csrc/engine/rdma/rdma_env.h index cd3ff3c..f4b51c9 100644 --- a/csrc/engine/rdma/rdma_env.h +++ b/csrc/engine/rdma/rdma_env.h @@ -21,5 +21,7 @@ inline const int SLIME_QP_NUM = get_env("SLIME_QP_NUM", inline const int SLIME_CQ_NUM = get_env("SLIME_CQ_NUM", 1); inline const int SLIME_MAX_CQ_DEPTH = get_env("SLIME_MAX_CQ_DEPTH", 8192); inline const int SLIME_AGG_QP_NUM = get_env("SLIME_AGG_QP_NUM", 1); - +inline const int SLIME_DUMMY_BUFFER_SIZE = get_env("SLIME_DUMMY_BUFFER_SIZE", 16); +inline const int SLIME_META_BUFFER_SIZE = get_env("SLIME_META_BUFFER_SIZE", 4); +inline const int SLIME_STATUS_SLOT_SIZE = get_env("SLIME_STATUS_SLOT_SIZE", 4); } // namespace slime diff --git a/tests/cpp/recv_test.cpp b/tests/cpp/recv_test.cpp index fac5905..38cb171 100644 --- a/tests/cpp/recv_test.cpp +++ b/tests/cpp/recv_test.cpp @@ -10,37 +10,36 @@ using json = nlohmann::json; using namespace slime; -DEFINE_string(DEVICE_NAME, "rxe_0", "device name"); -DEFINE_uint32(IB_PORT, 1, "device name"); +DEFINE_string(DEVICE_NAME, "rxe_0", "RDMA device name"); DEFINE_string(LINK_TYPE, "RoCE", "IB or RoCE"); - +DEFINE_int32(IB_PORT, 1, "RDMA port number"); +DEFINE_int32(PORT_DATA, 5557, "ZMQ DATA port"); +DEFINE_int32(PORT_META, 5558, "ZMQ META port"); DEFINE_string(PEER_ADDR, "127.0.0.1", "peer IP address"); -DEFINE_int32(PORT_DATA, 5557, "ZMQ control port"); -DEFINE_int32(PORT_MRCN, 5558, "ZMQ control port"); int main(int argc, char** argv) { - std::cout << "Init the RMDA ENDPOINT OF SEND... " << std::endl; + std::cout << "Init the RMDA ENDPOINT OF RECV... " << std::endl; // Construct the end_point - auto end_point = std::make_shared(FLAGS_DEVICE_NAME, FLAGS_IB_PORT, FLAGS_LINK_TYPE, 4); + auto end_point = std::make_shared(FLAGS_DEVICE_NAME, FLAGS_IB_PORT, FLAGS_LINK_TYPE, 1); std::cout << "RDMA QP INFO VIA TCP... " << std::endl; // RDMA control plane via TCP zmq::context_t zmq_ctx_data(2); - zmq::context_t zmq_ctx_mmrg(2); + zmq::context_t zmq_ctx_meta(2); zmq::socket_t sock_data(zmq_ctx_data, ZMQ_REQ); - zmq::socket_t sock_mmrg(zmq_ctx_mmrg, ZMQ_REQ); + zmq::socket_t sock_meta(zmq_ctx_meta, ZMQ_REQ); sock_data.connect("tcp://" + FLAGS_PEER_ADDR + ":" + std::to_string(FLAGS_PORT_DATA)); - sock_mmrg.connect("tcp://" + FLAGS_PEER_ADDR + ":" + std::to_string(FLAGS_PORT_MRCN)); + sock_meta.connect("tcp://" + FLAGS_PEER_ADDR + ":" + std::to_string(FLAGS_PORT_META)); zmq::message_t local_data_channel_info(end_point->dataCtxInfo().dump()); zmq::message_t local_meta_channel_info(end_point->metaCtxInfo().dump()); sock_data.send(local_data_channel_info, zmq::send_flags::none); - sock_mmrg.send(local_meta_channel_info, zmq::send_flags::none); + sock_meta.send(local_meta_channel_info, zmq::send_flags::none); std::cout << "Send the RDMA Info to other side..." << std::endl; @@ -48,47 +47,136 @@ int main(int argc, char** argv) zmq::message_t meta_channel_info; auto send_data_result = sock_data.recv(data_channel_info); - auto recv_data_result = sock_mmrg.recv(meta_channel_info); + auto recv_data_result = sock_meta.recv(meta_channel_info); end_point->connect(json::parse(data_channel_info.to_string()), json::parse(meta_channel_info.to_string())); std::cout << "Connect Success..." << std::endl; std::cout << "Finish the connection of QP, start to RECV of buf_0 and buf_1... " << std::endl; - const uint32_t batch_size_buf_0 = 1; - std::vector data_buf_0_0(8192, 'A'); - std::vector ptrs_buf_0 = {reinterpret_cast(data_buf_0_0.data())}; - std::vector data_sizes_buf_0 = {data_buf_0_0.size()}; - std::vector offset_buf_0 = {0}; - - const uint32_t batch_size_buf_1 = 2; - std::vector data_buf_1_0(1024, 'B'); - std::vector data_buf_1_1(2048, 'C'); - std::vector ptrs_buf_1 = {reinterpret_cast(data_buf_1_0.data()), - reinterpret_cast(data_buf_1_1.data())}; - std::vector data_sizes_buf_1 = {data_buf_1_0.size(), data_buf_1_1.size()}; - std::vector offset_buf_1 = {0, 0}; - - RDMABuffer buf_0(end_point, ptrs_buf_0, data_sizes_buf_0, offset_buf_0); - RDMABuffer buf_1(end_point, ptrs_buf_1, data_sizes_buf_1, offset_buf_0); + const uint32_t batch_size_buf_0 = 1; + std::vector data_buf_0(8192, 'A'); + uintptr_t ptrs_buf_0 = reinterpret_cast(data_buf_0.data()); + size_t data_sizes_buf_0 = data_buf_0.size(); + size_t offset_buf_0 = 0; + + const uint32_t batch_size_buf_1 = 1; + std::vector data_buf_1(8192, 'B'); + uintptr_t ptrs_buf_1 = reinterpret_cast(data_buf_1.data()); + size_t data_sizes_buf_1 = data_buf_1.size(); + size_t offset_buf_1 = 0; + + const uint32_t batch_size_buf_2 = 1; + std::vector data_buf_2(8192, 'C'); + uintptr_t ptrs_buf_2 = reinterpret_cast(data_buf_2.data()); + size_t data_sizes_buf_2 = data_buf_2.size(); + size_t offset_buf_2 = 0; + + const uint32_t batch_size_buf_3 = 1; + std::vector data_buf_3(8192, 'D'); + uintptr_t ptrs_buf_3 = reinterpret_cast(data_buf_3.data()); + size_t data_sizes_buf_3 = data_buf_3.size(); + size_t offset_buf_3 = 0; + + const uint32_t batch_size_buf_4 = 1; + std::vector data_buf_4(8192, 'E'); + uintptr_t ptrs_buf_4 = reinterpret_cast(data_buf_4.data()); + size_t data_sizes_buf_4 = data_buf_4.size(); + size_t offset_buf_4 = 0; + + const uint32_t batch_size_buf_5 = 1; + std::vector data_buf_5(8192, 'F'); + uintptr_t ptrs_buf_5 = reinterpret_cast(data_buf_5.data()); + size_t data_sizes_buf_5 = data_buf_5.size(); + size_t offset_buf_5 = 0; + + std::vector data_buf_6(8192, 'F'); + uintptr_t ptrs_buf_6 = reinterpret_cast(data_buf_6.data()); + size_t data_sizes_buf_6 = data_buf_6.size(); + size_t offset_buf_6 = 0; + + std::vector data_buf_7(8192, 'F'); + uintptr_t ptrs_buf_7 = reinterpret_cast(data_buf_7.data()); + size_t data_sizes_buf_7 = data_buf_7.size(); + size_t offset_buf_7 = 0; + + std::vector data_buf_8(8192, 'F'); + uintptr_t ptrs_buf_8 = reinterpret_cast(data_buf_8.data()); + size_t data_sizes_buf_8 = data_buf_8.size(); + size_t offset_buf_8 = 0; + + auto buf_0 = std::make_shared(end_point, ptrs_buf_0, offset_buf_0, data_sizes_buf_0); + auto buf_1 = std::make_shared(end_point, ptrs_buf_1, offset_buf_1, data_sizes_buf_1); + auto buf_2 = std::make_shared(end_point, ptrs_buf_2, offset_buf_2, data_sizes_buf_2); + auto buf_3 = std::make_shared(end_point, ptrs_buf_3, offset_buf_3, data_sizes_buf_3); + auto buf_4 = std::make_shared(end_point, ptrs_buf_4, offset_buf_4, data_sizes_buf_4); + auto buf_5 = std::make_shared(end_point, ptrs_buf_5, offset_buf_5, data_sizes_buf_5); + auto buf_6 = std::make_shared(end_point, ptrs_buf_6, offset_buf_6, data_sizes_buf_6); + auto buf_7 = std::make_shared(end_point, ptrs_buf_7, offset_buf_7, data_sizes_buf_7); + auto buf_8 = std::make_shared(end_point, ptrs_buf_8, offset_buf_8, data_sizes_buf_8); + std::cout << "Launch EDNPOINT ..." << std::endl; - buf_1.recv(); - buf_0.recv(); + buf_0->recv(); + buf_1->recv(); + buf_2->recv(); + buf_3->recv(); + buf_4->recv(); + buf_5->recv(); + buf_6->recv(); + buf_7->recv(); + buf_8->recv(); std::cout << "Main thread working Test..." << std::endl; std::cout << "Main thread working Test..." << std::endl; std::cout << "Main thread working Test..." << std::endl; std::cout << "Main thread working Test..." << std::endl; std::cout << "Main thread working Test..." << std::endl; - std::cout << "Wait Send Complete..." << std::endl; - buf_0.waitRecv(); - buf_1.waitRecv(); - - bool data_buf_0_0_correct = std::all_of(data_buf_0_0.begin(), data_buf_0_0.end(), [](char c) { return c == '0'; }); - bool data_buf_1_0_correct = std::all_of(data_buf_1_0.begin(), data_buf_1_0.end(), [](char c) { return c == '1'; }); - bool data_buf_1_1_correct = std::all_of(data_buf_1_1.begin(), data_buf_1_1.end(), [](char c) { return c == '2'; }); - assert(data_buf_0_0_correct && "Data_0_0 should contain '0'"); - assert(data_buf_1_0_correct && "Data_1_0 should contain '1'"); - assert(data_buf_1_1_correct && "Data_1_1 should contain '2'"); + std::cout << "Wait RECV Complete..." << std::endl; + buf_0->waitRecv(); + buf_1->waitRecv(); + buf_2->waitRecv(); + buf_3->waitRecv(); + buf_4->waitRecv(); + buf_5->waitRecv(); + buf_6->waitRecv(); + buf_7->waitRecv(); + buf_8->waitRecv(); + // buf_1->waitRecv(); + + std::cout << data_buf_0[0] << std::endl; + std::cout << data_buf_1[0] << std::endl; + std::cout << data_buf_2[0] << std::endl; + std::cout << data_buf_3[0] << std::endl; + std::cout << data_buf_4[0] << std::endl; + std::cout << data_buf_5[0] << std::endl; + std::cout << data_buf_6[0] << std::endl; + std::cout << data_buf_7[0] << std::endl; + std::cout << data_buf_8[0] << std::endl; + bool data_buf_0_correct = std::all_of(data_buf_0.begin(), data_buf_0.end(), [](char c) { return c == '0'; }); + assert(data_buf_0_correct && "Data_0 should contain '0'"); + + bool data_buf_1_correct = std::all_of(data_buf_1.begin(), data_buf_1.end(), [](char c) { return c == '1'; }); + assert(data_buf_1_correct && "Data_1 should contain '1'"); + + bool data_buf_2_correct = std::all_of(data_buf_2.begin(), data_buf_2.end(), [](char c) { return c == '2'; }); + assert(data_buf_2_correct && "Data_2 should contain '2'"); + + bool data_buf_3_correct = std::all_of(data_buf_3.begin(), data_buf_3.end(), [](char c) { return c == '3'; }); + assert(data_buf_3_correct && "Data_3 should contain '3'"); + + bool data_buf_4_correct = std::all_of(data_buf_4.begin(), data_buf_4.end(), [](char c) { return c == '4'; }); + assert(data_buf_4_correct && "Data_4 should contain '4'"); + + bool data_buf_5_correct = std::all_of(data_buf_5.begin(), data_buf_5.end(), [](char c) { return c == '5'; }); + assert(data_buf_5_correct && "Data_5 should contain '5'"); + + bool data_buf_6_correct = std::all_of(data_buf_6.begin(), data_buf_6.end(), [](char c) { return c == '6'; }); + assert(data_buf_6_correct && "Data_6 should contain '6'"); + + bool data_buf_7_correct = std::all_of(data_buf_7.begin(), data_buf_7.end(), [](char c) { return c == '7'; }); + assert(data_buf_7_correct && "Data_7 should contain '7'"); + + bool data_buf_8_correct = std::all_of(data_buf_8.begin(), data_buf_8.end(), [](char c) { return c == '8'; }); + assert(data_buf_8_correct && "Data_8 should contain '8'"); std::cout << "The RECV test completed and data verified." << std::endl; diff --git a/tests/cpp/send_test.cpp b/tests/cpp/send_test.cpp index 6554a79..f81cf81 100644 --- a/tests/cpp/send_test.cpp +++ b/tests/cpp/send_test.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -12,73 +13,154 @@ using namespace slime; DEFINE_string(DEVICE_NAME, "rxe_0", "RDMA device name"); DEFINE_string(LINK_TYPE, "RoCE", "IB or RoCE"); DEFINE_int32(IB_PORT, 1, "RDMA port number"); -DEFINE_int32(PORT_DATA, 5557, "ZMQ control port"); -DEFINE_int32(PORT_MRCN, 5558, "ZMQ control port"); +DEFINE_int32(PORT_DATA, 5557, "ZMQ DATA port"); +DEFINE_int32(PORT_META, 5558, "ZMQ META port"); int main(int argc, char** argv) { - std::cout << "Init the RMDA ENDPOINT OF RECV... " << std::endl; + std::cout << "Init the RMDA ENDPOINT OF SEND... " << std::endl; // Construct the end_point - auto end_point = std::make_shared(FLAGS_DEVICE_NAME, FLAGS_IB_PORT, FLAGS_LINK_TYPE, 4); + auto end_point = std::make_shared(FLAGS_DEVICE_NAME, FLAGS_IB_PORT, FLAGS_LINK_TYPE, 1); std::cout << "RDMA QP INFO VIA TCP... " << std::endl; // RDMA control plane via TCP zmq::context_t zmq_ctx_data(2); - zmq::context_t zmq_ctx_mmrg(2); + zmq::context_t zmq_ctx_meta(2); zmq::socket_t sock_data(zmq_ctx_data, ZMQ_REP); - zmq::socket_t sock_mmrg(zmq_ctx_mmrg, ZMQ_REP); + zmq::socket_t sock_meta(zmq_ctx_meta, ZMQ_REP); sock_data.bind("tcp://*:" + std::to_string(FLAGS_PORT_DATA)); - sock_mmrg.bind("tcp://*:" + std::to_string(FLAGS_PORT_MRCN)); + sock_meta.bind("tcp://*:" + std::to_string(FLAGS_PORT_META)); zmq::message_t data_channel_info; - zmq::message_t mmrg_channel_info; + zmq::message_t meta_channel_info; auto data_channel_info_res = sock_data.recv(data_channel_info); - auto mmrg_channel_info_res = sock_mmrg.recv(mmrg_channel_info); + auto meta_channel_info_res = sock_meta.recv(meta_channel_info); std::cout << "Send the RDMA Info to other side..." << std::endl; zmq::message_t local_data_channel_info(end_point->dataCtxInfo().dump()); zmq::message_t local_meta_channel_info(end_point->metaCtxInfo().dump()); sock_data.send(local_data_channel_info, zmq::send_flags::none); - sock_mmrg.send(local_meta_channel_info, zmq::send_flags::none); + sock_meta.send(local_meta_channel_info, zmq::send_flags::none); + + end_point->connect(json::parse(data_channel_info.to_string()), json::parse(meta_channel_info.to_string())); - end_point->connect(json::parse(data_channel_info.to_string()), json::parse(mmrg_channel_info.to_string())); std::cout << "Connect Success..." << std::endl; std::cout << "Finish the connection of QP, start to SEND of buf_0 and buf_1..." << std::endl; const uint32_t batch_size_buf_0 = 1; std::vector data_buf_0(8192, '0'); - std::vector ptrs_buf_0 = {reinterpret_cast(data_buf_0.data())}; - std::vector data_sizes_buf_0 = {data_buf_0.size()}; - std::vector offset_buf_0 = {0}; + uintptr_t ptrs_buf_0 = reinterpret_cast(data_buf_0.data()); + size_t data_sizes_buf_0 = data_buf_0.size(); + size_t offset_buf_0 = 0; + + const uint32_t batch_size_buf_1 = 1; + std::vector data_buf_1(8192, '1'); + + uintptr_t ptrs_buf_1 = reinterpret_cast(data_buf_1.data()); + size_t data_sizes_buf_1 = data_buf_1.size(); + size_t offset_buf_1 = 0; + + const uint32_t batch_size_buf_2 = 1; + std::vector data_buf_2(8192, '2'); + + uintptr_t ptrs_buf_2 = reinterpret_cast(data_buf_2.data()); + size_t data_sizes_buf_2 = data_buf_2.size(); + size_t offset_buf_2 = 0; + + const uint32_t batch_size_buf_3 = 3; + std::vector data_buf_3(8192, '3'); + + uintptr_t ptrs_buf_3 = reinterpret_cast(data_buf_3.data()); + size_t data_sizes_buf_3 = data_buf_3.size(); + size_t offset_buf_3 = 0; + + const uint32_t batch_size_buf_4 = 4; + std::vector data_buf_4(8192, '4'); + + uintptr_t ptrs_buf_4 = reinterpret_cast(data_buf_4.data()); + size_t data_sizes_buf_4 = data_buf_4.size(); + size_t offset_buf_4 = 0; + + const uint32_t batch_size_buf_5 = 5; + std::vector data_buf_5(8192, '5'); + + uintptr_t ptrs_buf_5 = reinterpret_cast(data_buf_5.data()); + size_t data_sizes_buf_5 = data_buf_5.size(); + size_t offset_buf_5 = 0; + + const uint32_t batch_size_buf_6 = 5; + std::vector data_buf_6(8192, '6'); + + uintptr_t ptrs_buf_6 = reinterpret_cast(data_buf_6.data()); + size_t data_sizes_buf_6 = data_buf_6.size(); + size_t offset_buf_6 = 0; + + const uint32_t batch_size_buf_7 = 5; + std::vector data_buf_7(8192, '7'); + + uintptr_t ptrs_buf_7 = reinterpret_cast(data_buf_7.data()); + size_t data_sizes_buf_7 = data_buf_7.size(); + size_t offset_buf_7 = 0; + + const uint32_t batch_size_buf_8 = 5; + std::vector data_buf_8(8192, '8'); + + uintptr_t ptrs_buf_8 = reinterpret_cast(data_buf_8.data()); + size_t data_sizes_buf_8 = data_buf_8.size(); + size_t offset_buf_8 = 0; + + // const uint32_t batch_size_buf_1 = 2; + // std::vector data_buf_1_0(1024, '1'); + // std::vector data_buf_1_1(2048, '2'); - const uint32_t batch_size_buf_1 = 2; - std::vector data_buf_1_0(1024, '1'); - std::vector data_buf_1_1(2048, '2'); + // std::vector ptrs_buf_1 = {reinterpret_cast(data_buf_1_0.data()), + // reinterpret_cast(data_buf_1_1.data())}; + // std::vector data_sizes_buf_1 = {data_buf_1_0.size(), data_buf_1_1.size()}; + // std::vector offset_buf_1 = {0, 0}; - std::vector ptrs_buf_1 = {reinterpret_cast(data_buf_1_0.data()), - reinterpret_cast(data_buf_1_1.data())}; - std::vector data_sizes_buf_1 = {data_buf_1_0.size(), data_buf_1_1.size()}; - std::vector offset_buf_1 = {0,0}; + auto buf_0 = std::make_shared(end_point, ptrs_buf_0, offset_buf_0, data_sizes_buf_0); + auto buf_1 = std::make_shared(end_point, ptrs_buf_1, offset_buf_1, data_sizes_buf_1); + auto buf_2 = std::make_shared(end_point, ptrs_buf_2, offset_buf_2, data_sizes_buf_2); + auto buf_3 = std::make_shared(end_point, ptrs_buf_3, offset_buf_3, data_sizes_buf_3); + auto buf_4 = std::make_shared(end_point, ptrs_buf_4, offset_buf_4, data_sizes_buf_4); + auto buf_5 = std::make_shared(end_point, ptrs_buf_5, offset_buf_5, data_sizes_buf_5); + auto buf_6 = std::make_shared(end_point, ptrs_buf_6, offset_buf_6, data_sizes_buf_6); + auto buf_7 = std::make_shared(end_point, ptrs_buf_7, offset_buf_7, data_sizes_buf_7); + auto buf_8 = std::make_shared(end_point, ptrs_buf_8, offset_buf_8, data_sizes_buf_8); + // auto buf_1 = std::make_shared(end_point, ptrs_buf_1, offset_buf_1, data_sizes_buf_1); - RDMABuffer buf_0(end_point, ptrs_buf_0, data_sizes_buf_0, offset_buf_0); - RDMABuffer buf_1(end_point, ptrs_buf_1, data_sizes_buf_1, offset_buf_1); std::cout << "Launch EDNPOINT ..." << std::endl; - buf_1.send(); - buf_0.send(); + // buf_1->send(); + buf_0->send(); + buf_1->send(); + buf_2->send(); + buf_3->send(); + buf_4->send(); + buf_5->send(); + buf_6->send(); + buf_7->send(); + buf_8->send(); std::cout << "Main thread working Test..." << std::endl; std::cout << "Main thread working Test..." << std::endl; std::cout << "Main thread working Test..." << std::endl; std::cout << "Main thread working Test..." << std::endl; std::cout << "Main thread working Test..." << std::endl; std::cout << "Wait SEND Complete..." << std::endl; - buf_0.waitSend(); - buf_1.waitSend(); + buf_0->waitSend(); + buf_1->waitSend(); + buf_2->waitSend(); + buf_3->waitSend(); + buf_4->waitSend(); + buf_5->waitSend(); + buf_6->waitSend(); + buf_7->waitSend(); + buf_8->waitSend(); std::cout << "The SEND test completed." << std::endl;