From 7f451e6a9b1dda2c8f76064dda6bb1d1d7d4c131 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Thu, 30 Apr 2026 17:22:13 +0100 Subject: [PATCH 01/38] Initial commit. --- CMakeLists.txt | 25 +- mpi-test.c | 24 ++ src/host-device/CMakeLists.txt | 3 +- src/host-device/mpi_comms.c | 561 +++++++++++++++++++++++++++++++++ src/host-device/mpi_comms.h | 86 +++++ 5 files changed, 695 insertions(+), 4 deletions(-) create mode 100644 mpi-test.c create mode 100644 src/host-device/mpi_comms.c create mode 100644 src/host-device/mpi_comms.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fae95b..ec1d35d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,7 @@ cmake_minimum_required(VERSION 3.21) project(CQ-SIMBE VERSION 0.1.0 DESCRIPTION "CQ compliant simulated backend." - LANGUAGES C -) + LANGUAGES C) # User configure options @@ -41,10 +40,22 @@ option( OFF ) +option( + BUILD_WITH_MPI + "Build with MPI-based communication. Turned ON by default." + ON +) +message(STATUS "BUILD_WITH_MPI turned ${BUILD_WITH_MPI}.") + +if (BUILD_WITH_MPI) + find_package(MPI REQUIRED) + include_directories(SYSTEM ${MPI_INCLUDE_PATH}) +endif() + # Dependencies find_package(Threads REQUIRED) -find_package(QuEST 4.0 REQUIRED) +find_package(QuEST REQUIRED) # CQ-SimBE Library @@ -64,6 +75,10 @@ target_include_directories(cq-simbe target_link_libraries(cq-simbe PRIVATE Threads::Threads QuEST::QuEST) +if (BUILD_WITH_MPI) + target_link_libraries(cq-simbe PRIVATE ${MPI_C_LIBRARIES}) +endif() + if (BUILD_FORTRAN_INTERFACE) enable_language(Fortran) @@ -92,6 +107,10 @@ endif() add_subdirectory(src) +add_executable(cq-mpi-test mpi-test.c) +target_link_libraries(cq-mpi-test PRIVATE CQ-SIMBE::cq-simbe) +#target_link_libraries(cq-mpi-test PRIVATE ${MPI_C_LIBRARIES}) + if (ENABLE_TESTING) find_package(unity REQUIRED) diff --git a/mpi-test.c b/mpi-test.c new file mode 100644 index 0000000..229efa0 --- /dev/null +++ b/mpi-test.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include "cq.h" +#include "datatypes.h" +#include "host_ops.h" + +int main() { + int rank; + // MPI_Init(NULL, NULL); + // MPI_Comm_rank(MPI_COMM_WORLD, &rank); + // printf("Hello from rank %d\n", rank); + // MPI_Finalize(); + // volatile int foo = 1; + // while (foo) + // sleep(5); + + cq_init(1); + // qubit* qr; + // alloc_qureg(&qr, 5); + + cq_finalise(1); + return 0; +} diff --git a/src/host-device/CMakeLists.txt b/src/host-device/CMakeLists.txt index df2447f..d6c006e 100644 --- a/src/host-device/CMakeLists.txt +++ b/src/host-device/CMakeLists.txt @@ -2,4 +2,5 @@ target_sources(cq-simbe PRIVATE comms.c kernel_utils.c -) \ No newline at end of file + mpi_comms.c +) diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c new file mode 100644 index 0000000..c425107 --- /dev/null +++ b/src/host-device/mpi_comms.c @@ -0,0 +1,561 @@ +// I think that the comms.h should stay the same (and ideally +// the rest of the code) and if we compile with MPI build, then +// we link against this file. Otherwise against comms.c + +#include "mpi_comms.h" +#include +#include +#include +#include "datatypes.h" +#include "src/device/control.h" +#include "src/host-device/comms.h" +#include "src/host/opcodes.h" + +#include +#include +#include + +static MPI_Comm cq_mpi_comm = MPI_COMM_WORLD; + +struct cq_mpi_env { + int rank; +}; + +static struct cq_mpi_env mpi_env; + +void print_params_header(struct ctrl_params_header header) { + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + printf("host header: "); + } else { + printf("device header: "); + } + printf("Incoming header:\n"); + switch (header.type) { + case PARAMS_UINT_T: { + printf("type: UINT, "); + break; + } + case PARAMS_ALLOC_T: { + printf("type: ALLOC, "); + break; + } + case PARAMS_EXEC_T: { + printf("type: EXEC, "); + break; + } + default: { + break; + } + } + printf(" %d\n", header.params_msg_size); +} + +void pack_uint_params(void* src, void* dest) {} + +// void send_uint_params(void* params) { +void send_uint_params(struct device_ctrl_params ctrl_params) { + int device_rank = 1; + MPI_Datatype uint_type = + sizeof(unsigned int) == 4 ? MPI_UINT32_T : MPI_UINT64_T; + + struct ctrl_params_header msg_header = {.type = ctrl_params.type, + .params_msg_size = 1}; + int header_size = sizeof(struct ctrl_params_header); + + MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + MPI_Ssend(ctrl_params.data, 1, uint_type, device_rank, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm); +} + +void recv_uint_params(void* params) { + MPI_Datatype uint_type = + sizeof(unsigned int) == 4 ? MPI_UINT32_T : MPI_UINT64_T; + MPI_Status status; + params = malloc(sizeof(unsigned int)); + MPI_Recv((unsigned int*)params, 1, uint_type, CQ_MPI_HOST_RANK, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); + printf("successfully copied into params %d\n", *(unsigned int*)(params)); +} + +// void send_alloc_params(void* params) { +void send_alloc_params(struct device_ctrl_params ctrl_params) { + // pack device_alloc_params + device_alloc_params* alloc_params = (device_alloc_params*)ctrl_params.data; + int max_buffer_size; + MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &max_buffer_size); // NQUBITS + int member_size; + MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &member_size); // qregistry_idx + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_INT, cq_mpi_comm, &member_size); // status + max_buffer_size += member_size; + + char* send_buffer = malloc(max_buffer_size); + if (send_buffer == NULL) { + printf("Failed to allocate buffer for sending device allocation params.\n"); + exit(-1); + } + int position; + MPI_Pack(&alloc_params->NQUBITS, 1, MPI_UINT64_T, send_buffer, + max_buffer_size, &position, cq_mpi_comm); + MPI_Pack(&alloc_params->qregistry_idx, 1, MPI_UINT64_T, send_buffer, + max_buffer_size, &position, cq_mpi_comm); + MPI_Pack(&alloc_params->status, 1, MPI_INT, send_buffer, max_buffer_size, + &position, cq_mpi_comm); + + // all nicely packed + + struct ctrl_params_header msg_header = {.type = ctrl_params.type, + .params_msg_size = position}; + int header_size = sizeof(struct ctrl_params_header); + + int device_rank = 1; + MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + MPI_Ssend(send_buffer, position, MPI_PACKED, 1, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm); + + free(send_buffer); +} +void recv_alloc_params(void* params) {} +void send_exec_params(struct device_ctrl_params ctrl_params) {} +void recv_exec_params(void* params) {} + +// void comm_ctrl_params(struct ctrl_params_header msg_header, +// void* params, +// param_packer_fn packer) { +// if (mpi_env.rank == CQ_MPI_HOST_RANK) { +// send_ctrl_params(msg_header, params, packer); +// } else { +// recv_ctrl_params(); +// } +// } + +// void send_ctrl_params(struct ctrl_params_header msg_header, +// void* params, +// param_packer_fn packer) { +// int device_rank = 1; +// size_t header_size = sizeof(struct ctrl_params_header); +// MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, +// CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); +// print_params_header(msg_header); + +void send_ctrl_params(struct device_ctrl_params ctrl_params) { + switch (ctrl_params.type) { + case PARAMS_UINT_T: { + send_uint_params(ctrl_params); + break; + } + case PARAMS_ALLOC_T: { + send_alloc_params(ctrl_params); + break; + } + case PARAMS_EXEC_T: { + send_exec_params(ctrl_params); + break; + } + default: { + break; + } + } +} + +void recv_ctrl_params(void* params) { + struct ctrl_params_header msg_header; + size_t header_size = sizeof(struct ctrl_params_header); + MPI_Status status; + MPI_Recv(&msg_header, header_size, MPI_BYTE, CQ_MPI_HOST_RANK, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); + print_params_header(msg_header); + switch (msg_header.type) { + case PARAMS_UINT_T: { + recv_uint_params(params); + break; + } + case PARAMS_ALLOC_T: { + recv_alloc_params(params); + break; + } + case PARAMS_EXEC_T: { + recv_exec_params(params); + break; + } + default: { + break; + } + } +} + +int mpi_initialise_device(const unsigned int VERBOSITY) { + printf("Initialising MPI.\n"); + MPI_Init(NULL, NULL); + printf("Initialised MPI.\n"); + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_env.rank); + + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + if (VERBOSITY > 0) { + printf("Initialising device.\n"); + } + pthread_mutex_init(&dev_ctrl.device_lock, NULL); + pthread_cond_init(&dev_ctrl.cond_device_busy, NULL); + pthread_cond_init(&dev_ctrl.cond_queue_empty, NULL); + pthread_cond_init(&dev_ctrl.cond_queue_full, NULL); + + dev_ctrl.run_device = true; + dev_ctrl.device_busy = true; + dev_ctrl.num_ops = 0; + dev_ctrl.next_op_in = 0; + dev_ctrl.next_op_out = 0; + + for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { + dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; + dev_ctrl.op_params_buffer[i] = NULL; + } + + pthread_create(&dev_ctrl.device_thread, NULL, &mpi_device_control_thread, + NULL); + } + + unsigned int verbosity = VERBOSITY; + // struct ctrl_params_header msg_header = { + // .type = PARAMS_UINT_T, .params_msg_size = sizeof(unsigned int)}; + // comm_ctrl_params(msg_header, &verbosity, &pack_uint_params); + + struct device_ctrl_params params = {.type = PARAMS_UINT_T, + .data = &verbosity}; + mpi_host_comm_ctrl_op(CQ_CTRL_INIT, params); + printf("Finished: comm_ctrl_op\n"); + mpi_host_wait_all_ops(); + printf("Finished: wait_all_ops\n"); + + return 0; +} + +void mpi_host_comm_ctrl_op(const enum ctrl_code OP, + struct device_ctrl_params ctrl_params) { + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + mpi_host_send_ctrl_op(OP, ctrl_params); + printf("host finished sending ctrl op\n"); + } else { + mpi_host_recv_ctrl_op(); + printf("device finished recv ctrl op\n"); + } +} + +// TODO: Need to know the size of ctrl_params +// maybe instead have struct like: +// struct CtrlParams { +// void* data; +// int size; +// } +// and wrap it whenever there is call to host_send_ctrl_op +size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, + struct device_ctrl_params ctrl_params) { + int device_rank = 1; + MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + printf("Host sent OP\n"); + // TODO: send ctrl_params + send_ctrl_params(ctrl_params); + + size_t num_ops = 0; + MPI_Status status; + MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm, &status); + printf("Host recv num ops: %zu\n", num_ops); + + return num_ops; +} + +size_t mpi_host_recv_ctrl_op() { + pthread_mutex_lock(&dev_ctrl.device_lock); + + while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { + // the control queue is full! + // we'll wait for it to not be full + pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); + } + + MPI_Status status; + MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_INT, + CQ_MPI_HOST_RANK, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); + printf("device recv OP\n"); + // TODO: recv ctrl_params + recv_ctrl_params(dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); + printf("device recv op params\n"); + + ++dev_ctrl.num_ops; + + // It's a ring buffer! + // advance next_op_in then mod out buffer size + ++dev_ctrl.next_op_in; + dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; + + MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, CQ_MPI_HOST_RANK, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + printf("device sent num ops: %zu\n", dev_ctrl.num_ops); + + pthread_cond_signal(&dev_ctrl.cond_queue_empty); + pthread_mutex_unlock(&dev_ctrl.device_lock); + + return dev_ctrl.num_ops; +} + +size_t mpi_host_wait_all_ops(void) { + printf("rank is %d\n", mpi_env.rank); + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + size_t num_ops = 0; + MPI_Status status; + int device_rank = 1; + printf("host got here\n"); + MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm, &status); + return num_ops; + + } else { + pthread_mutex_lock(&dev_ctrl.device_lock); + int foo = 0; + while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { + printf("%d: device got here\n", foo); + ++foo; + pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); + } + printf("device got here\n"); + MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, CQ_MPI_HOST_RANK, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + + pthread_mutex_unlock(&dev_ctrl.device_lock); + return dev_ctrl.num_ops; + } +} + +void* mpi_device_control_thread(void*) { + enum ctrl_code current_op = CQ_CTRL_IDLE; + void* current_op_params = NULL; + + // run_device set to FALSE at cq_finalise + while (dev_ctrl.run_device) { + pthread_mutex_lock(&dev_ctrl.device_lock); + + while (dev_ctrl.num_ops <= 0) { + // wait for a new op to be posted + dev_ctrl.device_busy = false; + pthread_cond_signal(&dev_ctrl.cond_device_busy); + pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); + } + + dev_ctrl.device_busy = true; + + // take the next op and params out of the dev_ctrl buffer, and then tidy + // up the dev_ctrl buffer + current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; + current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; + if (current_op_params == NULL) { + printf("Params are null!\n"); + } + dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; + dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; + + // decrease the number of queued operations and advance next_op_out + --dev_ctrl.num_ops; + ++dev_ctrl.next_op_out; + dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; + + // signal that the queue is no longer full and then relinquish mutex + pthread_cond_signal(&dev_ctrl.cond_queue_full); + pthread_mutex_unlock(&dev_ctrl.device_lock); + + control_registry[current_op](current_op_params); + } + + pthread_mutex_unlock(&dev_ctrl.device_lock); + + return NULL; +} + +int mpi_finalise_device(const unsigned int VERBOSITY) { + // Politely wait for the device to finish its current business + // otherwise setting dev_ctrl.run_device = false might break + // some stuff, and this should only be called in cq_finalise() + mpi_host_wait_all_ops(); + + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + if (VERBOSITY > 0) { + printf("Finalising device.\n"); + } + + pthread_mutex_lock(&dev_ctrl.device_lock); + dev_ctrl.run_device = false; + pthread_mutex_unlock(&dev_ctrl.device_lock); + } + + unsigned int verbosity = VERBOSITY; + struct device_ctrl_params params = {.type = PARAMS_UINT_T, + .data = &verbosity}; + mpi_host_comm_ctrl_op(CQ_CTRL_FINALISE, params); + + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + pthread_join(dev_ctrl.device_thread, NULL); + + dev_ctrl.device_busy = false; + + pthread_cond_destroy(&dev_ctrl.cond_device_busy); + pthread_cond_destroy(&dev_ctrl.cond_queue_empty); + pthread_cond_destroy(&dev_ctrl.cond_queue_full); + pthread_mutex_destroy(&dev_ctrl.device_lock); + } + + MPI_Finalize(); + return 0; +} +// struct DeviceLink dev_ctrl; +// +// int mpi_initialise_device(const unsigned int VERBOSITY) { +// if (VERBOSITY > 0) { +// printf("Initialising device.\n"); +// } +// +// int nprocs; +// int world_rank; +// MPI_Comm comm_split; +// +// MPI_Init(NULL, NULL); +// +// MPI_Comm_size(MPI_COMM_WORLD, &nprocs); +// MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); +// // int in_quest_comm = world_rank != 0; +// // MPI_Comm_split(MPI_COMM_WORLD, I_AM_QUANTUM, world_rank, &comm_split); +// +// dev_ctrl.run_device = true; +// dev_ctrl.device_busy = true; +// dev_ctrl.num_ops = 0; +// dev_ctrl.next_op_in = 0; +// dev_ctrl.next_op_out = 0; +// +// for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { +// dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; +// dev_ctrl.op_params_buffer[i] = NULL; +// } +// +// unsigned int verbosity = VERBOSITY; +// mpi_host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); +// mpi_host_wait_all_ops(); +// +// return 0; +// } +// +// size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, void* ctrl_params) { +// // 1. need to request from device queue size... +// // 2. send OP for device op_buffer +// int rank; +// MPI_Comm_rank(MPI_COMM_WORLD, &rank); +// if (rank == 0) { +// MPI_Status status; +// int device_rank = 1; +// +// while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { +// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD, &status); +// } +// MPI_Ssend(&OP, 1, MPI_INT, device_rank, 0, MPI_COMM_WORLD); +// // TODO: this needs solution +// // MPI_Ssend(ctrl_params, ctrl_params_extent, MPI_CHAR, device_rank, 0, +// // MPI_COMM_WORLD); +// +// ++dev_ctrl.num_ops; +// ++dev_ctrl.next_op_in; +// dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; +// +// // NOTE: matched by Recv on lines 120 and 122 +// MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD); +// MPI_Ssend(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD); +// +// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD, +// &status); +// MPI_Recv(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD, &status); +// +// } else { +// // NOTE: okay, I think it's simply wrong place for this code +// // actually we leave host rank as it is and this code goes to the device +// // code or something +// // int host_rank = 0; +// // MPI_Status status; +// // +// // // NOTE: if the device queue is full this will deadlock +// // // instead we have to call host_send_ctrl_op with a modification +// // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, +// // MPI_COMM_WORLD); +// // +// // MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_UINT64_T, +// // host_rank, 0, MPI_COMM_WORLD, &status); +// // +// // // TODO: this is tricky and needs solving +// // // MPI_Recv(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in], +// // // ctrl_params_extent, MPI_CHAR, +// // // host_rank, 0, MPI_COMM_WORLD, &status); +// // +// // ++dev_ctrl.num_ops; +// // ++dev_ctrl.next_op_in; +// // dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; +// // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, +// // MPI_COMM_WORLD); MPI_Ssend(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, +// // host_rank, 0, +// // MPI_COMM_WORLD); +// // } +// } +// return dev_ctrl.num_ops; +// } +// +// void* mpi_device_control_thread(void* par) { +// enum ctrl_code current_op = CQ_CTRL_IDLE; +// void* current_op_params = NULL; +// int host_rank = 0; +// MPI_Status status; +// +// // run_device set to FALSE at cq_finalise +// while (dev_ctrl.run_device) { +// // pthread_mutex_lock(&dev_ctrl.device_lock); +// +// while (dev_ctrl.num_ops <= 0) { +// // wait for a new op to be posted +// dev_ctrl.device_busy = false; +// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, +// MPI_COMM_WORLD, +// &status); +// MPI_Recv(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, host_rank, 0, +// MPI_COMM_WORLD, &status); +// +// pthread_cond_signal(&dev_ctrl.cond_device_busy); +// pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); +// } +// MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_UINT64_T, +// host_rank, 0, MPI_COMM_WORLD, &status); +// +// dev_ctrl.device_busy = true; +// +// // take the next op and params out of the dev_ctrl buffer, and then tidy +// // up the dev_ctrl buffer +// current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; +// current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; +// dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; +// dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; +// +// // decrease the number of queued operations and advance next_op_out +// --dev_ctrl.num_ops; +// ++dev_ctrl.next_op_out; +// dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; +// +// // signal that the queue is no longer full and then relinquish mutex +// pthread_cond_signal(&dev_ctrl.cond_queue_full); +// pthread_mutex_unlock(&dev_ctrl.device_lock); +// +// control_registry[current_op](current_op_params); +// } +// +// pthread_mutex_unlock(&dev_ctrl.device_lock); +// +// return NULL; +// } diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h new file mode 100644 index 0000000..90ff093 --- /dev/null +++ b/src/host-device/mpi_comms.h @@ -0,0 +1,86 @@ +#ifndef HOST_DEVICE_MPI_COMMS_H +#define HOST_DEVICE_MPI_COMMS_H + +#include "datatypes.h" +#include "src/host/opcodes.h" + +#include + +#include +#include + +#define __CQ_DEVICE_QUEUE_SIZE__ 16 +#define __CQ_HOST_DEVICE_MPI_PROC__ 0 +#define CQ_MPI_HOST_RANK 0 +#define CQ_HOST_DEVICE_MPI_TAG 0 + +enum ctrl_params_datatype { PARAMS_UINT_T, PARAMS_ALLOC_T, PARAMS_EXEC_T }; + +struct ctrl_params_header { + enum ctrl_params_datatype type; + int params_msg_size; +}; + +struct device_ctrl_params { + enum ctrl_params_datatype type; + void* data; +}; + +void print_params_header(struct ctrl_params_header header); + +typedef void (*param_packer_fn)(void* src, void* dest); + +void pack_uint_params(void* src, void* dest); +void pack_alloc_params(void* src, void* dest); +void pack_exec_params(void* src, void* dest); +// or send_xxx_params and recv_xxx_params +// void send_uint_params(void* params); +// void send_alloc_params(void* params); +// void send_exec_params(void* params); + +void send_uint_params(struct device_ctrl_params ctrl_params); +void send_alloc_params(struct device_ctrl_params ctrl_params); +void send_exec_params(struct device_ctrl_params ctrl_params); + +void recv_uint_params(void* params); +void recv_alloc_params(void* params); +void recv_exec_params(void* params); + +// void comm_ctrl_params(struct ctrl_params_header msg_header, +// void* params, +// param_packer_fn packer); +// void send_ctrl_params(struct ctrl_params_header msg_header, +// void* params, +// param_packer_fn packer); +void send_ctrl_params(struct device_ctrl_params ctrl_params); + +void recv_ctrl_params(void* params); + +int mpi_initialise_device(const unsigned int VERBOSITY); + +void mpi_host_comm_ctrl_op(const enum ctrl_code OP, + struct device_ctrl_params ctrl_params); + +size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, + struct device_ctrl_params ctrl_params); + +size_t mpi_host_recv_ctrl_op(void); + +size_t mpi_host_sync_exec(cq_exec* const ehp); + +size_t mpi_host_wait_exec(cq_exec* const ehp); + +void mpi_host_request_halt(cq_exec* const ehp); + +size_t mpi_host_wait_all_ops(void); + +size_t mpi_device_sync_exec(const cq_status STATUS, + const size_t SHOT, + cstate const* const RESULT, + cq_exec* ehp); + +void* mpi_device_control_thread(void*); + +int mpi_finalise_device(const unsigned int VERBOSITY); + +#endif From f54a930d1584c76ed66049f59927cf8f89d5e2e1 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Fri, 1 May 2026 12:15:24 +0100 Subject: [PATCH 02/38] [WIP]: implemented communication of device control params for verbosity and allocation parameters. --- mpi-test.c | 6 +- src/host-device/mpi_comms.c | 232 ++++++++++++++++++++++++++---------- src/host-device/mpi_comms.h | 17 ++- 3 files changed, 186 insertions(+), 69 deletions(-) diff --git a/mpi-test.c b/mpi-test.c index 229efa0..dbe19d1 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -16,9 +16,9 @@ int main() { // sleep(5); cq_init(1); - // qubit* qr; - // alloc_qureg(&qr, 5); - + qubit* qr; + alloc_qureg(&qr, 5); + printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); cq_finalise(1); return 0; } diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index c425107..9a4f794 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -25,11 +25,10 @@ static struct cq_mpi_env mpi_env; void print_params_header(struct ctrl_params_header header) { if (mpi_env.rank == CQ_MPI_HOST_RANK) { - printf("host header: "); + printf("Host [header]: "); } else { - printf("device header: "); + printf("Device [header]: "); } - printf("Incoming header:\n"); switch (header.type) { case PARAMS_UINT_T: { printf("type: UINT, "); @@ -50,16 +49,63 @@ void print_params_header(struct ctrl_params_header header) { printf(" %d\n", header.params_msg_size); } -void pack_uint_params(void* src, void* dest) {} +void print_alloc_params(void* params) { + device_alloc_params* alloc_params = (device_alloc_params*)params; + + printf("Device alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", + alloc_params->NQUBITS, alloc_params->qregistry_idx, + alloc_params->status); +} + +void print_op(const enum ctrl_code OP) { + switch (OP) { + case CQ_CTRL_IDLE: { + printf("CQ_CTRL_IDLE"); + break; + } + case CQ_CTRL_ALLOC: { + printf("CQ_CTRL_ALLOC"); + break; + } + case CQ_CTRL_DEALLOC: { + printf("CQ_CTRL_DEALLOC"); + break; + } + case CQ_CTRL_INIT: { + printf("CQ_CTRL_INIT"); + break; + } + case CQ_CTRL_FINALISE: { + printf("CQ_CTRL_FINALISE"); + break; + } + case CQ_CTRL_RUN_QKERNEL: { + break; + } + case CQ_CTRL_RUN_PQKERNEL: { + break; + } + case CQ_CTRL_TEST: { + break; + } + case CQ_CTRL_ABORT: { + break; + } + default: { + break; + } + } + printf("\n"); +} // void send_uint_params(void* params) { void send_uint_params(struct device_ctrl_params ctrl_params) { - int device_rank = 1; + int device_rank = CQ_MPI_DEVICE_RANK; MPI_Datatype uint_type = sizeof(unsigned int) == 4 ? MPI_UINT32_T : MPI_UINT64_T; - struct ctrl_params_header msg_header = {.type = ctrl_params.type, - .params_msg_size = 1}; + struct ctrl_params_header msg_header = { + .type = ctrl_params.type, .params_msg_size = sizeof(unsigned int)}; int header_size = sizeof(struct ctrl_params_header); MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, @@ -68,58 +114,110 @@ void send_uint_params(struct device_ctrl_params ctrl_params) { cq_mpi_comm); } -void recv_uint_params(void* params) { - MPI_Datatype uint_type = - sizeof(unsigned int) == 4 ? MPI_UINT32_T : MPI_UINT64_T; +void recv_uint_params(void** params, int params_size) { + printf("Device: Attempting to recieve verbosity param...\n"); + if (params == NULL) { + printf("From recv_uint_params: params is NULL.\n"); + exit(-2); + } + + // free previous, now unused resources + if (*params != NULL) { + free(params); + *params = NULL; + } + MPI_Status status; - params = malloc(sizeof(unsigned int)); - MPI_Recv((unsigned int*)params, 1, uint_type, CQ_MPI_HOST_RANK, + *params = malloc(params_size); + if (*params == NULL) { + printf("From recv_uint_params: malloc failed.\n"); + exit(-2); + } + + MPI_Recv(*params, params_size, MPI_BYTE, CQ_MPI_HOST_RANK, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); - printf("successfully copied into params %d\n", *(unsigned int*)(params)); } // void send_alloc_params(void* params) { void send_alloc_params(struct device_ctrl_params ctrl_params) { + // TODO: use memcpy because no pointers in alloc params + // // pack device_alloc_params - device_alloc_params* alloc_params = (device_alloc_params*)ctrl_params.data; - int max_buffer_size; - MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &max_buffer_size); // NQUBITS - int member_size; - MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &member_size); // qregistry_idx - max_buffer_size += member_size; - MPI_Pack_size(1, MPI_INT, cq_mpi_comm, &member_size); // status - max_buffer_size += member_size; - - char* send_buffer = malloc(max_buffer_size); - if (send_buffer == NULL) { - printf("Failed to allocate buffer for sending device allocation params.\n"); - exit(-1); - } - int position; - MPI_Pack(&alloc_params->NQUBITS, 1, MPI_UINT64_T, send_buffer, - max_buffer_size, &position, cq_mpi_comm); - MPI_Pack(&alloc_params->qregistry_idx, 1, MPI_UINT64_T, send_buffer, - max_buffer_size, &position, cq_mpi_comm); - MPI_Pack(&alloc_params->status, 1, MPI_INT, send_buffer, max_buffer_size, - &position, cq_mpi_comm); + // device_alloc_params* alloc_params = + // (device_alloc_params*)ctrl_params.data; int max_buffer_size; + // MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &max_buffer_size); // NQUBITS + // int member_size; + // MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &member_size); // + // qregistry_idx max_buffer_size += member_size; MPI_Pack_size(1, MPI_INT, + // cq_mpi_comm, &member_size); // status max_buffer_size += member_size; + // + // char* send_buffer = malloc(max_buffer_size); + // if (send_buffer == NULL) { + // printf("Failed to allocate buffer for sending device allocation + // params.\n"); exit(-1); + // } + // int position; + // MPI_Pack(&alloc_params->NQUBITS, 1, MPI_UINT64_T, send_buffer, + // max_buffer_size, &position, cq_mpi_comm); + // MPI_Pack(&alloc_params->qregistry_idx, 1, MPI_UINT64_T, send_buffer, + // max_buffer_size, &position, cq_mpi_comm); + // MPI_Pack(&alloc_params->status, 1, MPI_INT, send_buffer, max_buffer_size, + // &position, cq_mpi_comm); // all nicely packed + // struct ctrl_params_header msg_header = {.type = ctrl_params.type, + // .params_msg_size = position}; + printf("Host: Attempting to send device_alloc_params...\n"); + size_t buffer_size = sizeof(device_alloc_params); + void* send_buffer = malloc(buffer_size); + memcpy(send_buffer, ctrl_params.data, buffer_size); struct ctrl_params_header msg_header = {.type = ctrl_params.type, - .params_msg_size = position}; + .params_msg_size = buffer_size}; + int header_size = sizeof(struct ctrl_params_header); - int device_rank = 1; + int device_rank = CQ_MPI_DEVICE_RANK; MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - MPI_Ssend(send_buffer, position, MPI_PACKED, 1, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm); + MPI_Ssend(send_buffer, buffer_size, MPI_BYTE, device_rank, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + + // MPI_Ssend(send_buffer, position, MPI_PACKED, device_rank, + // CQ_HOST_DEVICE_MPI_TAG, + // cq_mpi_comm); free(send_buffer); + printf("Host: Sent device_alloc_params...\n"); } -void recv_alloc_params(void* params) {} + +void recv_alloc_params(void** params, int params_size) { + printf("Device: Attempting to recieve device_alloc_params...\n"); + if (params == NULL) { + printf("From recv_alloc_params: params is NULL.\n"); + exit(-2); + } + + // free previous, now unused resources + if (*params != NULL) { + free(params); + *params = NULL; + } + + MPI_Status status; + *params = malloc(params_size); + if (*params == NULL) { + printf("From recv_alloc_params: malloc failed.\n"); + exit(-2); + } + + MPI_Recv(*params, params_size, MPI_BYTE, CQ_MPI_HOST_RANK, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); + printf("Device: Recieved device_alloc_params...\n"); +} + void send_exec_params(struct device_ctrl_params ctrl_params) {} -void recv_exec_params(void* params) {} +void recv_exec_params(void** params, int params_size) {} // void comm_ctrl_params(struct ctrl_params_header msg_header, // void* params, @@ -160,7 +258,7 @@ void send_ctrl_params(struct device_ctrl_params ctrl_params) { } } -void recv_ctrl_params(void* params) { +void recv_ctrl_params(void** params) { struct ctrl_params_header msg_header; size_t header_size = sizeof(struct ctrl_params_header); MPI_Status status; @@ -169,15 +267,15 @@ void recv_ctrl_params(void* params) { print_params_header(msg_header); switch (msg_header.type) { case PARAMS_UINT_T: { - recv_uint_params(params); + recv_uint_params(params, msg_header.params_msg_size); break; } case PARAMS_ALLOC_T: { - recv_alloc_params(params); + recv_alloc_params(params, msg_header.params_msg_size); break; } case PARAMS_EXEC_T: { - recv_exec_params(params); + recv_exec_params(params, msg_header.params_msg_size); break; } default: { @@ -235,10 +333,10 @@ void mpi_host_comm_ctrl_op(const enum ctrl_code OP, struct device_ctrl_params ctrl_params) { if (mpi_env.rank == CQ_MPI_HOST_RANK) { mpi_host_send_ctrl_op(OP, ctrl_params); - printf("host finished sending ctrl op\n"); + printf("Host [comm_ctrl_op]: finished sending ctrl op\n"); } else { mpi_host_recv_ctrl_op(); - printf("device finished recv ctrl op\n"); + printf("Device [comm_ctrl_op]: finished recv ctrl op\n"); } } @@ -251,10 +349,11 @@ void mpi_host_comm_ctrl_op(const enum ctrl_code OP, // and wrap it whenever there is call to host_send_ctrl_op size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, struct device_ctrl_params ctrl_params) { - int device_rank = 1; + int device_rank = CQ_MPI_DEVICE_RANK; + // TODO: merge sends? MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - printf("Host sent OP\n"); - // TODO: send ctrl_params + printf("Host [send_ctrl_op]: Sent "); + print_op(OP); send_ctrl_params(ctrl_params); size_t num_ops = 0; @@ -266,6 +365,7 @@ size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, return num_ops; } +// TODO: rename -- it is device comms thread that runs this size_t mpi_host_recv_ctrl_op() { pthread_mutex_lock(&dev_ctrl.device_lock); @@ -278,10 +378,17 @@ size_t mpi_host_recv_ctrl_op() { MPI_Status status; MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_INT, CQ_MPI_HOST_RANK, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); - printf("device recv OP\n"); - // TODO: recv ctrl_params - recv_ctrl_params(dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); - printf("device recv op params\n"); + printf("Device [recv_ctrl_op]: Recieved "); + print_op(dev_ctrl.op_buffer[dev_ctrl.next_op_in]); + + recv_ctrl_params(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); + if (dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_ALLOC || + dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_DEALLOC) { + print_alloc_params(dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); + } else { + printf("Device verbosity params: VERBOSITY: %d\n", + *(unsigned int*)dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); + } ++dev_ctrl.num_ops; @@ -292,7 +399,7 @@ size_t mpi_host_recv_ctrl_op() { MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, CQ_MPI_HOST_RANK, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - printf("device sent num ops: %zu\n", dev_ctrl.num_ops); + printf("Device [recv_ctrl_op]: sent num ops: %zu\n", dev_ctrl.num_ops); pthread_cond_signal(&dev_ctrl.cond_queue_empty); pthread_mutex_unlock(&dev_ctrl.device_lock); @@ -301,12 +408,11 @@ size_t mpi_host_recv_ctrl_op() { } size_t mpi_host_wait_all_ops(void) { - printf("rank is %d\n", mpi_env.rank); if (mpi_env.rank == CQ_MPI_HOST_RANK) { size_t num_ops = 0; MPI_Status status; - int device_rank = 1; - printf("host got here\n"); + int device_rank = CQ_MPI_DEVICE_RANK; + printf("Host [wait_all_ops]: got here\n"); MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); return num_ops; @@ -315,11 +421,11 @@ size_t mpi_host_wait_all_ops(void) { pthread_mutex_lock(&dev_ctrl.device_lock); int foo = 0; while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { - printf("%d: device got here\n", foo); + printf("Device [wait_all_ops][loop]: got here %d\n", foo); ++foo; pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); } - printf("device got here\n"); + printf("Device [wait_all_ops]: got here\n"); MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, CQ_MPI_HOST_RANK, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); @@ -353,7 +459,13 @@ void* mpi_device_control_thread(void*) { printf("Params are null!\n"); } dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; - dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; + // release resources allocated in specialisation of recv_ctrl_params + // Alternativally comment this out and handle freeing in those + // specialisations -- see commented out first few lines in recv_uint_params. + // free(dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]); + // dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; + // NOTE: 1: went with freeing in recievers solution + // NOTE: 2: since we have ring buffer, resources will be freed on snd pass // decrease the number of queued operations and advance next_op_out --dev_ctrl.num_ops; diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index 90ff093..b3a6cf0 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -12,6 +12,7 @@ #define __CQ_DEVICE_QUEUE_SIZE__ 16 #define __CQ_HOST_DEVICE_MPI_PROC__ 0 #define CQ_MPI_HOST_RANK 0 +#define CQ_MPI_DEVICE_RANK 1 #define CQ_HOST_DEVICE_MPI_TAG 0 enum ctrl_params_datatype { PARAMS_UINT_T, PARAMS_ALLOC_T, PARAMS_EXEC_T }; @@ -27,6 +28,8 @@ struct device_ctrl_params { }; void print_params_header(struct ctrl_params_header header); +void print_alloc_params(void* params); +void print_op(const enum ctrl_code OP); typedef void (*param_packer_fn)(void* src, void* dest); @@ -38,13 +41,17 @@ void pack_exec_params(void* src, void* dest); // void send_alloc_params(void* params); // void send_exec_params(void* params); +void send_ctrl_params(struct device_ctrl_params ctrl_params); void send_uint_params(struct device_ctrl_params ctrl_params); + +// TODO: Need to send the updated params back from device to host. void send_alloc_params(struct device_ctrl_params ctrl_params); void send_exec_params(struct device_ctrl_params ctrl_params); -void recv_uint_params(void* params); -void recv_alloc_params(void* params); -void recv_exec_params(void* params); +void recv_ctrl_params(void** params); +void recv_uint_params(void** params, int params_size); +void recv_alloc_params(void** params, int params_size); +void recv_exec_params(void** params, int params_size); // void comm_ctrl_params(struct ctrl_params_header msg_header, // void* params, @@ -52,9 +59,6 @@ void recv_exec_params(void* params); // void send_ctrl_params(struct ctrl_params_header msg_header, // void* params, // param_packer_fn packer); -void send_ctrl_params(struct device_ctrl_params ctrl_params); - -void recv_ctrl_params(void* params); int mpi_initialise_device(const unsigned int VERBOSITY); @@ -64,6 +68,7 @@ void mpi_host_comm_ctrl_op(const enum ctrl_code OP, size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, struct device_ctrl_params ctrl_params); +// TODO: rename -- it is device comms thread that runs this size_t mpi_host_recv_ctrl_op(void); size_t mpi_host_sync_exec(cq_exec* const ehp); From 8331eb184f96ed5ade4892ed6d8f7197bef6244d Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 5 May 2026 16:45:35 +0100 Subject: [PATCH 03/38] [WIP]: Implemented sending of device control parameters back to the host. --- mpi-test.c | 6 +- src/host-device/mpi_comms.c | 266 ++++++++++++++++++++++++++---------- src/host-device/mpi_comms.h | 47 ++++--- 3 files changed, 229 insertions(+), 90 deletions(-) diff --git a/mpi-test.c b/mpi-test.c index dbe19d1..35457c2 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -18,7 +18,11 @@ int main() { cq_init(1); qubit* qr; alloc_qureg(&qr, 5); - printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); + printf( + "[MAIN]: created qreg: with NQUBITS: %zu, qregistry_idx: %d, offset: " + "%d\n", + qr->N, qr->registry_index, qr->offset); + // printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); cq_finalise(1); return 0; } diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 9a4f794..23fffb4 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -23,23 +23,34 @@ struct cq_mpi_env { static struct cq_mpi_env mpi_env; -void print_params_header(struct ctrl_params_header header) { +const char* get_comm_source() { if (mpi_env.rank == CQ_MPI_HOST_RANK) { - printf("Host [header]: "); + return "Host:\t\t"; + } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { + return "Device:\t\t"; } else { - printf("Device [header]: "); + return ""; } +} +void print_params_header(struct ctrl_params_header header) { + // if (mpi_env.rank == CQ_MPI_HOST_RANK) { + // printf("Host [header]: "); + // } else { + // printf("Device [header]: "); + // } switch (header.type) { case PARAMS_UINT_T: { - printf("type: UINT, "); + printf("%s [header]: type: UINT, ", get_comm_source()); break; } case PARAMS_ALLOC_T: { - printf("type: ALLOC, "); + // printf("type: ALLOC, "); + printf("%s [header]: type: ALLOC, ", get_comm_source()); break; } case PARAMS_EXEC_T: { - printf("type: EXEC, "); + // printf("type: EXEC, "); + printf("%s [header]: type: EXEC, ", get_comm_source()); break; } default: { @@ -52,8 +63,8 @@ void print_params_header(struct ctrl_params_header header) { void print_alloc_params(void* params) { device_alloc_params* alloc_params = (device_alloc_params*)params; - printf("Device alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", - alloc_params->NQUBITS, alloc_params->qregistry_idx, + printf("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", + get_comm_source(), alloc_params->NQUBITS, alloc_params->qregistry_idx, alloc_params->status); } @@ -80,15 +91,19 @@ void print_op(const enum ctrl_code OP) { break; } case CQ_CTRL_RUN_QKERNEL: { + printf("CQ_CTRL_RUN_QKERNEL"); break; } case CQ_CTRL_RUN_PQKERNEL: { + printf("CQ_CTRL_RUN_PQKERNEL"); break; } case CQ_CTRL_TEST: { + printf("CQ_CTRL_TEST"); break; } case CQ_CTRL_ABORT: { + printf("CQ_CTRL_ABORT"); break; } default: { @@ -98,9 +113,61 @@ void print_op(const enum ctrl_code OP) { printf("\n"); } +enum ctrl_params_datatype op_type_to_params_type(enum ctrl_code op_type) + +{ + switch (op_type) { + case CQ_CTRL_ALLOC: + return PARAMS_ALLOC_T; + case CQ_CTRL_DEALLOC: + return PARAMS_ALLOC_T; + case CQ_CTRL_INIT: + return PARAMS_UINT_T; + case CQ_CTRL_FINALISE: + return PARAMS_UINT_T; + case CQ_CTRL_RUN_QKERNEL: + return PARAMS_EXEC_T; + case CQ_CTRL_RUN_PQKERNEL: + return PARAMS_EXEC_T; + default: { + printf("Unhandled op type. Returning PARAMS_UINT_T. Expect segfault.\n"); + return PARAMS_UINT_T; + }; + } +} + +size_t static get_params_size(enum ctrl_params_datatype type) { + switch (type) { + case PARAMS_UINT_T: { + return sizeof(unsigned int); + break; + } + case PARAMS_ALLOC_T: { + return sizeof(device_alloc_params); + break; + } + case PARAMS_EXEC_T: { + return 0; + break; + } + default: { + return 0; + break; + } + } +} + +void params_deep_copy(enum ctrl_params_datatype params_type, + void* src, + void** dest) { + const size_t params_size = get_params_size(params_type); + *dest = malloc(params_size); + memcpy(*dest, src, params_size); +} + // void send_uint_params(void* params) { -void send_uint_params(struct device_ctrl_params ctrl_params) { - int device_rank = CQ_MPI_DEVICE_RANK; +// TODO: do the destination parameter +void send_uint_params(struct device_ctrl_params ctrl_params, int dest) { MPI_Datatype uint_type = sizeof(unsigned int) == 4 ? MPI_UINT32_T : MPI_UINT64_T; @@ -108,14 +175,14 @@ void send_uint_params(struct device_ctrl_params ctrl_params) { .type = ctrl_params.type, .params_msg_size = sizeof(unsigned int)}; int header_size = sizeof(struct ctrl_params_header); - MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - MPI_Ssend(ctrl_params.data, 1, uint_type, device_rank, CQ_HOST_DEVICE_MPI_TAG, + MPI_Ssend(&msg_header, header_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm); + MPI_Ssend(ctrl_params.data, 1, uint_type, dest, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); } -void recv_uint_params(void** params, int params_size) { - printf("Device: Attempting to recieve verbosity param...\n"); +void recv_uint_params(void** params, int params_size, int src) { + printf("%s Attempting to recieve verbosity param...\n", get_comm_source()); if (params == NULL) { printf("From recv_uint_params: params is NULL.\n"); exit(-2); @@ -123,6 +190,9 @@ void recv_uint_params(void** params, int params_size) { // free previous, now unused resources if (*params != NULL) { + printf("From recv_uint_params: *params is NULL.\n"); + exit(-2); + free(params); *params = NULL; } @@ -134,12 +204,12 @@ void recv_uint_params(void** params, int params_size) { exit(-2); } - MPI_Recv(*params, params_size, MPI_BYTE, CQ_MPI_HOST_RANK, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); + MPI_Recv(*params, params_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm, &status); } // void send_alloc_params(void* params) { -void send_alloc_params(struct device_ctrl_params ctrl_params) { +void send_alloc_params(struct device_ctrl_params ctrl_params, int dest) { // TODO: use memcpy because no pointers in alloc params // // pack device_alloc_params @@ -168,7 +238,7 @@ void send_alloc_params(struct device_ctrl_params ctrl_params) { // struct ctrl_params_header msg_header = {.type = ctrl_params.type, // .params_msg_size = position}; - printf("Host: Attempting to send device_alloc_params...\n"); + printf("%s Attempting to send device_alloc_params...\n", get_comm_source()); size_t buffer_size = sizeof(device_alloc_params); void* send_buffer = malloc(buffer_size); memcpy(send_buffer, ctrl_params.data, buffer_size); @@ -177,22 +247,22 @@ void send_alloc_params(struct device_ctrl_params ctrl_params) { int header_size = sizeof(struct ctrl_params_header); - int device_rank = CQ_MPI_DEVICE_RANK; - MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - MPI_Ssend(send_buffer, buffer_size, MPI_BYTE, device_rank, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + MPI_Ssend(&msg_header, header_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm); + MPI_Ssend(send_buffer, buffer_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm); // MPI_Ssend(send_buffer, position, MPI_PACKED, device_rank, // CQ_HOST_DEVICE_MPI_TAG, // cq_mpi_comm); free(send_buffer); - printf("Host: Sent device_alloc_params...\n"); + printf("%s Sent device_alloc_params...\n", get_comm_source()); } -void recv_alloc_params(void** params, int params_size) { - printf("Device: Attempting to recieve device_alloc_params...\n"); +void recv_alloc_params(void** params, int params_size, int src) { + printf("%s Attempting to recieve device_alloc_params...\n", + get_comm_source()); if (params == NULL) { printf("From recv_alloc_params: params is NULL.\n"); exit(-2); @@ -200,6 +270,8 @@ void recv_alloc_params(void** params, int params_size) { // free previous, now unused resources if (*params != NULL) { + printf("From recv_alloc_params: *params is NULL.\n"); + exit(-2); free(params); *params = NULL; } @@ -211,13 +283,13 @@ void recv_alloc_params(void** params, int params_size) { exit(-2); } - MPI_Recv(*params, params_size, MPI_BYTE, CQ_MPI_HOST_RANK, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); - printf("Device: Recieved device_alloc_params...\n"); + MPI_Recv(*params, params_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm, &status); + printf("%s Recieved device_alloc_params...\n", get_comm_source()); } -void send_exec_params(struct device_ctrl_params ctrl_params) {} -void recv_exec_params(void** params, int params_size) {} +void send_exec_params(struct device_ctrl_params ctrl_params, int dest) {} +void recv_exec_params(void** params, int params_size, int src) {} // void comm_ctrl_params(struct ctrl_params_header msg_header, // void* params, @@ -238,18 +310,18 @@ void recv_exec_params(void** params, int params_size) {} // CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); // print_params_header(msg_header); -void send_ctrl_params(struct device_ctrl_params ctrl_params) { - switch (ctrl_params.type) { +void send_ctrl_params(const struct device_ctrl_params* ctrl_params, int dest) { + switch (ctrl_params->type) { case PARAMS_UINT_T: { - send_uint_params(ctrl_params); + send_uint_params(*ctrl_params, dest); break; } case PARAMS_ALLOC_T: { - send_alloc_params(ctrl_params); + send_alloc_params(*ctrl_params, dest); break; } case PARAMS_EXEC_T: { - send_exec_params(ctrl_params); + send_exec_params(*ctrl_params, dest); break; } default: { @@ -258,24 +330,24 @@ void send_ctrl_params(struct device_ctrl_params ctrl_params) { } } -void recv_ctrl_params(void** params) { +void recv_ctrl_params(void** params, int src) { struct ctrl_params_header msg_header; size_t header_size = sizeof(struct ctrl_params_header); MPI_Status status; - MPI_Recv(&msg_header, header_size, MPI_BYTE, CQ_MPI_HOST_RANK, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); + MPI_Recv(&msg_header, header_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm, &status); print_params_header(msg_header); switch (msg_header.type) { case PARAMS_UINT_T: { - recv_uint_params(params, msg_header.params_msg_size); + recv_uint_params(params, msg_header.params_msg_size, src); break; } case PARAMS_ALLOC_T: { - recv_alloc_params(params, msg_header.params_msg_size); + recv_alloc_params(params, msg_header.params_msg_size, src); break; } case PARAMS_EXEC_T: { - recv_exec_params(params, msg_header.params_msg_size); + recv_exec_params(params, msg_header.params_msg_size, src); break; } default: { @@ -321,7 +393,7 @@ int mpi_initialise_device(const unsigned int VERBOSITY) { struct device_ctrl_params params = {.type = PARAMS_UINT_T, .data = &verbosity}; - mpi_host_comm_ctrl_op(CQ_CTRL_INIT, params); + mpi_host_comm_ctrl_op(CQ_CTRL_INIT, ¶ms); printf("Finished: comm_ctrl_op\n"); mpi_host_wait_all_ops(); printf("Finished: wait_all_ops\n"); @@ -330,13 +402,13 @@ int mpi_initialise_device(const unsigned int VERBOSITY) { } void mpi_host_comm_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params ctrl_params) { + struct device_ctrl_params* ctrl_params) { if (mpi_env.rank == CQ_MPI_HOST_RANK) { mpi_host_send_ctrl_op(OP, ctrl_params); - printf("Host [comm_ctrl_op]: finished sending ctrl op\n"); + printf("%s [comm_ctrl_op]: finished sending ctrl op\n", get_comm_source()); } else { mpi_host_recv_ctrl_op(); - printf("Device [comm_ctrl_op]: finished recv ctrl op\n"); + printf("%s [comm_ctrl_op]: finished recv ctrl op\n", get_comm_source()); } } @@ -348,25 +420,37 @@ void mpi_host_comm_ctrl_op(const enum ctrl_code OP, // } // and wrap it whenever there is call to host_send_ctrl_op size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params ctrl_params) { - int device_rank = CQ_MPI_DEVICE_RANK; + struct device_ctrl_params* ctrl_params) { + const int device_rank = CQ_MPI_DEVICE_RANK; // TODO: merge sends? MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - printf("Host [send_ctrl_op]: Sent "); + printf("%s [send_ctrl_op]: Sent ", get_comm_source()); print_op(OP); - send_ctrl_params(ctrl_params); + send_ctrl_params(ctrl_params, device_rank); size_t num_ops = 0; MPI_Status status; MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); - printf("Host recv num ops: %zu\n", num_ops); + printf("%s recv num ops: %zu\n", get_comm_source(), num_ops); + + // alloc and dealloc should be blocking + if (OP == CQ_CTRL_ALLOC || OP == CQ_CTRL_DEALLOC) { + void* params_buffer = NULL; + recv_ctrl_params(¶ms_buffer, device_rank); + memcpy(ctrl_params->data, params_buffer, sizeof(device_alloc_params)); + printf("Performed deep copy\n"); + print_alloc_params(ctrl_params->data); + } + + // rest handled by executor return num_ops; } // TODO: rename -- it is device comms thread that runs this size_t mpi_host_recv_ctrl_op() { + const int host_rank = CQ_MPI_HOST_RANK; pthread_mutex_lock(&dev_ctrl.device_lock); while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { @@ -376,17 +460,17 @@ size_t mpi_host_recv_ctrl_op() { } MPI_Status status; - MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_INT, - CQ_MPI_HOST_RANK, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); - printf("Device [recv_ctrl_op]: Recieved "); + MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_INT, host_rank, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); + printf("%s [recv_ctrl_op]: Recieved ", get_comm_source()); print_op(dev_ctrl.op_buffer[dev_ctrl.next_op_in]); - recv_ctrl_params(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); + recv_ctrl_params(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in], host_rank); if (dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_ALLOC || dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_DEALLOC) { print_alloc_params(dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); } else { - printf("Device verbosity params: VERBOSITY: %d\n", + printf("%s verbosity params: VERBOSITY: %d\n", get_comm_source(), *(unsigned int*)dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); } @@ -397,9 +481,10 @@ size_t mpi_host_recv_ctrl_op() { ++dev_ctrl.next_op_in; dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; - MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, CQ_MPI_HOST_RANK, + MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - printf("Device [recv_ctrl_op]: sent num ops: %zu\n", dev_ctrl.num_ops); + printf("%s [recv_ctrl_op]: sent num ops: %zu\n", get_comm_source(), + dev_ctrl.num_ops); pthread_cond_signal(&dev_ctrl.cond_queue_empty); pthread_mutex_unlock(&dev_ctrl.device_lock); @@ -409,24 +494,29 @@ size_t mpi_host_recv_ctrl_op() { size_t mpi_host_wait_all_ops(void) { if (mpi_env.rank == CQ_MPI_HOST_RANK) { + const int device_rank = CQ_MPI_DEVICE_RANK; size_t num_ops = 0; MPI_Status status; - int device_rank = CQ_MPI_DEVICE_RANK; - printf("Host [wait_all_ops]: got here\n"); + printf("%s [wait_all_ops]: got here\n", get_comm_source()); + // rather than waiting for num ops, wait for the ctrl params and update + // recv_ctrl_params(void** params, int src) MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); return num_ops; } else { + const int host_rank = CQ_MPI_HOST_RANK; pthread_mutex_lock(&dev_ctrl.device_lock); int foo = 0; while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { - printf("Device [wait_all_ops][loop]: got here %d\n", foo); + printf("%s [wait_all_ops][loop]: got here %d\n", get_comm_source(), foo); ++foo; pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); } - printf("Device [wait_all_ops]: got here\n"); - MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, CQ_MPI_HOST_RANK, + printf("%s [wait_all_ops]: got here\n", get_comm_source()); + // rather than sending num ops, device_control_thread sends updated params? + // but also maybe I don't want MPI inside device_control_thread + MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); pthread_mutex_unlock(&dev_ctrl.device_lock); @@ -454,20 +544,36 @@ void* mpi_device_control_thread(void*) { // take the next op and params out of the dev_ctrl buffer, and then tidy // up the dev_ctrl buffer current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; - current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; + + // TODO: Can solve NOTE 4 (see below) by doing deep copy here... + // and freeing op_params_buffer earlier + // current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; + + params_deep_copy(op_type_to_params_type(current_op), + dev_ctrl.op_params_buffer[dev_ctrl.next_op_out], + ¤t_op_params); + if (current_op_params == NULL) { printf("Params are null!\n"); } + print_alloc_params(current_op_params); dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; // release resources allocated in specialisation of recv_ctrl_params // Alternativally comment this out and handle freeing in those // specialisations -- see commented out first few lines in recv_uint_params. - // free(dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]); - // dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; - // NOTE: 1: went with freeing in recievers solution - // NOTE: 2: since we have ring buffer, resources will be freed on snd pass - - // decrease the number of queued operations and advance next_op_out + free(dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]); // allocated in + // recv_ctrl_params + dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; + // NOTE:1 -- 01.05.2026: went with freeing in recievers solution + // NOTE:2 -- 01.05.2026: since we have ring buffer, resources will be freed + // on snd pass + // NOTE:3 -- 05.05.2026: actually reverted and went with freeing resources + // here as sometimes I want update params in the reciever (see end of this + // function) + + // const size_t last_op_idx = dev_ctrl.next_op_out; + + // decrease the number of queued operations and advance next_op_out --dev_ctrl.num_ops; ++dev_ctrl.next_op_out; dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; @@ -477,6 +583,22 @@ void* mpi_device_control_thread(void*) { pthread_mutex_unlock(&dev_ctrl.device_lock); control_registry[current_op](current_op_params); + + // TODO: send updated ctrl_params to host + // also consider if this is the right place for the send... + // NOTE:4 -- 05.05.2026: This is quite nasty because: + // a) I don't wont to block thread for control_registry duration + // b) but I need to free the resources after and lock the dev_ctrl + // c) but meanwhile, other threads can modify dev_ctrl... + if (op_type_to_params_type(current_op) != PARAMS_UINT_T) { + struct device_ctrl_params params_to_sync = { + .type = op_type_to_params_type(current_op), + .data = current_op_params}; + print_alloc_params(params_to_sync.data); + const int host_rank = CQ_MPI_HOST_RANK; + send_ctrl_params(¶ms_to_sync, host_rank); + } + free(current_op_params); // allocated in params_deep_copy(); } pthread_mutex_unlock(&dev_ctrl.device_lock); @@ -503,7 +625,7 @@ int mpi_finalise_device(const unsigned int VERBOSITY) { unsigned int verbosity = VERBOSITY; struct device_ctrl_params params = {.type = PARAMS_UINT_T, .data = &verbosity}; - mpi_host_comm_ctrl_op(CQ_CTRL_FINALISE, params); + mpi_host_comm_ctrl_op(CQ_CTRL_FINALISE, ¶ms); if (mpi_env.rank != CQ_MPI_HOST_RANK) { pthread_join(dev_ctrl.device_thread, NULL); diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index b3a6cf0..e658e43 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -27,31 +27,44 @@ struct device_ctrl_params { void* data; }; +const char* get_comm_source(); void print_params_header(struct ctrl_params_header header); void print_alloc_params(void* params); void print_op(const enum ctrl_code OP); typedef void (*param_packer_fn)(void* src, void* dest); -void pack_uint_params(void* src, void* dest); -void pack_alloc_params(void* src, void* dest); -void pack_exec_params(void* src, void* dest); -// or send_xxx_params and recv_xxx_params -// void send_uint_params(void* params); -// void send_alloc_params(void* params); -// void send_exec_params(void* params); +enum ctrl_params_datatype op_type_to_params_type(enum ctrl_code op_type); -void send_ctrl_params(struct device_ctrl_params ctrl_params); -void send_uint_params(struct device_ctrl_params ctrl_params); +void params_deep_copy(enum ctrl_params_datatype params_type, + void* src, + void** dest); + +// void pack_uint_params(void* src, void* dest); +// void pack_alloc_params(void* src, void* dest); +// void pack_exec_params(void* src, void* dest); +// or send_xxx_params and recv_xxx_params +// void send_uint_params(void* params); +// void send_alloc_params(void* params); +// void send_exec_params(void* params); + +// TODO: const what should be const + +// NOTE: currently sending 2 messages, header than the payload +// could do 1 message and first probe the size on the recv side +// but probe might be actually slower -- benchmark? +void send_ctrl_params(const struct device_ctrl_params* ctrl_params, int dest); + +void send_uint_params(struct device_ctrl_params ctrl_params, int dest); // TODO: Need to send the updated params back from device to host. -void send_alloc_params(struct device_ctrl_params ctrl_params); -void send_exec_params(struct device_ctrl_params ctrl_params); +void send_alloc_params(struct device_ctrl_params ctrl_params, int dest); +void send_exec_params(struct device_ctrl_params ctrl_params, int dest); -void recv_ctrl_params(void** params); -void recv_uint_params(void** params, int params_size); -void recv_alloc_params(void** params, int params_size); -void recv_exec_params(void** params, int params_size); +void recv_ctrl_params(void** params, int src); +void recv_uint_params(void** params, int params_size, int src); +void recv_alloc_params(void** params, int params_size, int src); +void recv_exec_params(void** params, int params_size, int src); // void comm_ctrl_params(struct ctrl_params_header msg_header, // void* params, @@ -63,10 +76,10 @@ void recv_exec_params(void** params, int params_size); int mpi_initialise_device(const unsigned int VERBOSITY); void mpi_host_comm_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params ctrl_params); + struct device_ctrl_params* ctrl_params); size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params ctrl_params); + struct device_ctrl_params* ctrl_params); // TODO: rename -- it is device comms thread that runs this size_t mpi_host_recv_ctrl_op(void); From df2b0743fd36d22354607d08d60916f724bbd249 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Wed, 6 May 2026 12:15:44 +0100 Subject: [PATCH 04/38] WIP: Modifying the design of the comms. --- src/host-device/mpi_comms.c | 795 ------------------------------- src/host-device/mpi_comms.c.OLD | 801 ++++++++++++++++++++++++++++++++ src/host-device/mpi_comms.h | 104 ----- src/host-device/mpi_comms.h.OLD | 112 +++++ 4 files changed, 913 insertions(+), 899 deletions(-) create mode 100644 src/host-device/mpi_comms.c.OLD create mode 100644 src/host-device/mpi_comms.h.OLD diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 23fffb4..e69de29 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -1,795 +0,0 @@ -// I think that the comms.h should stay the same (and ideally -// the rest of the code) and if we compile with MPI build, then -// we link against this file. Otherwise against comms.c - -#include "mpi_comms.h" -#include -#include -#include -#include "datatypes.h" -#include "src/device/control.h" -#include "src/host-device/comms.h" -#include "src/host/opcodes.h" - -#include -#include -#include - -static MPI_Comm cq_mpi_comm = MPI_COMM_WORLD; - -struct cq_mpi_env { - int rank; -}; - -static struct cq_mpi_env mpi_env; - -const char* get_comm_source() { - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - return "Host:\t\t"; - } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { - return "Device:\t\t"; - } else { - return ""; - } -} -void print_params_header(struct ctrl_params_header header) { - // if (mpi_env.rank == CQ_MPI_HOST_RANK) { - // printf("Host [header]: "); - // } else { - // printf("Device [header]: "); - // } - switch (header.type) { - case PARAMS_UINT_T: { - printf("%s [header]: type: UINT, ", get_comm_source()); - break; - } - case PARAMS_ALLOC_T: { - // printf("type: ALLOC, "); - printf("%s [header]: type: ALLOC, ", get_comm_source()); - break; - } - case PARAMS_EXEC_T: { - // printf("type: EXEC, "); - printf("%s [header]: type: EXEC, ", get_comm_source()); - break; - } - default: { - break; - } - } - printf(" %d\n", header.params_msg_size); -} - -void print_alloc_params(void* params) { - device_alloc_params* alloc_params = (device_alloc_params*)params; - - printf("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", - get_comm_source(), alloc_params->NQUBITS, alloc_params->qregistry_idx, - alloc_params->status); -} - -void print_op(const enum ctrl_code OP) { - switch (OP) { - case CQ_CTRL_IDLE: { - printf("CQ_CTRL_IDLE"); - break; - } - case CQ_CTRL_ALLOC: { - printf("CQ_CTRL_ALLOC"); - break; - } - case CQ_CTRL_DEALLOC: { - printf("CQ_CTRL_DEALLOC"); - break; - } - case CQ_CTRL_INIT: { - printf("CQ_CTRL_INIT"); - break; - } - case CQ_CTRL_FINALISE: { - printf("CQ_CTRL_FINALISE"); - break; - } - case CQ_CTRL_RUN_QKERNEL: { - printf("CQ_CTRL_RUN_QKERNEL"); - break; - } - case CQ_CTRL_RUN_PQKERNEL: { - printf("CQ_CTRL_RUN_PQKERNEL"); - break; - } - case CQ_CTRL_TEST: { - printf("CQ_CTRL_TEST"); - break; - } - case CQ_CTRL_ABORT: { - printf("CQ_CTRL_ABORT"); - break; - } - default: { - break; - } - } - printf("\n"); -} - -enum ctrl_params_datatype op_type_to_params_type(enum ctrl_code op_type) - -{ - switch (op_type) { - case CQ_CTRL_ALLOC: - return PARAMS_ALLOC_T; - case CQ_CTRL_DEALLOC: - return PARAMS_ALLOC_T; - case CQ_CTRL_INIT: - return PARAMS_UINT_T; - case CQ_CTRL_FINALISE: - return PARAMS_UINT_T; - case CQ_CTRL_RUN_QKERNEL: - return PARAMS_EXEC_T; - case CQ_CTRL_RUN_PQKERNEL: - return PARAMS_EXEC_T; - default: { - printf("Unhandled op type. Returning PARAMS_UINT_T. Expect segfault.\n"); - return PARAMS_UINT_T; - }; - } -} - -size_t static get_params_size(enum ctrl_params_datatype type) { - switch (type) { - case PARAMS_UINT_T: { - return sizeof(unsigned int); - break; - } - case PARAMS_ALLOC_T: { - return sizeof(device_alloc_params); - break; - } - case PARAMS_EXEC_T: { - return 0; - break; - } - default: { - return 0; - break; - } - } -} - -void params_deep_copy(enum ctrl_params_datatype params_type, - void* src, - void** dest) { - const size_t params_size = get_params_size(params_type); - *dest = malloc(params_size); - memcpy(*dest, src, params_size); -} - -// void send_uint_params(void* params) { -// TODO: do the destination parameter -void send_uint_params(struct device_ctrl_params ctrl_params, int dest) { - MPI_Datatype uint_type = - sizeof(unsigned int) == 4 ? MPI_UINT32_T : MPI_UINT64_T; - - struct ctrl_params_header msg_header = { - .type = ctrl_params.type, .params_msg_size = sizeof(unsigned int)}; - int header_size = sizeof(struct ctrl_params_header); - - MPI_Ssend(&msg_header, header_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm); - MPI_Ssend(ctrl_params.data, 1, uint_type, dest, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm); -} - -void recv_uint_params(void** params, int params_size, int src) { - printf("%s Attempting to recieve verbosity param...\n", get_comm_source()); - if (params == NULL) { - printf("From recv_uint_params: params is NULL.\n"); - exit(-2); - } - - // free previous, now unused resources - if (*params != NULL) { - printf("From recv_uint_params: *params is NULL.\n"); - exit(-2); - - free(params); - *params = NULL; - } - - MPI_Status status; - *params = malloc(params_size); - if (*params == NULL) { - printf("From recv_uint_params: malloc failed.\n"); - exit(-2); - } - - MPI_Recv(*params, params_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm, &status); -} - -// void send_alloc_params(void* params) { -void send_alloc_params(struct device_ctrl_params ctrl_params, int dest) { - // TODO: use memcpy because no pointers in alloc params - // - // pack device_alloc_params - // device_alloc_params* alloc_params = - // (device_alloc_params*)ctrl_params.data; int max_buffer_size; - // MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &max_buffer_size); // NQUBITS - // int member_size; - // MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &member_size); // - // qregistry_idx max_buffer_size += member_size; MPI_Pack_size(1, MPI_INT, - // cq_mpi_comm, &member_size); // status max_buffer_size += member_size; - // - // char* send_buffer = malloc(max_buffer_size); - // if (send_buffer == NULL) { - // printf("Failed to allocate buffer for sending device allocation - // params.\n"); exit(-1); - // } - // int position; - // MPI_Pack(&alloc_params->NQUBITS, 1, MPI_UINT64_T, send_buffer, - // max_buffer_size, &position, cq_mpi_comm); - // MPI_Pack(&alloc_params->qregistry_idx, 1, MPI_UINT64_T, send_buffer, - // max_buffer_size, &position, cq_mpi_comm); - // MPI_Pack(&alloc_params->status, 1, MPI_INT, send_buffer, max_buffer_size, - // &position, cq_mpi_comm); - - // all nicely packed - // struct ctrl_params_header msg_header = {.type = ctrl_params.type, - // .params_msg_size = position}; - - printf("%s Attempting to send device_alloc_params...\n", get_comm_source()); - size_t buffer_size = sizeof(device_alloc_params); - void* send_buffer = malloc(buffer_size); - memcpy(send_buffer, ctrl_params.data, buffer_size); - struct ctrl_params_header msg_header = {.type = ctrl_params.type, - .params_msg_size = buffer_size}; - - int header_size = sizeof(struct ctrl_params_header); - - MPI_Ssend(&msg_header, header_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm); - MPI_Ssend(send_buffer, buffer_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm); - - // MPI_Ssend(send_buffer, position, MPI_PACKED, device_rank, - // CQ_HOST_DEVICE_MPI_TAG, - // cq_mpi_comm); - - free(send_buffer); - printf("%s Sent device_alloc_params...\n", get_comm_source()); -} - -void recv_alloc_params(void** params, int params_size, int src) { - printf("%s Attempting to recieve device_alloc_params...\n", - get_comm_source()); - if (params == NULL) { - printf("From recv_alloc_params: params is NULL.\n"); - exit(-2); - } - - // free previous, now unused resources - if (*params != NULL) { - printf("From recv_alloc_params: *params is NULL.\n"); - exit(-2); - free(params); - *params = NULL; - } - - MPI_Status status; - *params = malloc(params_size); - if (*params == NULL) { - printf("From recv_alloc_params: malloc failed.\n"); - exit(-2); - } - - MPI_Recv(*params, params_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm, &status); - printf("%s Recieved device_alloc_params...\n", get_comm_source()); -} - -void send_exec_params(struct device_ctrl_params ctrl_params, int dest) {} -void recv_exec_params(void** params, int params_size, int src) {} - -// void comm_ctrl_params(struct ctrl_params_header msg_header, -// void* params, -// param_packer_fn packer) { -// if (mpi_env.rank == CQ_MPI_HOST_RANK) { -// send_ctrl_params(msg_header, params, packer); -// } else { -// recv_ctrl_params(); -// } -// } - -// void send_ctrl_params(struct ctrl_params_header msg_header, -// void* params, -// param_packer_fn packer) { -// int device_rank = 1; -// size_t header_size = sizeof(struct ctrl_params_header); -// MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, -// CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); -// print_params_header(msg_header); - -void send_ctrl_params(const struct device_ctrl_params* ctrl_params, int dest) { - switch (ctrl_params->type) { - case PARAMS_UINT_T: { - send_uint_params(*ctrl_params, dest); - break; - } - case PARAMS_ALLOC_T: { - send_alloc_params(*ctrl_params, dest); - break; - } - case PARAMS_EXEC_T: { - send_exec_params(*ctrl_params, dest); - break; - } - default: { - break; - } - } -} - -void recv_ctrl_params(void** params, int src) { - struct ctrl_params_header msg_header; - size_t header_size = sizeof(struct ctrl_params_header); - MPI_Status status; - MPI_Recv(&msg_header, header_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm, &status); - print_params_header(msg_header); - switch (msg_header.type) { - case PARAMS_UINT_T: { - recv_uint_params(params, msg_header.params_msg_size, src); - break; - } - case PARAMS_ALLOC_T: { - recv_alloc_params(params, msg_header.params_msg_size, src); - break; - } - case PARAMS_EXEC_T: { - recv_exec_params(params, msg_header.params_msg_size, src); - break; - } - default: { - break; - } - } -} - -int mpi_initialise_device(const unsigned int VERBOSITY) { - printf("Initialising MPI.\n"); - MPI_Init(NULL, NULL); - printf("Initialised MPI.\n"); - MPI_Comm_rank(MPI_COMM_WORLD, &mpi_env.rank); - - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - if (VERBOSITY > 0) { - printf("Initialising device.\n"); - } - pthread_mutex_init(&dev_ctrl.device_lock, NULL); - pthread_cond_init(&dev_ctrl.cond_device_busy, NULL); - pthread_cond_init(&dev_ctrl.cond_queue_empty, NULL); - pthread_cond_init(&dev_ctrl.cond_queue_full, NULL); - - dev_ctrl.run_device = true; - dev_ctrl.device_busy = true; - dev_ctrl.num_ops = 0; - dev_ctrl.next_op_in = 0; - dev_ctrl.next_op_out = 0; - - for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { - dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; - dev_ctrl.op_params_buffer[i] = NULL; - } - - pthread_create(&dev_ctrl.device_thread, NULL, &mpi_device_control_thread, - NULL); - } - - unsigned int verbosity = VERBOSITY; - // struct ctrl_params_header msg_header = { - // .type = PARAMS_UINT_T, .params_msg_size = sizeof(unsigned int)}; - // comm_ctrl_params(msg_header, &verbosity, &pack_uint_params); - - struct device_ctrl_params params = {.type = PARAMS_UINT_T, - .data = &verbosity}; - mpi_host_comm_ctrl_op(CQ_CTRL_INIT, ¶ms); - printf("Finished: comm_ctrl_op\n"); - mpi_host_wait_all_ops(); - printf("Finished: wait_all_ops\n"); - - return 0; -} - -void mpi_host_comm_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params* ctrl_params) { - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - mpi_host_send_ctrl_op(OP, ctrl_params); - printf("%s [comm_ctrl_op]: finished sending ctrl op\n", get_comm_source()); - } else { - mpi_host_recv_ctrl_op(); - printf("%s [comm_ctrl_op]: finished recv ctrl op\n", get_comm_source()); - } -} - -// TODO: Need to know the size of ctrl_params -// maybe instead have struct like: -// struct CtrlParams { -// void* data; -// int size; -// } -// and wrap it whenever there is call to host_send_ctrl_op -size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params* ctrl_params) { - const int device_rank = CQ_MPI_DEVICE_RANK; - // TODO: merge sends? - MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - printf("%s [send_ctrl_op]: Sent ", get_comm_source()); - print_op(OP); - send_ctrl_params(ctrl_params, device_rank); - - size_t num_ops = 0; - MPI_Status status; - MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm, &status); - printf("%s recv num ops: %zu\n", get_comm_source(), num_ops); - - // alloc and dealloc should be blocking - if (OP == CQ_CTRL_ALLOC || OP == CQ_CTRL_DEALLOC) { - void* params_buffer = NULL; - recv_ctrl_params(¶ms_buffer, device_rank); - memcpy(ctrl_params->data, params_buffer, sizeof(device_alloc_params)); - printf("Performed deep copy\n"); - print_alloc_params(ctrl_params->data); - } - - // rest handled by executor - - return num_ops; -} - -// TODO: rename -- it is device comms thread that runs this -size_t mpi_host_recv_ctrl_op() { - const int host_rank = CQ_MPI_HOST_RANK; - pthread_mutex_lock(&dev_ctrl.device_lock); - - while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { - // the control queue is full! - // we'll wait for it to not be full - pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); - } - - MPI_Status status; - MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_INT, host_rank, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); - printf("%s [recv_ctrl_op]: Recieved ", get_comm_source()); - print_op(dev_ctrl.op_buffer[dev_ctrl.next_op_in]); - - recv_ctrl_params(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in], host_rank); - if (dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_ALLOC || - dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_DEALLOC) { - print_alloc_params(dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); - } else { - printf("%s verbosity params: VERBOSITY: %d\n", get_comm_source(), - *(unsigned int*)dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); - } - - ++dev_ctrl.num_ops; - - // It's a ring buffer! - // advance next_op_in then mod out buffer size - ++dev_ctrl.next_op_in; - dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; - - MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - printf("%s [recv_ctrl_op]: sent num ops: %zu\n", get_comm_source(), - dev_ctrl.num_ops); - - pthread_cond_signal(&dev_ctrl.cond_queue_empty); - pthread_mutex_unlock(&dev_ctrl.device_lock); - - return dev_ctrl.num_ops; -} - -size_t mpi_host_wait_all_ops(void) { - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - const int device_rank = CQ_MPI_DEVICE_RANK; - size_t num_ops = 0; - MPI_Status status; - printf("%s [wait_all_ops]: got here\n", get_comm_source()); - // rather than waiting for num ops, wait for the ctrl params and update - // recv_ctrl_params(void** params, int src) - MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm, &status); - return num_ops; - - } else { - const int host_rank = CQ_MPI_HOST_RANK; - pthread_mutex_lock(&dev_ctrl.device_lock); - int foo = 0; - while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { - printf("%s [wait_all_ops][loop]: got here %d\n", get_comm_source(), foo); - ++foo; - pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); - } - printf("%s [wait_all_ops]: got here\n", get_comm_source()); - // rather than sending num ops, device_control_thread sends updated params? - // but also maybe I don't want MPI inside device_control_thread - MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - - pthread_mutex_unlock(&dev_ctrl.device_lock); - return dev_ctrl.num_ops; - } -} - -void* mpi_device_control_thread(void*) { - enum ctrl_code current_op = CQ_CTRL_IDLE; - void* current_op_params = NULL; - - // run_device set to FALSE at cq_finalise - while (dev_ctrl.run_device) { - pthread_mutex_lock(&dev_ctrl.device_lock); - - while (dev_ctrl.num_ops <= 0) { - // wait for a new op to be posted - dev_ctrl.device_busy = false; - pthread_cond_signal(&dev_ctrl.cond_device_busy); - pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); - } - - dev_ctrl.device_busy = true; - - // take the next op and params out of the dev_ctrl buffer, and then tidy - // up the dev_ctrl buffer - current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; - - // TODO: Can solve NOTE 4 (see below) by doing deep copy here... - // and freeing op_params_buffer earlier - // current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; - - params_deep_copy(op_type_to_params_type(current_op), - dev_ctrl.op_params_buffer[dev_ctrl.next_op_out], - ¤t_op_params); - - if (current_op_params == NULL) { - printf("Params are null!\n"); - } - print_alloc_params(current_op_params); - dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; - // release resources allocated in specialisation of recv_ctrl_params - // Alternativally comment this out and handle freeing in those - // specialisations -- see commented out first few lines in recv_uint_params. - free(dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]); // allocated in - // recv_ctrl_params - dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; - // NOTE:1 -- 01.05.2026: went with freeing in recievers solution - // NOTE:2 -- 01.05.2026: since we have ring buffer, resources will be freed - // on snd pass - // NOTE:3 -- 05.05.2026: actually reverted and went with freeing resources - // here as sometimes I want update params in the reciever (see end of this - // function) - - // const size_t last_op_idx = dev_ctrl.next_op_out; - - // decrease the number of queued operations and advance next_op_out - --dev_ctrl.num_ops; - ++dev_ctrl.next_op_out; - dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; - - // signal that the queue is no longer full and then relinquish mutex - pthread_cond_signal(&dev_ctrl.cond_queue_full); - pthread_mutex_unlock(&dev_ctrl.device_lock); - - control_registry[current_op](current_op_params); - - // TODO: send updated ctrl_params to host - // also consider if this is the right place for the send... - // NOTE:4 -- 05.05.2026: This is quite nasty because: - // a) I don't wont to block thread for control_registry duration - // b) but I need to free the resources after and lock the dev_ctrl - // c) but meanwhile, other threads can modify dev_ctrl... - if (op_type_to_params_type(current_op) != PARAMS_UINT_T) { - struct device_ctrl_params params_to_sync = { - .type = op_type_to_params_type(current_op), - .data = current_op_params}; - print_alloc_params(params_to_sync.data); - const int host_rank = CQ_MPI_HOST_RANK; - send_ctrl_params(¶ms_to_sync, host_rank); - } - free(current_op_params); // allocated in params_deep_copy(); - } - - pthread_mutex_unlock(&dev_ctrl.device_lock); - - return NULL; -} - -int mpi_finalise_device(const unsigned int VERBOSITY) { - // Politely wait for the device to finish its current business - // otherwise setting dev_ctrl.run_device = false might break - // some stuff, and this should only be called in cq_finalise() - mpi_host_wait_all_ops(); - - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - if (VERBOSITY > 0) { - printf("Finalising device.\n"); - } - - pthread_mutex_lock(&dev_ctrl.device_lock); - dev_ctrl.run_device = false; - pthread_mutex_unlock(&dev_ctrl.device_lock); - } - - unsigned int verbosity = VERBOSITY; - struct device_ctrl_params params = {.type = PARAMS_UINT_T, - .data = &verbosity}; - mpi_host_comm_ctrl_op(CQ_CTRL_FINALISE, ¶ms); - - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - pthread_join(dev_ctrl.device_thread, NULL); - - dev_ctrl.device_busy = false; - - pthread_cond_destroy(&dev_ctrl.cond_device_busy); - pthread_cond_destroy(&dev_ctrl.cond_queue_empty); - pthread_cond_destroy(&dev_ctrl.cond_queue_full); - pthread_mutex_destroy(&dev_ctrl.device_lock); - } - - MPI_Finalize(); - return 0; -} -// struct DeviceLink dev_ctrl; -// -// int mpi_initialise_device(const unsigned int VERBOSITY) { -// if (VERBOSITY > 0) { -// printf("Initialising device.\n"); -// } -// -// int nprocs; -// int world_rank; -// MPI_Comm comm_split; -// -// MPI_Init(NULL, NULL); -// -// MPI_Comm_size(MPI_COMM_WORLD, &nprocs); -// MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); -// // int in_quest_comm = world_rank != 0; -// // MPI_Comm_split(MPI_COMM_WORLD, I_AM_QUANTUM, world_rank, &comm_split); -// -// dev_ctrl.run_device = true; -// dev_ctrl.device_busy = true; -// dev_ctrl.num_ops = 0; -// dev_ctrl.next_op_in = 0; -// dev_ctrl.next_op_out = 0; -// -// for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { -// dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; -// dev_ctrl.op_params_buffer[i] = NULL; -// } -// -// unsigned int verbosity = VERBOSITY; -// mpi_host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); -// mpi_host_wait_all_ops(); -// -// return 0; -// } -// -// size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, void* ctrl_params) { -// // 1. need to request from device queue size... -// // 2. send OP for device op_buffer -// int rank; -// MPI_Comm_rank(MPI_COMM_WORLD, &rank); -// if (rank == 0) { -// MPI_Status status; -// int device_rank = 1; -// -// while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { -// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD, &status); -// } -// MPI_Ssend(&OP, 1, MPI_INT, device_rank, 0, MPI_COMM_WORLD); -// // TODO: this needs solution -// // MPI_Ssend(ctrl_params, ctrl_params_extent, MPI_CHAR, device_rank, 0, -// // MPI_COMM_WORLD); -// -// ++dev_ctrl.num_ops; -// ++dev_ctrl.next_op_in; -// dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; -// -// // NOTE: matched by Recv on lines 120 and 122 -// MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD); -// MPI_Ssend(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD); -// -// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD, -// &status); -// MPI_Recv(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD, &status); -// -// } else { -// // NOTE: okay, I think it's simply wrong place for this code -// // actually we leave host rank as it is and this code goes to the device -// // code or something -// // int host_rank = 0; -// // MPI_Status status; -// // -// // // NOTE: if the device queue is full this will deadlock -// // // instead we have to call host_send_ctrl_op with a modification -// // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, -// // MPI_COMM_WORLD); -// // -// // MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_UINT64_T, -// // host_rank, 0, MPI_COMM_WORLD, &status); -// // -// // // TODO: this is tricky and needs solving -// // // MPI_Recv(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in], -// // // ctrl_params_extent, MPI_CHAR, -// // // host_rank, 0, MPI_COMM_WORLD, &status); -// // -// // ++dev_ctrl.num_ops; -// // ++dev_ctrl.next_op_in; -// // dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; -// // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, -// // MPI_COMM_WORLD); MPI_Ssend(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, -// // host_rank, 0, -// // MPI_COMM_WORLD); -// // } -// } -// return dev_ctrl.num_ops; -// } -// -// void* mpi_device_control_thread(void* par) { -// enum ctrl_code current_op = CQ_CTRL_IDLE; -// void* current_op_params = NULL; -// int host_rank = 0; -// MPI_Status status; -// -// // run_device set to FALSE at cq_finalise -// while (dev_ctrl.run_device) { -// // pthread_mutex_lock(&dev_ctrl.device_lock); -// -// while (dev_ctrl.num_ops <= 0) { -// // wait for a new op to be posted -// dev_ctrl.device_busy = false; -// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, -// MPI_COMM_WORLD, -// &status); -// MPI_Recv(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, host_rank, 0, -// MPI_COMM_WORLD, &status); -// -// pthread_cond_signal(&dev_ctrl.cond_device_busy); -// pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); -// } -// MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_UINT64_T, -// host_rank, 0, MPI_COMM_WORLD, &status); -// -// dev_ctrl.device_busy = true; -// -// // take the next op and params out of the dev_ctrl buffer, and then tidy -// // up the dev_ctrl buffer -// current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; -// current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; -// dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; -// dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; -// -// // decrease the number of queued operations and advance next_op_out -// --dev_ctrl.num_ops; -// ++dev_ctrl.next_op_out; -// dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; -// -// // signal that the queue is no longer full and then relinquish mutex -// pthread_cond_signal(&dev_ctrl.cond_queue_full); -// pthread_mutex_unlock(&dev_ctrl.device_lock); -// -// control_registry[current_op](current_op_params); -// } -// -// pthread_mutex_unlock(&dev_ctrl.device_lock); -// -// return NULL; -// } diff --git a/src/host-device/mpi_comms.c.OLD b/src/host-device/mpi_comms.c.OLD new file mode 100644 index 0000000..b948e76 --- /dev/null +++ b/src/host-device/mpi_comms.c.OLD @@ -0,0 +1,801 @@ +// I think that the comms.h should stay the same (and ideally +// the rest of the code) and if we compile with MPI build, then +// we link against this file. Otherwise against comms.c + +#include "mpi_comms.h" +#include +#include +#include +#include "datatypes.h" +#include "src/device/control.h" +#include "src/host-device/comms.h" +#include "src/host/opcodes.h" + +#include +#include +#include + +static MPI_Comm cq_mpi_comm = MPI_COMM_WORLD; + +struct cq_mpi_env { + int rank; +}; + +static struct cq_mpi_env mpi_env; + +const char* get_comm_source() { + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + return "Host:\t\t"; + } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { + return "Device:\t\t"; + } else { + return ""; + } +} +void print_params_header(struct ctrl_params_header header) { + // if (mpi_env.rank == CQ_MPI_HOST_RANK) { + // printf("Host [header]: "); + // } else { + // printf("Device [header]: "); + // } + switch (header.type) { + case PARAMS_UINT_T: { + printf("%s [header]: type: UINT, ", get_comm_source()); + break; + } + case PARAMS_ALLOC_T: { + // printf("type: ALLOC, "); + printf("%s [header]: type: ALLOC, ", get_comm_source()); + break; + } + case PARAMS_EXEC_T: { + // printf("type: EXEC, "); + printf("%s [header]: type: EXEC, ", get_comm_source()); + break; + } + default: { + break; + } + } + printf(" %d\n", header.params_msg_size); +} + +void print_alloc_params(void* params) { + device_alloc_params* alloc_params = (device_alloc_params*)params; + + printf("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", + get_comm_source(), alloc_params->NQUBITS, alloc_params->qregistry_idx, + alloc_params->status); +} + +void print_op(const enum ctrl_code OP) { + switch (OP) { + case CQ_CTRL_IDLE: { + printf("CQ_CTRL_IDLE"); + break; + } + case CQ_CTRL_ALLOC: { + printf("CQ_CTRL_ALLOC"); + break; + } + case CQ_CTRL_DEALLOC: { + printf("CQ_CTRL_DEALLOC"); + break; + } + case CQ_CTRL_INIT: { + printf("CQ_CTRL_INIT"); + break; + } + case CQ_CTRL_FINALISE: { + printf("CQ_CTRL_FINALISE"); + break; + } + case CQ_CTRL_RUN_QKERNEL: { + printf("CQ_CTRL_RUN_QKERNEL"); + break; + } + case CQ_CTRL_RUN_PQKERNEL: { + printf("CQ_CTRL_RUN_PQKERNEL"); + break; + } + case CQ_CTRL_TEST: { + printf("CQ_CTRL_TEST"); + break; + } + case CQ_CTRL_ABORT: { + printf("CQ_CTRL_ABORT"); + break; + } + default: { + break; + } + } + printf("\n"); +} + +enum ctrl_params_datatype op_type_to_params_type(enum ctrl_code op_type) + +{ + switch (op_type) { + case CQ_CTRL_ALLOC: + return PARAMS_ALLOC_T; + case CQ_CTRL_DEALLOC: + return PARAMS_ALLOC_T; + case CQ_CTRL_INIT: + return PARAMS_UINT_T; + case CQ_CTRL_FINALISE: + return PARAMS_UINT_T; + case CQ_CTRL_RUN_QKERNEL: + return PARAMS_EXEC_T; + case CQ_CTRL_RUN_PQKERNEL: + return PARAMS_EXEC_T; + default: { + printf("Unhandled op type. Returning PARAMS_UINT_T. Expect segfault.\n"); + return PARAMS_UINT_T; + }; + } +} + +size_t static get_params_size(enum ctrl_params_datatype type) { + switch (type) { + case PARAMS_UINT_T: { + return sizeof(unsigned int); + break; + } + case PARAMS_ALLOC_T: { + return sizeof(device_alloc_params); + break; + } + case PARAMS_EXEC_T: { + return 0; + break; + } + default: { + return 0; + break; + } + } +} + +void params_deep_copy(enum ctrl_params_datatype params_type, + void* src, + void** dest) { + const size_t params_size = get_params_size(params_type); + *dest = malloc(params_size); + memcpy(*dest, src, params_size); +} + +// void send_uint_params(void* params) { +// TODO: do the destination parameter +void send_uint_params(struct device_ctrl_params ctrl_params, int dest) { + MPI_Datatype uint_type = + sizeof(unsigned int) == 4 ? MPI_UINT32_T : MPI_UINT64_T; + + struct ctrl_params_header msg_header = { + .type = ctrl_params.type, .params_msg_size = sizeof(unsigned int)}; + int header_size = sizeof(struct ctrl_params_header); + + MPI_Ssend(&msg_header, header_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm); + MPI_Ssend(ctrl_params.data, 1, uint_type, dest, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm); +} + +void recv_uint_params(void** params, int params_size, int src) { + printf("%s Attempting to recieve verbosity param...\n", get_comm_source()); + if (params == NULL) { + printf("From recv_uint_params: params is NULL.\n"); + exit(-2); + } + + // free previous, now unused resources + if (*params != NULL) { + printf("From recv_uint_params: *params is NULL.\n"); + exit(-2); + + free(params); + *params = NULL; + } + + MPI_Status status; + *params = malloc(params_size); + if (*params == NULL) { + printf("From recv_uint_params: malloc failed.\n"); + exit(-2); + } + + MPI_Recv(*params, params_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm, &status); +} + +// void send_alloc_params(void* params) { +void send_alloc_params(struct device_ctrl_params ctrl_params, int dest) { + // TODO: use memcpy because no pointers in alloc params + // + // pack device_alloc_params + // device_alloc_params* alloc_params = + // (device_alloc_params*)ctrl_params.data; int max_buffer_size; + // MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &max_buffer_size); // NQUBITS + // int member_size; + // MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &member_size); // + // qregistry_idx max_buffer_size += member_size; MPI_Pack_size(1, MPI_INT, + // cq_mpi_comm, &member_size); // status max_buffer_size += member_size; + // + // char* send_buffer = malloc(max_buffer_size); + // if (send_buffer == NULL) { + // printf("Failed to allocate buffer for sending device allocation + // params.\n"); exit(-1); + // } + // int position; + // MPI_Pack(&alloc_params->NQUBITS, 1, MPI_UINT64_T, send_buffer, + // max_buffer_size, &position, cq_mpi_comm); + // MPI_Pack(&alloc_params->qregistry_idx, 1, MPI_UINT64_T, send_buffer, + // max_buffer_size, &position, cq_mpi_comm); + // MPI_Pack(&alloc_params->status, 1, MPI_INT, send_buffer, max_buffer_size, + // &position, cq_mpi_comm); + + // all nicely packed + // struct ctrl_params_header msg_header = {.type = ctrl_params.type, + // .params_msg_size = position}; + + printf("%s Attempting to send device_alloc_params...\n", get_comm_source()); + size_t buffer_size = sizeof(device_alloc_params); + void* send_buffer = malloc(buffer_size); + memcpy(send_buffer, ctrl_params.data, buffer_size); + struct ctrl_params_header msg_header = {.type = ctrl_params.type, + .params_msg_size = buffer_size}; + + int header_size = sizeof(struct ctrl_params_header); + + MPI_Ssend(&msg_header, header_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm); + MPI_Ssend(send_buffer, buffer_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm); + + // MPI_Ssend(send_buffer, position, MPI_PACKED, device_rank, + // CQ_HOST_DEVICE_MPI_TAG, + // cq_mpi_comm); + + free(send_buffer); + printf("%s Sent device_alloc_params...\n", get_comm_source()); +} + +void recv_alloc_params(void** params, int params_size, int src) { + printf("%s Attempting to recieve device_alloc_params...\n", + get_comm_source()); + if (params == NULL) { + printf("From recv_alloc_params: params is NULL.\n"); + exit(-2); + } + + // free previous, now unused resources + if (*params != NULL) { + printf("From recv_alloc_params: *params is NULL.\n"); + exit(-2); + free(params); + *params = NULL; + } + + MPI_Status status; + *params = malloc(params_size); + if (*params == NULL) { + printf("From recv_alloc_params: malloc failed.\n"); + exit(-2); + } + + MPI_Recv(*params, params_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm, &status); + printf("%s Recieved device_alloc_params...\n", get_comm_source()); +} + +void send_exec_params(struct device_ctrl_params ctrl_params, int dest) {} +void recv_exec_params(void** params, int params_size, int src) {} + +// void comm_ctrl_params(struct ctrl_params_header msg_header, +// void* params, +// param_packer_fn packer) { +// if (mpi_env.rank == CQ_MPI_HOST_RANK) { +// send_ctrl_params(msg_header, params, packer); +// } else { +// recv_ctrl_params(); +// } +// } + +// void send_ctrl_params(struct ctrl_params_header msg_header, +// void* params, +// param_packer_fn packer) { +// int device_rank = 1; +// size_t header_size = sizeof(struct ctrl_params_header); +// MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, +// CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); +// print_params_header(msg_header); + +void send_ctrl_params(const struct device_ctrl_params* ctrl_params, int dest) { + switch (ctrl_params->type) { + case PARAMS_UINT_T: { + send_uint_params(*ctrl_params, dest); + break; + } + case PARAMS_ALLOC_T: { + send_alloc_params(*ctrl_params, dest); + break; + } + case PARAMS_EXEC_T: { + send_exec_params(*ctrl_params, dest); + break; + } + default: { + break; + } + } +} + +void recv_ctrl_params(void** params, int src) { + struct ctrl_params_header msg_header; + size_t header_size = sizeof(struct ctrl_params_header); + MPI_Status status; + MPI_Recv(&msg_header, header_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm, &status); + print_params_header(msg_header); + switch (msg_header.type) { + case PARAMS_UINT_T: { + recv_uint_params(params, msg_header.params_msg_size, src); + break; + } + case PARAMS_ALLOC_T: { + recv_alloc_params(params, msg_header.params_msg_size, src); + break; + } + case PARAMS_EXEC_T: { + recv_exec_params(params, msg_header.params_msg_size, src); + break; + } + default: { + break; + } + } +} + +int mpi_initialise_device(const unsigned int VERBOSITY) { + printf("Initialising MPI.\n"); + MPI_Init(NULL, NULL); + printf("Initialised MPI.\n"); + MPI_Comm_rank(MPI_COMM_WORLD, &mpi_env.rank); + + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + if (VERBOSITY > 0) { + printf("Initialising device.\n"); + } + pthread_mutex_init(&dev_ctrl.device_lock, NULL); + pthread_cond_init(&dev_ctrl.cond_device_busy, NULL); + pthread_cond_init(&dev_ctrl.cond_queue_empty, NULL); + pthread_cond_init(&dev_ctrl.cond_queue_full, NULL); + + dev_ctrl.run_device = true; + dev_ctrl.device_busy = true; + dev_ctrl.num_ops = 0; + dev_ctrl.next_op_in = 0; + dev_ctrl.next_op_out = 0; + + for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { + dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; + dev_ctrl.op_params_buffer[i] = NULL; + } + + pthread_create(&dev_ctrl.device_thread, NULL, &mpi_device_control_thread, + NULL); + } + + unsigned int verbosity = VERBOSITY; + // struct ctrl_params_header msg_header = { + // .type = PARAMS_UINT_T, .params_msg_size = sizeof(unsigned int)}; + // comm_ctrl_params(msg_header, &verbosity, &pack_uint_params); + + struct device_ctrl_params params = {.type = PARAMS_UINT_T, + .data = &verbosity}; + mpi_host_comm_ctrl_op(CQ_CTRL_INIT, ¶ms); + printf("Finished: comm_ctrl_op\n"); + mpi_host_wait_all_ops(); + printf("Finished: wait_all_ops\n"); + + return 0; +} + +void mpi_host_comm_ctrl_op(const enum ctrl_code OP, + struct device_ctrl_params* ctrl_params) { + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + mpi_host_send_ctrl_op(OP, ctrl_params); + printf("%s [comm_ctrl_op]: finished sending ctrl op\n", get_comm_source()); + } else { + mpi_host_recv_ctrl_op(); + printf("%s [comm_ctrl_op]: finished recv ctrl op\n", get_comm_source()); + } +} + +// TODO: Need to know the size of ctrl_params +// maybe instead have struct like: +// struct CtrlParams { +// void* data; +// int size; +// } +// and wrap it whenever there is call to host_send_ctrl_op +size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, + struct device_ctrl_params* ctrl_params) { + const int device_rank = CQ_MPI_DEVICE_RANK; + // TODO: merge sends? + MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + printf("%s [send_ctrl_op]: Sent ", get_comm_source()); + print_op(OP); + send_ctrl_params(ctrl_params, device_rank); + + size_t num_ops = 0; + MPI_Status status; + MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_HOST_DEVICE_MPI_TAG, + cq_mpi_comm, &status); + printf("%s recv num ops: %zu\n", get_comm_source(), num_ops); + + // alloc and dealloc should be blocking + if (OP == CQ_CTRL_ALLOC || OP == CQ_CTRL_DEALLOC) { + void* params_buffer = NULL; + recv_ctrl_params(¶ms_buffer, device_rank); + memcpy(ctrl_params->data, params_buffer, sizeof(device_alloc_params)); + printf("Performed deep copy\n"); + print_alloc_params(ctrl_params->data); + } + + // rest handled by executor + + return num_ops; +} + +// TODO: rename -- it is device comms thread that runs this +size_t mpi_host_recv_ctrl_op() { + const int host_rank = CQ_MPI_HOST_RANK; + pthread_mutex_lock(&dev_ctrl.device_lock); + + while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { + // the control queue is full! + // we'll wait for it to not be full + pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); + } + + MPI_Status status; + MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_INT, host_rank, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); + printf("%s [recv_ctrl_op]: Recieved ", get_comm_source()); + print_op(dev_ctrl.op_buffer[dev_ctrl.next_op_in]); + + recv_ctrl_params(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in], host_rank); + if (dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_ALLOC || + dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_DEALLOC) { + print_alloc_params(dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); + } else { + printf("%s verbosity params: VERBOSITY: %d\n", get_comm_source(), + *(unsigned int*)dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); + } + + ++dev_ctrl.num_ops; + + // It's a ring buffer! + // advance next_op_in then mod out buffer size + ++dev_ctrl.next_op_in; + dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; + + MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, + CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + printf("%s [recv_ctrl_op]: sent num ops: %zu\n", get_comm_source(), + dev_ctrl.num_ops); + + pthread_cond_signal(&dev_ctrl.cond_queue_empty); + pthread_mutex_unlock(&dev_ctrl.device_lock); + + return dev_ctrl.num_ops; +} + +size_t mpi_host_wait_all_ops(void) { + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + const int device_rank = CQ_MPI_DEVICE_RANK; + size_t num_ops = 0; + MPI_Status status; + printf("%s [wait_all_ops]: got here\n", get_comm_source()); + // rather than waiting for num ops, wait for the ctrl params and update + // recv_ctrl_params(void** params, int src) + // MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, + // CQ_HOST_DEVICE_MPI_TAG, + // cq_mpi_comm, &status); + return num_ops; + + } else { + const int host_rank = CQ_MPI_HOST_RANK; + pthread_mutex_lock(&dev_ctrl.device_lock); + int foo = 0; + while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { + printf("%s [wait_all_ops][loop]: got here %d\n", get_comm_source(), foo); + ++foo; + pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); + } + printf("%s [wait_all_ops]: got here\n", get_comm_source()); + // rather than sending num ops, device_control_thread sends updated params? + // but also maybe I don't want MPI inside device_control_thread + // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, + // CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); + + pthread_mutex_unlock(&dev_ctrl.device_lock); + printf("%s leaving wait_ops now\n", get_comm_source()); + return dev_ctrl.num_ops; + } +} + +void* mpi_device_control_thread(void*) { + enum ctrl_code current_op = CQ_CTRL_IDLE; + void* current_op_params = NULL; + + // run_device set to FALSE at cq_finalise + while (dev_ctrl.run_device) { + pthread_mutex_lock(&dev_ctrl.device_lock); + + while (dev_ctrl.num_ops <= 0) { + // wait for a new op to be posted + dev_ctrl.device_busy = false; + pthread_cond_signal(&dev_ctrl.cond_device_busy); + pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); + } + + dev_ctrl.device_busy = true; + + // take the next op and params out of the dev_ctrl buffer, and then tidy + // up the dev_ctrl buffer + current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; + + // TODO: Can solve NOTE 4 (see below) by doing deep copy here... + // and freeing op_params_buffer earlier + // current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; + + params_deep_copy(op_type_to_params_type(current_op), + dev_ctrl.op_params_buffer[dev_ctrl.next_op_out], + ¤t_op_params); + + if (current_op_params == NULL) { + printf("Params are null!\n"); + } + print_alloc_params(current_op_params); + dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; + // release resources allocated in specialisation of recv_ctrl_params + // Alternativally comment this out and handle freeing in those + // specialisations -- see commented out first few lines in recv_uint_params. + free(dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]); // allocated in + // recv_ctrl_params + dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; + + // NOTE:1 -- 01.05.2026: went with freeing in recievers solution + // NOTE:2 -- 01.05.2026: since we have ring buffer, resources will be freed + // on snd pass + // NOTE:3 -- 05.05.2026: actually reverted and went with freeing resources + // here as sometimes I want update params in the reciever (see end of this + // function) + + // const size_t last_op_idx = dev_ctrl.next_op_out; + + // decrease the number of queued operations and advance next_op_out + --dev_ctrl.num_ops; + ++dev_ctrl.next_op_out; + dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; + + // signal that the queue is no longer full and then relinquish mutex + pthread_cond_signal(&dev_ctrl.cond_queue_full); + pthread_mutex_unlock(&dev_ctrl.device_lock); + + control_registry[current_op](current_op_params); + + // TODO: send updated ctrl_params to host + // also consider if this is the right place for the send... + // NOTE:4 -- 05.05.2026: This is quite nasty because: + // a) I don't wont to block thread for control_registry duration + // b) but I need to free the resources after and lock the dev_ctrl + // c) but meanwhile, other threads can modify dev_ctrl... + if (op_type_to_params_type(current_op) != PARAMS_UINT_T) { + struct device_ctrl_params params_to_sync = { + .type = op_type_to_params_type(current_op), + .data = current_op_params}; + print_alloc_params(params_to_sync.data); + const int host_rank = CQ_MPI_HOST_RANK; + send_ctrl_params(¶ms_to_sync, host_rank); + } + free(current_op_params); // allocated in params_deep_copy(); + } + + pthread_mutex_unlock(&dev_ctrl.device_lock); + + return NULL; +} + +int mpi_finalise_device(const unsigned int VERBOSITY) { + // Politely wait for the device to finish its current business + // otherwise setting dev_ctrl.run_device = false might break + // some stuff, and this should only be called in cq_finalise() + printf("%s [finalise_device]: Entered\n", get_comm_source()); + mpi_host_wait_all_ops(); + printf("%s [finalise_device]: passed the host wait all ops\n", + get_comm_source()); + + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + if (VERBOSITY > 0) { + printf("Finalising device.\n"); + } + + pthread_mutex_lock(&dev_ctrl.device_lock); + dev_ctrl.run_device = false; + pthread_mutex_unlock(&dev_ctrl.device_lock); + } + + unsigned int verbosity = VERBOSITY; + struct device_ctrl_params params = {.type = PARAMS_UINT_T, + .data = &verbosity}; + mpi_host_comm_ctrl_op(CQ_CTRL_FINALISE, ¶ms); + + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + pthread_join(dev_ctrl.device_thread, NULL); + + dev_ctrl.device_busy = false; + + pthread_cond_destroy(&dev_ctrl.cond_device_busy); + pthread_cond_destroy(&dev_ctrl.cond_queue_empty); + pthread_cond_destroy(&dev_ctrl.cond_queue_full); + pthread_mutex_destroy(&dev_ctrl.device_lock); + } + + MPI_Finalize(); + return 0; +} +// struct DeviceLink dev_ctrl; +// +// int mpi_initialise_device(const unsigned int VERBOSITY) { +// if (VERBOSITY > 0) { +// printf("Initialising device.\n"); +// } +// +// int nprocs; +// int world_rank; +// MPI_Comm comm_split; +// +// MPI_Init(NULL, NULL); +// +// MPI_Comm_size(MPI_COMM_WORLD, &nprocs); +// MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); +// // int in_quest_comm = world_rank != 0; +// // MPI_Comm_split(MPI_COMM_WORLD, I_AM_QUANTUM, world_rank, &comm_split); +// +// dev_ctrl.run_device = true; +// dev_ctrl.device_busy = true; +// dev_ctrl.num_ops = 0; +// dev_ctrl.next_op_in = 0; +// dev_ctrl.next_op_out = 0; +// +// for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { +// dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; +// dev_ctrl.op_params_buffer[i] = NULL; +// } +// +// unsigned int verbosity = VERBOSITY; +// mpi_host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); +// mpi_host_wait_all_ops(); +// +// return 0; +// } +// +// size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, void* ctrl_params) { +// // 1. need to request from device queue size... +// // 2. send OP for device op_buffer +// int rank; +// MPI_Comm_rank(MPI_COMM_WORLD, &rank); +// if (rank == 0) { +// MPI_Status status; +// int device_rank = 1; +// +// while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { +// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD, &status); +// } +// MPI_Ssend(&OP, 1, MPI_INT, device_rank, 0, MPI_COMM_WORLD); +// // TODO: this needs solution +// // MPI_Ssend(ctrl_params, ctrl_params_extent, MPI_CHAR, device_rank, 0, +// // MPI_COMM_WORLD); +// +// ++dev_ctrl.num_ops; +// ++dev_ctrl.next_op_in; +// dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; +// +// // NOTE: matched by Recv on lines 120 and 122 +// MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD); +// MPI_Ssend(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD); +// +// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD, +// &status); +// MPI_Recv(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, device_rank, 0, +// MPI_COMM_WORLD, &status); +// +// } else { +// // NOTE: okay, I think it's simply wrong place for this code +// // actually we leave host rank as it is and this code goes to the device +// // code or something +// // int host_rank = 0; +// // MPI_Status status; +// // +// // // NOTE: if the device queue is full this will deadlock +// // // instead we have to call host_send_ctrl_op with a modification +// // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, +// // MPI_COMM_WORLD); +// // +// // MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_UINT64_T, +// // host_rank, 0, MPI_COMM_WORLD, &status); +// // +// // // TODO: this is tricky and needs solving +// // // MPI_Recv(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in], +// // // ctrl_params_extent, MPI_CHAR, +// // // host_rank, 0, MPI_COMM_WORLD, &status); +// // +// // ++dev_ctrl.num_ops; +// // ++dev_ctrl.next_op_in; +// // dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; +// // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, +// // MPI_COMM_WORLD); MPI_Ssend(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, +// // host_rank, 0, +// // MPI_COMM_WORLD); +// // } +// } +// return dev_ctrl.num_ops; +// } +// +// void* mpi_device_control_thread(void* par) { +// enum ctrl_code current_op = CQ_CTRL_IDLE; +// void* current_op_params = NULL; +// int host_rank = 0; +// MPI_Status status; +// +// // run_device set to FALSE at cq_finalise +// while (dev_ctrl.run_device) { +// // pthread_mutex_lock(&dev_ctrl.device_lock); +// +// while (dev_ctrl.num_ops <= 0) { +// // wait for a new op to be posted +// dev_ctrl.device_busy = false; +// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, +// MPI_COMM_WORLD, +// &status); +// MPI_Recv(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, host_rank, 0, +// MPI_COMM_WORLD, &status); +// +// pthread_cond_signal(&dev_ctrl.cond_device_busy); +// pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); +// } +// MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_UINT64_T, +// host_rank, 0, MPI_COMM_WORLD, &status); +// +// dev_ctrl.device_busy = true; +// +// // take the next op and params out of the dev_ctrl buffer, and then tidy +// // up the dev_ctrl buffer +// current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; +// current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; +// dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; +// dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; +// +// // decrease the number of queued operations and advance next_op_out +// --dev_ctrl.num_ops; +// ++dev_ctrl.next_op_out; +// dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; +// +// // signal that the queue is no longer full and then relinquish mutex +// pthread_cond_signal(&dev_ctrl.cond_queue_full); +// pthread_mutex_unlock(&dev_ctrl.device_lock); +// +// control_registry[current_op](current_op_params); +// } +// +// pthread_mutex_unlock(&dev_ctrl.device_lock); +// +// return NULL; +// } diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index e658e43..e69de29 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -1,104 +0,0 @@ -#ifndef HOST_DEVICE_MPI_COMMS_H -#define HOST_DEVICE_MPI_COMMS_H - -#include "datatypes.h" -#include "src/host/opcodes.h" - -#include - -#include -#include - -#define __CQ_DEVICE_QUEUE_SIZE__ 16 -#define __CQ_HOST_DEVICE_MPI_PROC__ 0 -#define CQ_MPI_HOST_RANK 0 -#define CQ_MPI_DEVICE_RANK 1 -#define CQ_HOST_DEVICE_MPI_TAG 0 - -enum ctrl_params_datatype { PARAMS_UINT_T, PARAMS_ALLOC_T, PARAMS_EXEC_T }; - -struct ctrl_params_header { - enum ctrl_params_datatype type; - int params_msg_size; -}; - -struct device_ctrl_params { - enum ctrl_params_datatype type; - void* data; -}; - -const char* get_comm_source(); -void print_params_header(struct ctrl_params_header header); -void print_alloc_params(void* params); -void print_op(const enum ctrl_code OP); - -typedef void (*param_packer_fn)(void* src, void* dest); - -enum ctrl_params_datatype op_type_to_params_type(enum ctrl_code op_type); - -void params_deep_copy(enum ctrl_params_datatype params_type, - void* src, - void** dest); - -// void pack_uint_params(void* src, void* dest); -// void pack_alloc_params(void* src, void* dest); -// void pack_exec_params(void* src, void* dest); -// or send_xxx_params and recv_xxx_params -// void send_uint_params(void* params); -// void send_alloc_params(void* params); -// void send_exec_params(void* params); - -// TODO: const what should be const - -// NOTE: currently sending 2 messages, header than the payload -// could do 1 message and first probe the size on the recv side -// but probe might be actually slower -- benchmark? -void send_ctrl_params(const struct device_ctrl_params* ctrl_params, int dest); - -void send_uint_params(struct device_ctrl_params ctrl_params, int dest); - -// TODO: Need to send the updated params back from device to host. -void send_alloc_params(struct device_ctrl_params ctrl_params, int dest); -void send_exec_params(struct device_ctrl_params ctrl_params, int dest); - -void recv_ctrl_params(void** params, int src); -void recv_uint_params(void** params, int params_size, int src); -void recv_alloc_params(void** params, int params_size, int src); -void recv_exec_params(void** params, int params_size, int src); - -// void comm_ctrl_params(struct ctrl_params_header msg_header, -// void* params, -// param_packer_fn packer); -// void send_ctrl_params(struct ctrl_params_header msg_header, -// void* params, -// param_packer_fn packer); - -int mpi_initialise_device(const unsigned int VERBOSITY); - -void mpi_host_comm_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params* ctrl_params); - -size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params* ctrl_params); - -// TODO: rename -- it is device comms thread that runs this -size_t mpi_host_recv_ctrl_op(void); - -size_t mpi_host_sync_exec(cq_exec* const ehp); - -size_t mpi_host_wait_exec(cq_exec* const ehp); - -void mpi_host_request_halt(cq_exec* const ehp); - -size_t mpi_host_wait_all_ops(void); - -size_t mpi_device_sync_exec(const cq_status STATUS, - const size_t SHOT, - cstate const* const RESULT, - cq_exec* ehp); - -void* mpi_device_control_thread(void*); - -int mpi_finalise_device(const unsigned int VERBOSITY); - -#endif diff --git a/src/host-device/mpi_comms.h.OLD b/src/host-device/mpi_comms.h.OLD new file mode 100644 index 0000000..306bb0d --- /dev/null +++ b/src/host-device/mpi_comms.h.OLD @@ -0,0 +1,112 @@ +#ifndef HOST_DEVICE_MPI_COMMS_H +#define HOST_DEVICE_MPI_COMMS_H + +#include "datatypes.h" +#include "src/host/opcodes.h" + +#include + +#include +#include + +#define __CQ_DEVICE_QUEUE_SIZE__ 16 +#define __CQ_HOST_DEVICE_MPI_PROC__ 0 +#define CQ_MPI_HOST_RANK 0 +#define CQ_MPI_DEVICE_RANK 1 +#define CQ_HOST_DEVICE_MPI_TAG 0 + +enum ctrl_params_datatype { PARAMS_UINT_T, PARAMS_ALLOC_T, PARAMS_EXEC_T }; + +struct ctrl_params_header { + enum ctrl_params_datatype type; + int params_msg_size; +}; + +struct device_ctrl_params { + enum ctrl_params_datatype type; + void* data; +}; + +const char* get_comm_source(void); +void print_params_header(struct ctrl_params_header header); +void print_alloc_params(void* params); +void print_op(const enum ctrl_code OP); + +typedef void (*param_packer_fn)(void* src, void* dest); + +enum ctrl_params_datatype op_type_to_params_type(enum ctrl_code op_type); + +void params_deep_copy(enum ctrl_params_datatype params_type, + void* src, + void** dest); + +#define HOST_ONLY \ + { \ + int rank; \ + MPI_Comm_rank(MPI_COMM_WORLD, &rank); \ + if (rank != CQ_MPI_HOST_RANK) \ + return CQ_SUCCESS; \ + } + +// void pack_uint_params(void* src, void* dest); +// void pack_alloc_params(void* src, void* dest); +// void pack_exec_params(void* src, void* dest); +// or send_xxx_params and recv_xxx_params +// void send_uint_params(void* params); +// void send_alloc_params(void* params); +// void send_exec_params(void* params); + +// TODO: const what should be const + +// NOTE: currently sending 2 messages, header than the payload +// could do 1 message and first probe the size on the recv side +// but probe might be actually slower -- benchmark? +void send_ctrl_params(const struct device_ctrl_params* ctrl_params, int dest); + +void send_uint_params(struct device_ctrl_params ctrl_params, int dest); + +// TODO: Need to send the updated params back from device to host. +void send_alloc_params(struct device_ctrl_params ctrl_params, int dest); +void send_exec_params(struct device_ctrl_params ctrl_params, int dest); + +void recv_ctrl_params(void** params, int src); +void recv_uint_params(void** params, int params_size, int src); +void recv_alloc_params(void** params, int params_size, int src); +void recv_exec_params(void** params, int params_size, int src); + +// void comm_ctrl_params(struct ctrl_params_header msg_header, +// void* params, +// param_packer_fn packer); +// void send_ctrl_params(struct ctrl_params_header msg_header, +// void* params, +// param_packer_fn packer); + +int mpi_initialise_device(const unsigned int VERBOSITY); + +void mpi_host_comm_ctrl_op(const enum ctrl_code OP, + struct device_ctrl_params* ctrl_params); + +size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, + struct device_ctrl_params* ctrl_params); + +// TODO: rename -- it is device comms thread that runs this +size_t mpi_host_recv_ctrl_op(void); + +size_t mpi_host_sync_exec(cq_exec* const ehp); + +size_t mpi_host_wait_exec(cq_exec* const ehp); + +void mpi_host_request_halt(cq_exec* const ehp); + +size_t mpi_host_wait_all_ops(void); + +size_t mpi_device_sync_exec(const cq_status STATUS, + const size_t SHOT, + cstate const* const RESULT, + cq_exec* ehp); + +void* mpi_device_control_thread(void*); + +int mpi_finalise_device(const unsigned int VERBOSITY); + +#endif From 26b594bda4149a2735be1e76d0c7fad91ca41fbb Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Thu, 7 May 2026 11:08:17 +0100 Subject: [PATCH 05/38] [WIP]: Implemented cq initialise and finalise under new MPI comms design. --- mpi-test.c | 29 ++-- src/host-device/mpi_comms.c | 260 ++++++++++++++++++++++++++++++++++++ src/host-device/mpi_comms.h | 109 +++++++++++++++ 3 files changed, 386 insertions(+), 12 deletions(-) diff --git a/mpi-test.c b/mpi-test.c index 35457c2..a3e711a 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -4,25 +4,30 @@ #include "cq.h" #include "datatypes.h" #include "host_ops.h" +#include "src/host-device/mpi_comms.h" + +#include int main() { - int rank; - // MPI_Init(NULL, NULL); - // MPI_Comm_rank(MPI_COMM_WORLD, &rank); - // printf("Hello from rank %d\n", rank); - // MPI_Finalize(); + // int rank; + // MPI_Init(NULL, NULL); + // MPI_Comm_rank(MPI_COMM_WORLD, &rank); + // printf("Hello from rank %d\n", rank); + // MPI_Finalize(); // volatile int foo = 1; // while (foo) // sleep(5); + // return 0; cq_init(1); - qubit* qr; - alloc_qureg(&qr, 5); - printf( - "[MAIN]: created qreg: with NQUBITS: %zu, qregistry_idx: %d, offset: " - "%d\n", - qr->N, qr->registry_index, qr->offset); - // printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); + // qubit* qr; + // alloc_qureg(&qr, 5); + // printf( + // "[MAIN]: created qreg: with NQUBITS: %zu, qregistry_idx: %d, offset: " + // "%d\n", + // qr->N, qr->registry_index, qr->offset); + // // printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); cq_finalise(1); + // finalise_host_device_mpi(1); return 0; } diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index e69de29..7a4d6e0 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -0,0 +1,260 @@ +#include "mpi_comms.h" + +#include "src/host-device/comms.h" +#include "src/host/opcodes.h" + +#include +#include + +#include +#include +#include +#include + +static MPI_Comm CQ_MPI_COMM = MPI_COMM_WORLD; + +struct cq_mpi_env { + int rank; +}; + +static struct cq_mpi_env mpi_env = {.rank = -1}; + +const char* get_comm_source() { + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + return "Host:\t\t"; + } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { + return "Device:\t\t"; + } else { + return ""; + } +} + +int get_rank(void) { + return mpi_env.rank; +} + +const char* op_to_str(const enum ctrl_code OP) { + switch (OP) { + case CQ_CTRL_IDLE: { + return "CQ_CTRL_IDLE"; + break; + } + case CQ_CTRL_ALLOC: { + return "CQ_CTRL_ALLOC"; + break; + } + case CQ_CTRL_DEALLOC: { + return "CQ_CTRL_DEALLOC"; + break; + } + case CQ_CTRL_INIT: { + return "CQ_CTRL_INIT"; + break; + } + case CQ_CTRL_FINALISE: { + return "CQ_CTRL_FINALISE"; + break; + } + case CQ_CTRL_WAIT: { + return "CQ_CTRL_WAIT"; + break; + } + case CQ_CTRL_RUN_QKERNEL: { + return "CQ_CTRL_RUN_QKERNEL"; + break; + } + case CQ_CTRL_RUN_PQKERNEL: { + return "CQ_CTRL_RUN_PQKERNEL"; + break; + } + case CQ_CTRL_TEST: { + return "CQ_CTRL_TEST"; + break; + } + case CQ_CTRL_ABORT: { + return "CQ_CTRL_ABORT"; + break; + } + default: { + break; + } + } + return ""; +} + +void init_host_device_mpi(const unsigned int VERBOSITY) { + if (VERBOSITY > 0) { + printf("Initialising MPI.\n"); + } + MPI_Init(NULL, NULL); + MPI_Comm_rank(CQ_MPI_COMM, &mpi_env.rank); + if (VERBOSITY > 0) { + printf("Initialised MPI.\n"); + } + + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + // send INIT OP + // const int device_rank = CQ_MPI_DEVICE_RANK; + // const enum ctrl_code OP = CQ_CTRL_INIT; + // printf("%s attempting to send OP\n", get_comm_source()); + // MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + // printf("%s sent OP\n", get_comm_source()); + } else { + // recv Init Op + // enum ctrl_code OP; + // const int host_rank = CQ_MPI_HOST_RANK; + // MPI_Status status; + // MPI_Recv(&OP, 1, MPI_INT, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, + // &status); + // setup device + // initialise_device(VERBOSITY); + // start listening -- alternatively we can start pthread here + dev_ctrl.run_device = true; + printf("%s started listening...\n", get_comm_source()); + device_listen(); + printf("%s closing connection.\n", get_comm_source()); + finalise_host_device_mpi(VERBOSITY); + // if OP == Finalise + // break the loop + // and return from here + // then pass through all host ops + // and in cq-finalise cleanup resources + } +} + +void finalise_host_device_mpi(const unsigned int VERBOSITY) { + printf("%s Finalising MPI.\n", get_comm_source()); + MPI_Finalize(); + printf("Finalised MPI.\n"); +} + +void mpi_host_send_ctrl_op(const enum ctrl_code OP, + struct ctrl_params* params) { + const int device_rank = CQ_MPI_DEVICE_RANK; + printf("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), + op_to_str(OP)); + MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + printf("%s [send_ctrl_op]: sent %s\n", get_comm_source(), op_to_str(OP)); +} + +void mpi_host_wait_all_ops(void) { + printf("%s [wait_all_ops]: waiting...\n", get_comm_source()); + mpi_host_send_ctrl_op(CQ_CTRL_WAIT, NULL); + size_t num_ops; + const int device_rank = CQ_MPI_DEVICE_RANK; + MPI_Status status; + MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_MPI_COMMS_TAG, + CQ_MPI_COMM, &status); + printf("%s [wait_all_ops]: all ops completed (num_ops: %zu)\n", + get_comm_source(), num_ops); +} + +// ---------------------------------------------------------------------------- +// Device Comm Ops +// ---------------------------------------------------------------------------- + +void device_listen(void) { + while (dev_ctrl.run_device) { + enum ctrl_code OP; + const int host_rank = CQ_MPI_HOST_RANK; + MPI_Status status; + printf("%s [device_listen]: recieving OP...\n", get_comm_source()); + MPI_Recv(&OP, 1, MPI_INT, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, + &status); + printf("%s [device_listen]: recieved %s\n", get_comm_source(), + op_to_str(OP)); + + // optionally get params and run op + // by running op I mean modyfing the internal dev_ctrl fields + // and then the worker thread handles the rest. + device_dispatch_ctrl_op(OP); + // if (OP == CQ_CTRL_FINALISE) { + // break; + // } + } +} + +// aka host_send_ctrl_op from comm.c +void insert_op(const enum ctrl_code OP) { + pthread_mutex_lock(&dev_ctrl.device_lock); + + while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { + // the control queue is full! + // we'll wait for it to not be full + pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); + } + + unsigned int* ctrl_params = (unsigned int*)malloc(sizeof(unsigned int)); + *ctrl_params = 1; + dev_ctrl.op_buffer[dev_ctrl.next_op_in] = OP; + dev_ctrl.op_params_buffer[dev_ctrl.next_op_in] = ctrl_params; + ++dev_ctrl.num_ops; + + // It's a ring buffer! + // advance next_op_in then mod out buffer size + ++dev_ctrl.next_op_in; + dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; + + pthread_cond_signal(&dev_ctrl.cond_queue_empty); + pthread_mutex_unlock(&dev_ctrl.device_lock); +} + +void device_dispatch_ctrl_op(const enum ctrl_code OP) { + switch (OP) { + case CQ_CTRL_INIT: { + initialise_device(1); + insert_op(OP); + break; + } + case CQ_CTRL_FINALISE: { + // insert_op(OP); + device_wait_all_ops(); + finalise_device(1); + break; + } + case CQ_CTRL_ALLOC: { + break; + } + case CQ_CTRL_DEALLOC: { + break; + } + case CQ_CTRL_RUN_QKERNEL: { + break; + } + case CQ_CTRL_RUN_PQKERNEL: { + break; + } + case CQ_CTRL_WAIT: { // NOTE: added in MPI extension + size_t num_ops = device_wait_all_ops(); + const int host_rank = CQ_MPI_HOST_RANK; + printf("%s [dispatch]: sending num ops: %zu...\n", get_comm_source(), + num_ops); + MPI_Ssend(&num_ops, 1, MPI_UINT64_T, host_rank, CQ_MPI_COMMS_TAG, + CQ_MPI_COMM); + printf("%s [dispatch]: sent num ops.\n", get_comm_source()); + break; + } + case CQ_CTRL_ABORT: { + break; + } + case CQ_CTRL_IDLE: { + break; + } + case CQ_CTRL_TEST: { + break; + } + default: { + break; + } + } +} + +size_t device_wait_all_ops(void) { + pthread_mutex_lock(&dev_ctrl.device_lock); + while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { + pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); + } + + pthread_mutex_unlock(&dev_ctrl.device_lock); + return dev_ctrl.num_ops; +} diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index e69de29..1ed89d2 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -0,0 +1,109 @@ +#ifndef CQ_HOST_DEVICE_MPI_COMMS_H +#define CQ_HOST_DEVICE_MPI_COMMS_H + +#include +#include "src/host/opcodes.h" + +// ---------------------------------------------------------------------------- +// Macros +// ---------------------------------------------------------------------------- +#define CQ_MPI_HOST_RANK 0 +#define CQ_MPI_DEVICE_RANK 1 +#define CQ_MPI_COMMS_TAG 0 + +#define RUN_HOST_ONLY() \ + { \ + int rank = get_rank(); \ + if (rank == -1) \ + return CQ_ERROR; \ + if (get_rank() != CQ_MPI_HOST_RANK) \ + return CQ_SUCCESS; \ + } + +// ---------------------------------------------------------------------------- +// Datatypes +// ---------------------------------------------------------------------------- + +enum params_type { + CQ_CTRL_PARAMS_UINT, // this will not be needed + CQ_CTRL_PARAMS_ALLOC, + CQ_CTRL_PARAMS_EXEC +}; + +struct ctrl_params { + enum params_type type; + void* data; +}; + +/// +/// initialises MPI environment both on host and on the device. +/// @param VERBOSITY unsigned integer controlling diagnostic output. +void init_host_device_mpi(const unsigned int VERBOSITY); + +/// +/// finalises MPI environment both on host and on the device. +/// @param VERBOSITY unsigned integer controlling diagnostic output. +void finalise_host_device_mpi(const unsigned int VERBOSITY); + +// ---------------------------------------------------------------------------- +// Host Comm Ops +// ---------------------------------------------------------------------------- + +/// +/// sends the control operation to the device. +/// @param OP an enum argument specifying CQ_CTRL_OP +/// @param params datatype argument storing arbitrary data and data type, useful +/// for a given control operation +// void mpi_host_send_ctrl_op(const enum ctrl_code OP, struct ctrl_params* +// params); +void mpi_host_send_ctrl_op(const enum ctrl_code OP, struct ctrl_params* params); + +/// +/// blocks and waits for the operations to complete on the device. +/// at the end it should recieve updated params if needed??? +void mpi_host_wait_all_ops(void); + +// ---------------------------------------------------------------------------- +// Device Comm Ops +// ---------------------------------------------------------------------------- + +/// +/// wait for message with control operation from the host. The intended use of +/// this function is to use it as a listener in the loop on the device. +void mpi_device_recv_ctrl_op(void); + +/// +/// starts listening for the incoming messages from the host. +void device_listen(void); + +/// +/// dispatches recieved control operation to the worker thread. +/// @param OP an enum argument specifying CQ_CTRL_OP +void device_dispatch_ctrl_op(const enum ctrl_code OP); + +/// +/// blocks device comms thread and awaits for the worker to complete. +size_t device_wait_all_ops(void); + +// ---------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------- + +/// +/// returns the name of either host or the device. Used for printing +/// diagnostics. +/// @return name of the device (or host). +const char* get_comm_source(void); + +/// +/// returns the current MPI rank. +/// @return MPI rank of a given process. +int get_rank(void); + +/// +/// converts CQ control code to string. +/// @param OP an enum argument specifying CQ_CTRL_OP +/// @return string represntation of OP. +const char* op_to_str(const enum ctrl_code OP); + +#endif From c2f68d068da41f0e65d53e1b7c94ad449dd35706 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Thu, 7 May 2026 12:23:58 +0100 Subject: [PATCH 06/38] Implemented allocation and deallocation operations. --- mpi-test.c | 17 +- src/host-device/mpi_comms.c | 305 ++++++++++++++++++++++++------------ src/host-device/mpi_comms.h | 39 ++++- 3 files changed, 252 insertions(+), 109 deletions(-) diff --git a/mpi-test.c b/mpi-test.c index a3e711a..426bc3f 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -20,14 +20,15 @@ int main() { // return 0; cq_init(1); - // qubit* qr; - // alloc_qureg(&qr, 5); - // printf( - // "[MAIN]: created qreg: with NQUBITS: %zu, qregistry_idx: %d, offset: " - // "%d\n", - // qr->N, qr->registry_index, qr->offset); - // // printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); + qubit* qr; + alloc_qureg(&qr, 5); + if (get_rank() == 0) { + printf( + "[MAIN]: created qreg: with NQUBITS: %zu, qregistry_idx: %zu, offset: " + "%zu\n", + qr->N, qr->registry_index, qr->offset); + printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); + } cq_finalise(1); - // finalise_host_device_mpi(1); return 0; } diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 7a4d6e0..eace02f 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -10,6 +10,7 @@ #include #include #include +#include static MPI_Comm CQ_MPI_COMM = MPI_COMM_WORLD; @@ -19,69 +20,6 @@ struct cq_mpi_env { static struct cq_mpi_env mpi_env = {.rank = -1}; -const char* get_comm_source() { - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - return "Host:\t\t"; - } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { - return "Device:\t\t"; - } else { - return ""; - } -} - -int get_rank(void) { - return mpi_env.rank; -} - -const char* op_to_str(const enum ctrl_code OP) { - switch (OP) { - case CQ_CTRL_IDLE: { - return "CQ_CTRL_IDLE"; - break; - } - case CQ_CTRL_ALLOC: { - return "CQ_CTRL_ALLOC"; - break; - } - case CQ_CTRL_DEALLOC: { - return "CQ_CTRL_DEALLOC"; - break; - } - case CQ_CTRL_INIT: { - return "CQ_CTRL_INIT"; - break; - } - case CQ_CTRL_FINALISE: { - return "CQ_CTRL_FINALISE"; - break; - } - case CQ_CTRL_WAIT: { - return "CQ_CTRL_WAIT"; - break; - } - case CQ_CTRL_RUN_QKERNEL: { - return "CQ_CTRL_RUN_QKERNEL"; - break; - } - case CQ_CTRL_RUN_PQKERNEL: { - return "CQ_CTRL_RUN_PQKERNEL"; - break; - } - case CQ_CTRL_TEST: { - return "CQ_CTRL_TEST"; - break; - } - case CQ_CTRL_ABORT: { - return "CQ_CTRL_ABORT"; - break; - } - default: { - break; - } - } - return ""; -} - void init_host_device_mpi(const unsigned int VERBOSITY) { if (VERBOSITY > 0) { printf("Initialising MPI.\n"); @@ -92,48 +30,34 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { printf("Initialised MPI.\n"); } - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - // send INIT OP - // const int device_rank = CQ_MPI_DEVICE_RANK; - // const enum ctrl_code OP = CQ_CTRL_INIT; - // printf("%s attempting to send OP\n", get_comm_source()); - // MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); - // printf("%s sent OP\n", get_comm_source()); - } else { - // recv Init Op - // enum ctrl_code OP; - // const int host_rank = CQ_MPI_HOST_RANK; - // MPI_Status status; - // MPI_Recv(&OP, 1, MPI_INT, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, - // &status); - // setup device - // initialise_device(VERBOSITY); - // start listening -- alternatively we can start pthread here + if (mpi_env.rank != CQ_MPI_HOST_RANK) { dev_ctrl.run_device = true; printf("%s started listening...\n", get_comm_source()); device_listen(); printf("%s closing connection.\n", get_comm_source()); finalise_host_device_mpi(VERBOSITY); - // if OP == Finalise - // break the loop - // and return from here - // then pass through all host ops - // and in cq-finalise cleanup resources } } void finalise_host_device_mpi(const unsigned int VERBOSITY) { - printf("%s Finalising MPI.\n", get_comm_source()); + if (VERBOSITY > 0) { + printf("%s Finalising MPI.\n", get_comm_source()); + } MPI_Finalize(); - printf("Finalised MPI.\n"); + if (VERBOSITY > 0) { + printf("Finalised MPI.\n"); + } } -void mpi_host_send_ctrl_op(const enum ctrl_code OP, - struct ctrl_params* params) { +// void mpi_host_send_ctrl_op(const enum ctrl_code OP, +// struct ctrl_params* params) { +void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params) { const int device_rank = CQ_MPI_DEVICE_RANK; printf("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), op_to_str(OP)); MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + // send params based on OP + host_comm_params(OP, params); printf("%s [send_ctrl_op]: sent %s\n", get_comm_source(), op_to_str(OP)); } @@ -154,6 +78,7 @@ void mpi_host_wait_all_ops(void) { // ---------------------------------------------------------------------------- void device_listen(void) { + // run_device set to FALSE when OP == CQ_CTRL_FINALISE while (dev_ctrl.run_device) { enum ctrl_code OP; const int host_rank = CQ_MPI_HOST_RANK; @@ -168,14 +93,14 @@ void device_listen(void) { // by running op I mean modyfing the internal dev_ctrl fields // and then the worker thread handles the rest. device_dispatch_ctrl_op(OP); - // if (OP == CQ_CTRL_FINALISE) { - // break; - // } } } -// aka host_send_ctrl_op from comm.c -void insert_op(const enum ctrl_code OP) { +// NOTE: aka host_send_ctrl_op from comm.c +// really what I want to use is host_send_ctrl_op implemenetation but it needs +// renaming and wrapping into function called host_send_ctrl_op and depending on +// either MPI or pthread implementation use this in different ctx. +void insert_op(const enum ctrl_code OP, void* ctrl_params) { pthread_mutex_lock(&dev_ctrl.device_lock); while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { @@ -184,8 +109,8 @@ void insert_op(const enum ctrl_code OP) { pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); } - unsigned int* ctrl_params = (unsigned int*)malloc(sizeof(unsigned int)); - *ctrl_params = 1; + // unsigned int* ctrl_params = (unsigned int*)malloc(sizeof(unsigned int)); + //*ctrl_params = 1; dev_ctrl.op_buffer[dev_ctrl.next_op_in] = OP; dev_ctrl.op_params_buffer[dev_ctrl.next_op_in] = ctrl_params; ++dev_ctrl.num_ops; @@ -200,31 +125,71 @@ void insert_op(const enum ctrl_code OP) { } void device_dispatch_ctrl_op(const enum ctrl_code OP) { + // TODO: needs to add extra CTRL ops for EXEC sync in opcodes.h + // then modyfing host_sync_exec and host_wait_exec switch (OP) { case CQ_CTRL_INIT: { - initialise_device(1); - insert_op(OP); + const unsigned int VERBOSITY = 1; + initialise_device(VERBOSITY); + insert_op(OP, &VERBOSITY); break; } case CQ_CTRL_FINALISE: { - // insert_op(OP); + // don't need to insert op into worker. + // we just wait until worker is done and cleanup. device_wait_all_ops(); finalise_device(1); break; } case CQ_CTRL_ALLOC: { + // I think this is the correct way to do things + // because alloc is blocking + // also because of it being blocking I don't need + // to worry about params lifetime (from the worker perspecitve) + // recv params + // device_alloc_params params; + // recv_alloc_params(device_alloc_params* params, int src); + // insert_op(OP, params); + // wait? + // device_wait_all_ops(); + // send updated params + // send_alloc_params(device_alloc_params* params, int dest); + const int host_rank = CQ_MPI_HOST_RANK; + device_alloc_params params = {0}; + recv_alloc_params(¶ms, host_rank); + insert_op(OP, ¶ms); + device_wait_all_ops(); + send_alloc_params(¶ms, host_rank); break; } case CQ_CTRL_DEALLOC: { + // I think this is the correct way to do things + // because dealloc is blocking + // recv params + // insert_op + // wait? + // send updated params + const int host_rank = CQ_MPI_HOST_RANK; + device_alloc_params params = {0}; + recv_alloc_params(¶ms, host_rank); + insert_op(OP, ¶ms); + device_wait_all_ops(); + send_alloc_params(¶ms, host_rank); break; } case CQ_CTRL_RUN_QKERNEL: { + // recv params + // insert_op break; } case CQ_CTRL_RUN_PQKERNEL: { + // recv params + // insert_op break; } - case CQ_CTRL_WAIT: { // NOTE: added in MPI extension + case CQ_CTRL_WAIT: { + // wait for worker and send info to the host + // (which is blocked and waiting). size_t num_ops = device_wait_all_ops(); const int host_rank = CQ_MPI_HOST_RANK; printf("%s [dispatch]: sending num ops: %zu...\n", get_comm_source(), @@ -249,6 +214,10 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { } } +// NOTE: aka host_wait_all_ops from comm.c +// really what I want to use is host_wait_all_ops implementation but it needs +// renaming and wrapping into function called host_send_ctrl_op and depending on +// either MPI or pthread implementation use this in different ctx. size_t device_wait_all_ops(void) { pthread_mutex_lock(&dev_ctrl.device_lock); while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { @@ -258,3 +227,139 @@ size_t device_wait_all_ops(void) { pthread_mutex_unlock(&dev_ctrl.device_lock); return dev_ctrl.num_ops; } + +// ---------------------------------------------------------------------------- +// Device Control Paramaters Comms +// ---------------------------------------------------------------------------- + +void host_comm_params(const enum ctrl_code OP, void* params) { + switch (OP) { + case CQ_CTRL_INIT: { + break; + } + case CQ_CTRL_FINALISE: { + break; + } + case CQ_CTRL_ALLOC: { + const int device_rank = CQ_MPI_DEVICE_RANK; + send_alloc_params(params, device_rank); + recv_alloc_params(params, device_rank); + break; + } + case CQ_CTRL_DEALLOC: { + const int device_rank = CQ_MPI_DEVICE_RANK; + send_alloc_params(params, device_rank); + recv_alloc_params(params, device_rank); + break; + } + default: { + break; + } + } +} + +void recv_alloc_params(device_alloc_params* params, int src) { + printf("%s [recv_alloc_params]: recieving...\n", get_comm_source()); + + const size_t params_size = sizeof(device_alloc_params); + // void* recv_buffer = malloc(params_size); + // if (recv_buffer == NULL) { + // exit(-1); + // } + + MPI_Status status; + + MPI_Recv((char*)params, params_size, MPI_BYTE, src, CQ_MPI_COMMS_TAG, + CQ_MPI_COMM, &status); + + // memcpy((char*)params, recv_buffer, params_size); + // free(recv_buffer); + + printf("%s [recv_alloc_params]: recieved.\n", get_comm_source()); + print_alloc_params(params); +} + +void send_alloc_params(const device_alloc_params* params, int dest) { + const size_t params_size = sizeof(device_alloc_params); + printf("%s [send_alloc_params]: sending...\n", get_comm_source()); + print_alloc_params(params); + MPI_Ssend((char*)params, params_size, MPI_BYTE, dest, CQ_MPI_COMMS_TAG, + CQ_MPI_COMM); + printf("%s [send_alloc_params]: sent.\n", get_comm_source()); +} + +void recv_exec_params(device_alloc_params* params, int src) {} +void send_exec_params(device_alloc_params* params, int dest) {} + +// ---------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------- + +const char* get_comm_source() { + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + return "Host:\t\t"; + } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { + return "Device:\t\t"; + } else { + return ""; + } +} + +int get_rank(void) { + return mpi_env.rank; +} + +const char* op_to_str(const enum ctrl_code OP) { + switch (OP) { + case CQ_CTRL_IDLE: { + return "CQ_CTRL_IDLE"; + break; + } + case CQ_CTRL_ALLOC: { + return "CQ_CTRL_ALLOC"; + break; + } + case CQ_CTRL_DEALLOC: { + return "CQ_CTRL_DEALLOC"; + break; + } + case CQ_CTRL_INIT: { + return "CQ_CTRL_INIT"; + break; + } + case CQ_CTRL_FINALISE: { + return "CQ_CTRL_FINALISE"; + break; + } + case CQ_CTRL_WAIT: { + return "CQ_CTRL_WAIT"; + break; + } + case CQ_CTRL_RUN_QKERNEL: { + return "CQ_CTRL_RUN_QKERNEL"; + break; + } + case CQ_CTRL_RUN_PQKERNEL: { + return "CQ_CTRL_RUN_PQKERNEL"; + break; + } + case CQ_CTRL_TEST: { + return "CQ_CTRL_TEST"; + break; + } + case CQ_CTRL_ABORT: { + return "CQ_CTRL_ABORT"; + break; + } + default: { + break; + } + } + return ""; +} + +void print_alloc_params(const device_alloc_params* params) { + printf("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", + get_comm_source(), params->NQUBITS, params->qregistry_idx, + params->status); +} diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index 1ed89d2..ed4c738 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -2,6 +2,7 @@ #define CQ_HOST_DEVICE_MPI_COMMS_H #include +#include "src/host-device/comms.h" #include "src/host/opcodes.h" // ---------------------------------------------------------------------------- @@ -56,7 +57,12 @@ void finalise_host_device_mpi(const unsigned int VERBOSITY); /// for a given control operation // void mpi_host_send_ctrl_op(const enum ctrl_code OP, struct ctrl_params* // params); -void mpi_host_send_ctrl_op(const enum ctrl_code OP, struct ctrl_params* params); + +/// +/// sends the control operation to the device. +/// @param OP an enum argument specifying CQ_CTRL_OP +/// @param[in,out] params void pointer to arbitrary control parameters +void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params); /// /// blocks and waits for the operations to complete on the device. @@ -85,6 +91,32 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP); /// blocks device comms thread and awaits for the worker to complete. size_t device_wait_all_ops(void); +// ---------------------------------------------------------------------------- +// Device Control Paramaters Comms +// ---------------------------------------------------------------------------- + +/// +/// communicates control parameters between host and device. +/// @param OP an enum argument specifying CQ_CTRL_OP +/// @param[in,out] params void pointer to arbitrary params +void host_comm_params(const enum ctrl_code OP, void* params); + +/// +/// recieves allocation parameters from the source. +/// @param[out] params reference to parameters to store the results of +/// communication +/// @param src source rank of incoming message +void recv_alloc_params(device_alloc_params* params, int src); + +/// +/// sends allocation parameters to the destination. +/// @param[in] params reference to parameters to communicate +/// @param dest destination rank of outgoing message +void send_alloc_params(const device_alloc_params* params, int dest); + +// void recv_exec_params(device_alloc_params* params, int src); +// void send_exec_params(device_alloc_params* params, int dest); + // ---------------------------------------------------------------------------- // Helpers // ---------------------------------------------------------------------------- @@ -106,4 +138,9 @@ int get_rank(void); /// @return string represntation of OP. const char* op_to_str(const enum ctrl_code OP); +/// +/// prints values of the alloc parameters. +/// @param[in] params reference to parameters +void print_alloc_params(const device_alloc_params* params); + #endif From 8174b87a87e6071f45d2252d05a278c8c5c93d54 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Thu, 7 May 2026 17:44:42 +0100 Subject: [PATCH 07/38] [WIP]: Implementing executors. --- mpi-test.c | 44 ++++- src/host-device/mpi_comms.c | 310 +++++++++++++++++++++++++++++++----- src/host-device/mpi_comms.h | 10 +- 3 files changed, 314 insertions(+), 50 deletions(-) diff --git a/mpi-test.c b/mpi-test.c index 426bc3f..d6b2e39 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -8,6 +8,20 @@ #include +cq_status test_qkern(const size_t NQUBITS, + qubit* qr, + const size_t NMEASURE, + cstate* cr, + qkern_map* reg) { + CQ_REGISTER_KERNEL(reg) + set_qureg(qr, 0, NQUBITS); + for (size_t i = 0; i < NQUBITS; ++i) { + hadamard(&qr[i]); + } + measure_qureg(qr, NQUBITS, cr); + return CQ_SUCCESS; +} + int main() { // int rank; // MPI_Init(NULL, NULL); @@ -18,17 +32,31 @@ int main() { // while (foo) // sleep(5); // return 0; + const size_t NQUBITS = 10; + const size_t NSHOTS = 1; + const size_t NMEASURE = NQUBITS; cq_init(1); qubit* qr; - alloc_qureg(&qr, 5); - if (get_rank() == 0) { - printf( - "[MAIN]: created qreg: with NQUBITS: %zu, qregistry_idx: %zu, offset: " - "%zu\n", - qr->N, qr->registry_index, qr->offset); - printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); - } + alloc_qureg(&qr, NQUBITS); + // if (get_rank() == 0) { + // printf( + // "[MAIN]: created qreg: with NQUBITS: %zu, qregistry_idx: %zu, + // offset: " + // "%zu\n", + // qr->N, qr->registry_index, qr->offset); + // printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); + // } + + cstate cr[NMEASURE * NSHOTS]; + init_creg(NMEASURE * NSHOTS, -1, cr); + register_qkern(test_qkern); + + cq_exec eh; + a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); + wait_qrun(&eh); + + free_qureg(&qr); cq_finalise(1); return 0; } diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index eace02f..4bcbc37 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -1,8 +1,10 @@ #include "mpi_comms.h" +#include "datatypes.h" #include "src/host-device/comms.h" #include "src/host/opcodes.h" +#include #include #include @@ -18,6 +20,8 @@ struct cq_mpi_env { int rank; }; +static cq_exec* executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; +static int executor_id = 0; static struct cq_mpi_env mpi_env = {.rank = -1}; void init_host_device_mpi(const unsigned int VERBOSITY) { @@ -83,15 +87,12 @@ void device_listen(void) { enum ctrl_code OP; const int host_rank = CQ_MPI_HOST_RANK; MPI_Status status; - printf("%s [device_listen]: recieving OP...\n", get_comm_source()); + printf("%s [device_listen]: receiving OP...\n", get_comm_source()); MPI_Recv(&OP, 1, MPI_INT, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, &status); - printf("%s [device_listen]: recieved %s\n", get_comm_source(), + printf("%s [device_listen]: received %s\n", get_comm_source(), op_to_str(OP)); - // optionally get params and run op - // by running op I mean modyfing the internal dev_ctrl fields - // and then the worker thread handles the rest. device_dispatch_ctrl_op(OP); } } @@ -109,8 +110,6 @@ void insert_op(const enum ctrl_code OP, void* ctrl_params) { pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); } - // unsigned int* ctrl_params = (unsigned int*)malloc(sizeof(unsigned int)); - //*ctrl_params = 1; dev_ctrl.op_buffer[dev_ctrl.next_op_in] = OP; dev_ctrl.op_params_buffer[dev_ctrl.next_op_in] = ctrl_params; ++dev_ctrl.num_ops; @@ -124,6 +123,9 @@ void insert_op(const enum ctrl_code OP, void* ctrl_params) { pthread_mutex_unlock(&dev_ctrl.device_lock); } +// optionally get params and run op +// by running op I mean modyfing the internal dev_ctrl fields +// and then the worker thread handles the rest. void device_dispatch_ctrl_op(const enum ctrl_code OP) { // TODO: needs to add extra CTRL ops for EXEC sync in opcodes.h // then modyfing host_sync_exec and host_wait_exec @@ -142,18 +144,10 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { break; } case CQ_CTRL_ALLOC: { - // I think this is the correct way to do things - // because alloc is blocking - // also because of it being blocking I don't need - // to worry about params lifetime (from the worker perspecitve) - // recv params - // device_alloc_params params; - // recv_alloc_params(device_alloc_params* params, int src); - // insert_op(OP, params); - // wait? - // device_wait_all_ops(); - // send updated params - // send_alloc_params(device_alloc_params* params, int dest); + // Alloc is blocking: we get params, run allocation and + // send back the updated params. + // Also, because it's blocking I don't need to worry about + // params lifetime (from the worker perspecitve) const int host_rank = CQ_MPI_HOST_RANK; device_alloc_params params = {0}; recv_alloc_params(¶ms, host_rank); @@ -163,12 +157,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { break; } case CQ_CTRL_DEALLOC: { - // I think this is the correct way to do things - // because dealloc is blocking - // recv params - // insert_op - // wait? - // send updated params + // Same as Alloc const int host_rank = CQ_MPI_HOST_RANK; device_alloc_params params = {0}; recv_alloc_params(¶ms, host_rank); @@ -178,8 +167,9 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { break; } case CQ_CTRL_RUN_QKERNEL: { - // recv params - // insert_op + const int host_rank = CQ_MPI_HOST_RANK; + recv_exec_params(executor_handles[executor_id], host_rank); + insert_op(OP, executor_handles[executor_id]); break; } case CQ_CTRL_RUN_PQKERNEL: { @@ -187,6 +177,14 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // insert_op break; } + case CQ_CTRL_WAIT_EXEC: { + device_wait_all_ops(); + const int host_rank = CQ_MPI_HOST_RANK; + send_exec_params(executor_handles[executor_id], host_rank); + free(executor_handles[executor_id]); + executor_handles[executor_id] = NULL; + break; + } case CQ_CTRL_WAIT: { // wait for worker and send info to the host // (which is blocked and waiting). @@ -252,6 +250,16 @@ void host_comm_params(const enum ctrl_code OP, void* params) { recv_alloc_params(params, device_rank); break; } + case CQ_CTRL_RUN_QKERNEL: { + const int device_rank = CQ_MPI_DEVICE_RANK; + send_exec_params(params, device_rank); + break; + } + case CQ_CTRL_WAIT_EXEC: { + const int device_rank = CQ_MPI_DEVICE_RANK; + recv_exec_params(params, device_rank); + break; + } default: { break; } @@ -259,37 +267,233 @@ void host_comm_params(const enum ctrl_code OP, void* params) { } void recv_alloc_params(device_alloc_params* params, int src) { - printf("%s [recv_alloc_params]: recieving...\n", get_comm_source()); + printf("%s [recv_alloc_params]: receiving...\n", get_comm_source()); const size_t params_size = sizeof(device_alloc_params); - // void* recv_buffer = malloc(params_size); - // if (recv_buffer == NULL) { - // exit(-1); - // } - MPI_Status status; MPI_Recv((char*)params, params_size, MPI_BYTE, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, &status); - // memcpy((char*)params, recv_buffer, params_size); - // free(recv_buffer); - - printf("%s [recv_alloc_params]: recieved.\n", get_comm_source()); + printf("%s [recv_alloc_params]: received.\n", get_comm_source()); print_alloc_params(params); } void send_alloc_params(const device_alloc_params* params, int dest) { - const size_t params_size = sizeof(device_alloc_params); printf("%s [send_alloc_params]: sending...\n", get_comm_source()); + const size_t params_size = sizeof(device_alloc_params); print_alloc_params(params); MPI_Ssend((char*)params, params_size, MPI_BYTE, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); printf("%s [send_alloc_params]: sent.\n", get_comm_source()); } -void recv_exec_params(device_alloc_params* params, int src) {} -void send_exec_params(device_alloc_params* params, int dest) {} +void recv_exec_params(cq_exec* ehp, int src) { + printf("%s [recv_exec_params]: receiving...\n", get_comm_source()); + int msg_size; + MPI_Status status; + MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, &status); + void* recv_buffer = malloc(msg_size); + + // reserve space for all the date + currently unused members + if (ehp == NULL) { + printf("%s [recv_exec_params]: ehp is NULL. Allocating on device.\n", + get_comm_source()); + + // ehp = (cq_exec*)malloc(msg_size + sizeof(pthread_mutex_t) + + // sizeof(pthread_cond_t) + sizeof(void*)); + ehp = (cq_exec*)malloc(sizeof(cq_exec)); + } else { + printf("%s [recv_exec_params]: ehp is already allocated. So I'm on host.\n", + get_comm_source()); + } + + if (recv_buffer == NULL || ehp == NULL) { + printf("%s [recv_exec_params]: malloc failed. Exiting\n", + get_comm_source()); + exit(-10); + } + + MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_COMMS_TAG, + CQ_MPI_COMM, &status); + + const size_t bool_size = sizeof(bool); + const size_t cq_status_size = sizeof(cq_status); + // unpack + int position = 0; + MPI_Unpack(recv_buffer, msg_size, &position, &ehp->exec_init, bool_size, + MPI_BYTE, CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &ehp->complete, bool_size, + MPI_BYTE, CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &ehp->halt, bool_size, MPI_BYTE, + CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &ehp->status, cq_status_size, + MPI_BYTE, CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &ehp->nqubits, 1, MPI_UINT64_T, + CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &ehp->completed_shots, 1, + MPI_UINT64_T, CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &ehp->expected_shots, 1, + MPI_UINT64_T, CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &ehp->nmeasure, 1, MPI_UINT64_T, + CQ_MPI_COMM); + + size_t fname_size = 0; + MPI_Unpack(recv_buffer, msg_size, &position, &fname_size, 1, MPI_UINT64_T, + CQ_MPI_COMM); + ehp->fname = (char*)malloc(fname_size); + if (ehp->fname == NULL) { + printf("%s [recv_exec_params]: malloc ehp->fname failed. Exiting\n", + get_comm_source()); + exit(-10); + } + MPI_Unpack(recv_buffer, msg_size, &position, ehp->fname, fname_size, MPI_CHAR, + CQ_MPI_COMM); + + const size_t qreg_size = sizeof(qubit) * ehp->nqubits; + // NOTE: creg_size * expected_shots?? + const size_t creg_size = sizeof(cstate) * ehp->nmeasure; + + ehp->qreg = (qubit*)malloc(qreg_size); + if (ehp->qreg == NULL) { + printf("%s [recv_exec_params]: malloc ehp->qreg failed. Exiting\n", + get_comm_source()); + exit(-10); + } + + ehp->creg = (cstate*)malloc(creg_size); + if (ehp->creg == NULL) { + printf("%s [recv_exec_params]: malloc ehp->creg failed. Exiting\n", + get_comm_source()); + exit(-10); + } + + MPI_Unpack(recv_buffer, msg_size, &position, (char*)ehp->qreg, qreg_size, + MPI_BYTE, CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, (char*)ehp->creg, creg_size, + MPI_BYTE, CQ_MPI_COMM); + + // NOTE: recv ehp->params left for another day... it's for pqkerns + + free(recv_buffer); + + printf("%s [recv_exec_params]: received.\n", get_comm_source()); + print_ehp(ehp); +} + +void send_exec_params(cq_exec* ehp, int dest) { + printf("%s [send_exec_params]: sending...\n", get_comm_source()); + print_ehp(ehp); + + const size_t bool_size = sizeof(bool); + const size_t cq_status_size = sizeof(cq_status); + int max_buffer_size = 0; + int member_size = 0; + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM, + &max_buffer_size); // exec_init + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM, + &member_size); // complete + max_buffer_size += member_size; + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM, + &member_size); // halt + max_buffer_size += member_size; + MPI_Pack_size(cq_status_size, MPI_BYTE, CQ_MPI_COMM, + &member_size); // status + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + &member_size); // nqubits + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + &member_size); // completed_shots + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + &member_size); // expected_shots + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + &member_size); // nmeasure + max_buffer_size += member_size; + + // ------------------------------------------------------------------------- + // TODO: sending pthred stuff doesn't sound like a good idea... + // sounds like UB + // const size_t pthread_mutex_size = sizeof(pthread_mutex_t); + // MPI_Pack_size(pthread_mutex_size, MPI_BYTE, CQ_MPI_COMM, + // &member_size); // lock + // max_buffer_size += member_size; + // + // const size_t pthread_cond_size = sizeof(pthread_cond_t); + // MPI_Pack_size(pthread_cond_size, MPI_BYTE, CQ_MPI_COMM, + // &member_size); // cond_exec_complete + // max_buffer_size += member_size; + // ------------------------------------------------------------------------- + + size_t fname_size = 0; + if (ehp->fname != NULL) { + fname_size = strlen(ehp->fname) + 1; + } + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + &member_size); // fname_size + max_buffer_size += member_size; + MPI_Pack_size(fname_size, MPI_CHAR, CQ_MPI_COMM, + &member_size); // fname + max_buffer_size += member_size; + const size_t qreg_size = sizeof(qubit) * ehp->nqubits; + MPI_Pack_size(qreg_size, MPI_BYTE, CQ_MPI_COMM, + &member_size); // qreg + max_buffer_size += member_size; + + // NOTE: creg_size * expected_shots?? + const size_t creg_size = sizeof(cstate) * ehp->nmeasure; + MPI_Pack_size(creg_size, MPI_BYTE, CQ_MPI_COMM, + &member_size); // creg + max_buffer_size += member_size; + + // NOTE: sending ehp->params left for another day... it's for pqkerns + + char* send_buffer = malloc(max_buffer_size); + if (send_buffer == NULL) { + printf("Failed to allocate buffer for sending executor handle.\n"); + exit(-1); + } + int position; + MPI_Pack(&ehp->exec_init, bool_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + MPI_Pack(&ehp->complete, bool_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + MPI_Pack(&ehp->halt, bool_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + MPI_Pack(&ehp->status, cq_status_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + + MPI_Pack(&ehp->nqubits, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + MPI_Pack(&ehp->completed_shots, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + MPI_Pack(&ehp->expected_shots, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + MPI_Pack(&ehp->nmeasure, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + + // NOTE: skip pthread stuff... + + MPI_Pack(&fname_size, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + MPI_Pack(&ehp->fname, fname_size, MPI_CHAR, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + + MPI_Pack(ehp->qreg, qreg_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + MPI_Pack(&ehp->creg, creg_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM); + + // NOTE: ehp->prams left for another day + + MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_COMMS_TAG, + CQ_MPI_COMM); + + printf("%s [send_exec_params]: sent.\n", get_comm_source()); +} // ---------------------------------------------------------------------------- // Helpers @@ -363,3 +567,29 @@ void print_alloc_params(const device_alloc_params* params) { get_comm_source(), params->NQUBITS, params->qregistry_idx, params->status); } + +void print_ehp(const cq_exec* ehp) { + if (ehp == NULL) { + printf("ehp is NULL\n"); + } + + printf( + "%s ehp details:\nexec_init: %d, complete: %d, halt: %d, STATUS: " + "%d\nNQUBITS: " + "%zu, completed_shots: %zu, expected_shots: %zu, NMEASURE: %zu\nfname: " + "%s\nqreg:\n", + get_comm_source(), ehp->exec_init, ehp->complete, ehp->halt, ehp->status, + ehp->nqubits, ehp->completed_shots, ehp->expected_shots, ehp->nmeasure, + ehp->fname); + + for (size_t i = 0; i < ehp->nqubits; ++i) { + printf("qubit[%zu]: reg_idx: %zu, offset: %zu, N: %zu\n", i, + ehp->qreg[i].registry_index, ehp->qreg[i].offset, ehp->qreg[i].N); + } + printf("\ncreg:\n"); + + for (size_t i = 0; i < ehp->nmeasure; ++i) { + printf("cstate[%zu]: %d\n", i, ehp->creg[i]); + } + printf("%s ehp details END\n\n", get_comm_source()); +} diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index ed4c738..827e712 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -2,6 +2,7 @@ #define CQ_HOST_DEVICE_MPI_COMMS_H #include +#include "datatypes.h" #include "src/host-device/comms.h" #include "src/host/opcodes.h" @@ -114,8 +115,9 @@ void recv_alloc_params(device_alloc_params* params, int src); /// @param dest destination rank of outgoing message void send_alloc_params(const device_alloc_params* params, int dest); -// void recv_exec_params(device_alloc_params* params, int src); -// void send_exec_params(device_alloc_params* params, int dest); +// maybe **ehp +void recv_exec_params(cq_exec* ehp, int src); +void send_exec_params(cq_exec* ehp, int dest); // ---------------------------------------------------------------------------- // Helpers @@ -143,4 +145,8 @@ const char* op_to_str(const enum ctrl_code OP); /// @param[in] params reference to parameters void print_alloc_params(const device_alloc_params* params); +/// +/// prints members of the executor. +/// @param[in] ehp reference to executor +void print_ehp(const cq_exec* ehp); #endif From 3cba275aeed9c713074e54301940efab625e0d74 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Thu, 7 May 2026 18:12:30 +0100 Subject: [PATCH 08/38] Executors and synchronisation work... Now need to implement kernel registration. --- src/host-device/mpi_comms.c | 85 ++++++++++++++++++++----------------- src/host-device/mpi_comms.h | 8 +++- 2 files changed, 52 insertions(+), 41 deletions(-) diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 4bcbc37..559f459 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -168,7 +168,9 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { } case CQ_CTRL_RUN_QKERNEL: { const int host_rank = CQ_MPI_HOST_RANK; - recv_exec_params(executor_handles[executor_id], host_rank); + recv_exec_params(&executor_handles[executor_id], host_rank); + printf("PRINTING!!!"); + print_ehp(executor_handles[executor_id]); insert_op(OP, executor_handles[executor_id]); break; } @@ -257,7 +259,7 @@ void host_comm_params(const enum ctrl_code OP, void* params) { } case CQ_CTRL_WAIT_EXEC: { const int device_rank = CQ_MPI_DEVICE_RANK; - recv_exec_params(params, device_rank); + recv_exec_params(¶ms, device_rank); break; } default: { @@ -288,7 +290,8 @@ void send_alloc_params(const device_alloc_params* params, int dest) { printf("%s [send_alloc_params]: sent.\n", get_comm_source()); } -void recv_exec_params(cq_exec* ehp, int src) { +void recv_exec_params(cq_exec** ehp, int src) { + // TODO: do validation printf("%s [recv_exec_params]: receiving...\n", get_comm_source()); int msg_size; MPI_Status status; @@ -296,19 +299,20 @@ void recv_exec_params(cq_exec* ehp, int src) { void* recv_buffer = malloc(msg_size); // reserve space for all the date + currently unused members - if (ehp == NULL) { - printf("%s [recv_exec_params]: ehp is NULL. Allocating on device.\n", + if (*ehp == NULL) { + printf("%s [recv_exec_params]: *ehp is NULL. Allocating on device.\n", get_comm_source()); // ehp = (cq_exec*)malloc(msg_size + sizeof(pthread_mutex_t) + // sizeof(pthread_cond_t) + sizeof(void*)); - ehp = (cq_exec*)malloc(sizeof(cq_exec)); + *ehp = (cq_exec*)malloc(sizeof(cq_exec)); } else { - printf("%s [recv_exec_params]: ehp is already allocated. So I'm on host.\n", - get_comm_source()); + printf( + "%s [recv_exec_params]: *ehp is already allocated. So I'm on host.\n", + get_comm_source()); } - if (recv_buffer == NULL || ehp == NULL) { + if (recv_buffer == NULL || *ehp == NULL) { printf("%s [recv_exec_params]: malloc failed. Exiting\n", get_comm_source()); exit(-10); @@ -321,56 +325,58 @@ void recv_exec_params(cq_exec* ehp, int src) { const size_t cq_status_size = sizeof(cq_status); // unpack int position = 0; - MPI_Unpack(recv_buffer, msg_size, &position, &ehp->exec_init, bool_size, + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->exec_init, bool_size, MPI_BYTE, CQ_MPI_COMM); - MPI_Unpack(recv_buffer, msg_size, &position, &ehp->complete, bool_size, + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->complete, bool_size, MPI_BYTE, CQ_MPI_COMM); - MPI_Unpack(recv_buffer, msg_size, &position, &ehp->halt, bool_size, MPI_BYTE, - CQ_MPI_COMM); - MPI_Unpack(recv_buffer, msg_size, &position, &ehp->status, cq_status_size, + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->halt, bool_size, MPI_BYTE, CQ_MPI_COMM); - MPI_Unpack(recv_buffer, msg_size, &position, &ehp->nqubits, 1, MPI_UINT64_T, - CQ_MPI_COMM); - MPI_Unpack(recv_buffer, msg_size, &position, &ehp->completed_shots, 1, + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->status, cq_status_size, + MPI_BYTE, CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->nqubits, 1, MPI_UINT64_T, CQ_MPI_COMM); - MPI_Unpack(recv_buffer, msg_size, &position, &ehp->expected_shots, 1, + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->completed_shots, 1, + MPI_UINT64_T, CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->expected_shots, 1, + MPI_UINT64_T, CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->nmeasure, 1, MPI_UINT64_T, CQ_MPI_COMM); - MPI_Unpack(recv_buffer, msg_size, &position, &ehp->nmeasure, 1, MPI_UINT64_T, - CQ_MPI_COMM); size_t fname_size = 0; MPI_Unpack(recv_buffer, msg_size, &position, &fname_size, 1, MPI_UINT64_T, CQ_MPI_COMM); - ehp->fname = (char*)malloc(fname_size); - if (ehp->fname == NULL) { - printf("%s [recv_exec_params]: malloc ehp->fname failed. Exiting\n", + fname_size *= sizeof(char); + + (*ehp)->fname = (char*)malloc(fname_size); + if ((*ehp)->fname == NULL) { + printf("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", get_comm_source()); exit(-10); } - MPI_Unpack(recv_buffer, msg_size, &position, ehp->fname, fname_size, MPI_CHAR, - CQ_MPI_COMM); + MPI_Unpack(recv_buffer, msg_size, &position, (*ehp)->fname, fname_size, + MPI_CHAR, CQ_MPI_COMM); - const size_t qreg_size = sizeof(qubit) * ehp->nqubits; + const size_t qreg_size = sizeof(qubit) * (*ehp)->nqubits; // NOTE: creg_size * expected_shots?? - const size_t creg_size = sizeof(cstate) * ehp->nmeasure; + const size_t creg_size = sizeof(cstate) * (*ehp)->nmeasure; - ehp->qreg = (qubit*)malloc(qreg_size); - if (ehp->qreg == NULL) { - printf("%s [recv_exec_params]: malloc ehp->qreg failed. Exiting\n", + (*ehp)->qreg = (qubit*)malloc(qreg_size); + if ((*ehp)->qreg == NULL) { + printf("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", get_comm_source()); exit(-10); } - ehp->creg = (cstate*)malloc(creg_size); - if (ehp->creg == NULL) { - printf("%s [recv_exec_params]: malloc ehp->creg failed. Exiting\n", + (*ehp)->creg = (cstate*)malloc(creg_size); + if ((*ehp)->creg == NULL) { + printf("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", get_comm_source()); exit(-10); } - MPI_Unpack(recv_buffer, msg_size, &position, (char*)ehp->qreg, qreg_size, + MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->qreg, qreg_size, MPI_BYTE, CQ_MPI_COMM); - MPI_Unpack(recv_buffer, msg_size, &position, (char*)ehp->creg, creg_size, + MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->creg, creg_size, MPI_BYTE, CQ_MPI_COMM); // NOTE: recv ehp->params left for another day... it's for pqkerns @@ -378,7 +384,7 @@ void recv_exec_params(cq_exec* ehp, int src) { free(recv_buffer); printf("%s [recv_exec_params]: received.\n", get_comm_source()); - print_ehp(ehp); + print_ehp(*ehp); } void send_exec_params(cq_exec* ehp, int dest) { @@ -437,6 +443,7 @@ void send_exec_params(cq_exec* ehp, int dest) { MPI_Pack_size(fname_size, MPI_CHAR, CQ_MPI_COMM, &member_size); // fname max_buffer_size += member_size; + const size_t qreg_size = sizeof(qubit) * ehp->nqubits; MPI_Pack_size(qreg_size, MPI_BYTE, CQ_MPI_COMM, &member_size); // qreg @@ -455,7 +462,7 @@ void send_exec_params(cq_exec* ehp, int dest) { printf("Failed to allocate buffer for sending executor handle.\n"); exit(-1); } - int position; + int position = 0; MPI_Pack(&ehp->exec_init, bool_size, MPI_BYTE, send_buffer, max_buffer_size, &position, CQ_MPI_COMM); MPI_Pack(&ehp->complete, bool_size, MPI_BYTE, send_buffer, max_buffer_size, @@ -478,12 +485,12 @@ void send_exec_params(cq_exec* ehp, int dest) { MPI_Pack(&fname_size, 1, MPI_UINT64_T, send_buffer, max_buffer_size, &position, CQ_MPI_COMM); - MPI_Pack(&ehp->fname, fname_size, MPI_CHAR, send_buffer, max_buffer_size, + MPI_Pack(ehp->fname, fname_size, MPI_CHAR, send_buffer, max_buffer_size, &position, CQ_MPI_COMM); MPI_Pack(ehp->qreg, qreg_size, MPI_BYTE, send_buffer, max_buffer_size, &position, CQ_MPI_COMM); - MPI_Pack(&ehp->creg, creg_size, MPI_BYTE, send_buffer, max_buffer_size, + MPI_Pack(ehp->creg, creg_size, MPI_BYTE, send_buffer, max_buffer_size, &position, CQ_MPI_COMM); // NOTE: ehp->prams left for another day diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index 827e712..0c57f79 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -115,10 +115,14 @@ void recv_alloc_params(device_alloc_params* params, int src); /// @param dest destination rank of outgoing message void send_alloc_params(const device_alloc_params* params, int dest); -// maybe **ehp -void recv_exec_params(cq_exec* ehp, int src); +void recv_exec_params(cq_exec** ehp, int src); void send_exec_params(cq_exec* ehp, int dest); +void recv_qkern_name(char fname[__CQ_MAX_QKERN_NAME_LENGTH__], + size_t* fname_size, + int src); +void send_qkern_name(const char* fname, size_t fname_size, int dest); + // ---------------------------------------------------------------------------- // Helpers // ---------------------------------------------------------------------------- From 002526e7cb000b95d5ea5d1cebd05fd8973ba648 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Fri, 8 May 2026 16:27:18 +0100 Subject: [PATCH 09/38] Implemented executors with MPI communication. --- src/host-device/mpi_comms.c | 57 +++++++++++++++++++++++++++++++++---- src/host-device/mpi_comms.h | 26 +++++++++++------ 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 559f459..9578724 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -24,6 +24,15 @@ static cq_exec* executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; static int executor_id = 0; static struct cq_mpi_env mpi_env = {.rank = -1}; +struct communicator { + bool comm_busy; + pthread_t device_comm_thread; + pthread_cond_t cond_comm_busy; + pthread_mutex_t comm_lock; +}; + +static struct communicator dev_comm = {0}; + void init_host_device_mpi(const unsigned int VERBOSITY) { if (VERBOSITY > 0) { printf("Initialising MPI.\n"); @@ -36,14 +45,24 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { if (mpi_env.rank != CQ_MPI_HOST_RANK) { dev_ctrl.run_device = true; - printf("%s started listening...\n", get_comm_source()); - device_listen(); - printf("%s closing connection.\n", get_comm_source()); - finalise_host_device_mpi(VERBOSITY); + // printf("%s started listening...\n", get_comm_source()); + // device_listen(); + // printf("%s closing connection.\n", get_comm_source()); + // finalise_host_device_mpi(VERBOSITY); + dev_comm.comm_busy = true; + pthread_mutex_init(&dev_comm.comm_lock, NULL); + pthread_cond_init(&dev_comm.cond_comm_busy, NULL); + pthread_create(&dev_comm.device_comm_thread, NULL, &device_listen, NULL); } } void finalise_host_device_mpi(const unsigned int VERBOSITY) { + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + pthread_join(dev_comm.device_comm_thread, NULL); + dev_comm.comm_busy = false; + pthread_cond_destroy(&dev_comm.cond_comm_busy); + pthread_mutex_destroy(&dev_comm.comm_lock); + } if (VERBOSITY > 0) { printf("%s Finalising MPI.\n", get_comm_source()); } @@ -81,9 +100,15 @@ void mpi_host_wait_all_ops(void) { // Device Comm Ops // ---------------------------------------------------------------------------- -void device_listen(void) { +void* device_listen(void*) { // run_device set to FALSE when OP == CQ_CTRL_FINALISE + printf("%s started listening...\n", get_comm_source()); while (dev_ctrl.run_device) { + pthread_mutex_lock(&dev_comm.comm_lock); + dev_comm.comm_busy = true; + pthread_cond_signal(&dev_comm.cond_comm_busy); + pthread_mutex_unlock(&dev_comm.comm_lock); + enum ctrl_code OP; const int host_rank = CQ_MPI_HOST_RANK; MPI_Status status; @@ -94,7 +119,15 @@ void device_listen(void) { op_to_str(OP)); device_dispatch_ctrl_op(OP); + + pthread_mutex_lock(&dev_comm.comm_lock); + dev_comm.comm_busy = false; + pthread_cond_signal(&dev_comm.cond_comm_busy); + pthread_mutex_unlock(&dev_comm.comm_lock); } + printf("%s closing connection.\n", get_comm_source()); + + finalise_host_device_mpi(1); } // NOTE: aka host_send_ctrl_op from comm.c @@ -228,6 +261,20 @@ size_t device_wait_all_ops(void) { return dev_ctrl.num_ops; } +void device_wait_comms(void) { + pthread_mutex_lock(&dev_comm.comm_lock); + while (dev_comm.comm_busy) { + pthread_cond_wait(&dev_comm.cond_comm_busy, &dev_comm.comm_lock); + } + pthread_mutex_unlock(&dev_comm.comm_lock); +} + +void host_device_final_sync(void) { + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + device_wait_comms(); + } +} + // ---------------------------------------------------------------------------- // Device Control Paramaters Comms // ---------------------------------------------------------------------------- diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index 0c57f79..35e8461 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -13,13 +13,14 @@ #define CQ_MPI_DEVICE_RANK 1 #define CQ_MPI_COMMS_TAG 0 -#define RUN_HOST_ONLY() \ - { \ - int rank = get_rank(); \ - if (rank == -1) \ - return CQ_ERROR; \ - if (get_rank() != CQ_MPI_HOST_RANK) \ - return CQ_SUCCESS; \ +#define RUN_HOST_ONLY() \ + { \ + int rank = get_rank(); \ + if (rank == -1) \ + return CQ_ERROR; \ + if (get_rank() != CQ_MPI_HOST_RANK) { \ + return CQ_SUCCESS; \ + } \ } // ---------------------------------------------------------------------------- @@ -81,7 +82,7 @@ void mpi_device_recv_ctrl_op(void); /// /// starts listening for the incoming messages from the host. -void device_listen(void); +void* device_listen(void*); /// /// dispatches recieved control operation to the worker thread. @@ -92,6 +93,14 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP); /// blocks device comms thread and awaits for the worker to complete. size_t device_wait_all_ops(void); +/// +/// blocks device master thread and awaits for the comms to complete +void device_wait_comms(void); + +/// +/// wrapper around device_wait_comms, intended to be called from the host. +/// The intended caller of this function is cq_finalise only! +void host_device_final_sync(void); // ---------------------------------------------------------------------------- // Device Control Paramaters Comms // ---------------------------------------------------------------------------- @@ -118,6 +127,7 @@ void send_alloc_params(const device_alloc_params* params, int dest); void recv_exec_params(cq_exec** ehp, int src); void send_exec_params(cq_exec* ehp, int dest); +// no! void recv_qkern_name(char fname[__CQ_MAX_QKERN_NAME_LENGTH__], size_t* fname_size, int src); From d359dcfff8738ab072d0be9b2037d0dd84ea6f0e Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 11 May 2026 15:18:41 +0100 Subject: [PATCH 10/38] Cleaned up and restructured code. --- mpi-test.c | 5 +- src/host-device/mpi_comms.c | 244 +++++++++++++++++++++++------------- src/host-device/mpi_comms.h | 73 ++++++----- 3 files changed, 201 insertions(+), 121 deletions(-) diff --git a/mpi-test.c b/mpi-test.c index d6b2e39..aa38367 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -53,8 +53,9 @@ int main() { register_qkern(test_qkern); cq_exec eh; - a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); - wait_qrun(&eh); + // a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); + // wait_qrun(&eh); + s_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE); free_qureg(&qr); cq_finalise(1); diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 9578724..1ceb4ee 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -8,13 +8,14 @@ #include #include +#include #include #include #include #include #include -static MPI_Comm CQ_MPI_COMM = MPI_COMM_WORLD; +static const MPI_Comm CQ_MPI_COMM = MPI_COMM_WORLD; struct cq_mpi_env { int rank; @@ -32,43 +33,42 @@ struct communicator { }; static struct communicator dev_comm = {0}; +static unsigned int cq_mpi_verbosity = 0; + +static void cq_log(const char* format, ...) { + if (CQ_MPI_IMPL_DEBUG) { + va_list(args); + va_start(args, format); + vprintf(format, args); + } +} void init_host_device_mpi(const unsigned int VERBOSITY) { + cq_mpi_verbosity = VERBOSITY; if (VERBOSITY > 0) { - printf("Initialising MPI.\n"); + cq_log("Initialising MPI.\n"); } MPI_Init(NULL, NULL); MPI_Comm_rank(CQ_MPI_COMM, &mpi_env.rank); if (VERBOSITY > 0) { - printf("Initialised MPI.\n"); + cq_log("Initialised MPI.\n"); } if (mpi_env.rank != CQ_MPI_HOST_RANK) { - dev_ctrl.run_device = true; - // printf("%s started listening...\n", get_comm_source()); - // device_listen(); - // printf("%s closing connection.\n", get_comm_source()); - // finalise_host_device_mpi(VERBOSITY); - dev_comm.comm_busy = true; - pthread_mutex_init(&dev_comm.comm_lock, NULL); - pthread_cond_init(&dev_comm.cond_comm_busy, NULL); - pthread_create(&dev_comm.device_comm_thread, NULL, &device_listen, NULL); + device_init_comms(VERBOSITY); } } void finalise_host_device_mpi(const unsigned int VERBOSITY) { if (mpi_env.rank != CQ_MPI_HOST_RANK) { - pthread_join(dev_comm.device_comm_thread, NULL); - dev_comm.comm_busy = false; - pthread_cond_destroy(&dev_comm.cond_comm_busy); - pthread_mutex_destroy(&dev_comm.comm_lock); + device_finalise_comms(VERBOSITY); } if (VERBOSITY > 0) { - printf("%s Finalising MPI.\n", get_comm_source()); + cq_log("%s Finalising MPI.\n", get_comm_source()); } MPI_Finalize(); if (VERBOSITY > 0) { - printf("Finalised MPI.\n"); + cq_log("Finalised MPI.\n"); } } @@ -76,60 +76,36 @@ void finalise_host_device_mpi(const unsigned int VERBOSITY) { // struct ctrl_params* params) { void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params) { const int device_rank = CQ_MPI_DEVICE_RANK; - printf("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), + cq_log("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), op_to_str(OP)); MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); // send params based on OP host_comm_params(OP, params); - printf("%s [send_ctrl_op]: sent %s\n", get_comm_source(), op_to_str(OP)); + cq_log("%s [send_ctrl_op]: sent %s\n", get_comm_source(), op_to_str(OP)); } void mpi_host_wait_all_ops(void) { - printf("%s [wait_all_ops]: waiting...\n", get_comm_source()); + cq_log("%s [wait_all_ops]: waiting...\n", get_comm_source()); mpi_host_send_ctrl_op(CQ_CTRL_WAIT, NULL); size_t num_ops; const int device_rank = CQ_MPI_DEVICE_RANK; MPI_Status status; MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, &status); - printf("%s [wait_all_ops]: all ops completed (num_ops: %zu)\n", + cq_log("%s [wait_all_ops]: all ops completed (num_ops: %zu)\n", get_comm_source(), num_ops); } +void host_device_final_sync(void) { + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + device_wait_comms(); + } +} + // ---------------------------------------------------------------------------- // Device Comm Ops // ---------------------------------------------------------------------------- -void* device_listen(void*) { - // run_device set to FALSE when OP == CQ_CTRL_FINALISE - printf("%s started listening...\n", get_comm_source()); - while (dev_ctrl.run_device) { - pthread_mutex_lock(&dev_comm.comm_lock); - dev_comm.comm_busy = true; - pthread_cond_signal(&dev_comm.cond_comm_busy); - pthread_mutex_unlock(&dev_comm.comm_lock); - - enum ctrl_code OP; - const int host_rank = CQ_MPI_HOST_RANK; - MPI_Status status; - printf("%s [device_listen]: receiving OP...\n", get_comm_source()); - MPI_Recv(&OP, 1, MPI_INT, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, - &status); - printf("%s [device_listen]: received %s\n", get_comm_source(), - op_to_str(OP)); - - device_dispatch_ctrl_op(OP); - - pthread_mutex_lock(&dev_comm.comm_lock); - dev_comm.comm_busy = false; - pthread_cond_signal(&dev_comm.cond_comm_busy); - pthread_mutex_unlock(&dev_comm.comm_lock); - } - printf("%s closing connection.\n", get_comm_source()); - - finalise_host_device_mpi(1); -} - // NOTE: aka host_send_ctrl_op from comm.c // really what I want to use is host_send_ctrl_op implemenetation but it needs // renaming and wrapping into function called host_send_ctrl_op and depending on @@ -156,6 +132,77 @@ void insert_op(const enum ctrl_code OP, void* ctrl_params) { pthread_mutex_unlock(&dev_ctrl.device_lock); } +void device_init_comms(const unsigned int VERBOSITY) { + if (VERBOSITY > 0) { + cq_log( + "%s [device_init_comms]: setting up on-device communication thread.\n", + get_comm_source()); + } + dev_ctrl.run_device = true; + // cq_log("%s started listening...\n", get_comm_source()); + // device_listen(); + // cq_log("%s closing connection.\n", get_comm_source()); + // finalise_host_device_mpi(VERBOSITY); + dev_comm.comm_busy = true; + pthread_mutex_init(&dev_comm.comm_lock, NULL); + pthread_cond_init(&dev_comm.cond_comm_busy, NULL); + pthread_create(&dev_comm.device_comm_thread, NULL, &device_listen, NULL); + if (VERBOSITY > 0) { + cq_log("%s [device_init_comms]: on-device communication thread set up.\n", + get_comm_source()); + } +} + +void device_finalise_comms(const unsigned int VERBOSITY) { + if (VERBOSITY > 0) { + cq_log( + "%s [device_finalise_comms]: finalising on-device communication " + "thread.\n", + get_comm_source()); + } + + pthread_join(dev_comm.device_comm_thread, NULL); + dev_comm.comm_busy = false; + pthread_cond_destroy(&dev_comm.cond_comm_busy); + pthread_mutex_destroy(&dev_comm.comm_lock); + if (VERBOSITY > 0) { + cq_log( + "%s [device_finalise_comms]: on-device communication thread closed.\n", + get_comm_source()); + } +} + +void* device_listen(void* args) { + // run_device set to FALSE when OP == CQ_CTRL_FINALISE + cq_log("%s started listening...\n", get_comm_source()); + while (dev_ctrl.run_device) { + pthread_mutex_lock(&dev_comm.comm_lock); + dev_comm.comm_busy = true; + pthread_cond_signal(&dev_comm.cond_comm_busy); + pthread_mutex_unlock(&dev_comm.comm_lock); + + enum ctrl_code OP; + const int host_rank = CQ_MPI_HOST_RANK; + MPI_Status status; + cq_log("%s [device_listen]: receiving OP...\n", get_comm_source()); + MPI_Recv(&OP, 1, MPI_INT, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, + &status); + cq_log("%s [device_listen]: received %s\n", get_comm_source(), + op_to_str(OP)); + + device_dispatch_ctrl_op(OP); + + pthread_mutex_lock(&dev_comm.comm_lock); + dev_comm.comm_busy = false; + pthread_cond_signal(&dev_comm.cond_comm_busy); + pthread_mutex_unlock(&dev_comm.comm_lock); + } + cq_log("%s closing connection.\n", get_comm_source()); + + finalise_host_device_mpi(cq_mpi_verbosity); + return NULL; +} + // optionally get params and run op // by running op I mean modyfing the internal dev_ctrl fields // and then the worker thread handles the rest. @@ -164,16 +211,15 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // then modyfing host_sync_exec and host_wait_exec switch (OP) { case CQ_CTRL_INIT: { - const unsigned int VERBOSITY = 1; - initialise_device(VERBOSITY); - insert_op(OP, &VERBOSITY); + initialise_device(cq_mpi_verbosity); + insert_op(OP, &cq_mpi_verbosity); break; } case CQ_CTRL_FINALISE: { // don't need to insert op into worker. // we just wait until worker is done and cleanup. device_wait_all_ops(); - finalise_device(1); + finalise_device(cq_mpi_verbosity); break; } case CQ_CTRL_ALLOC: { @@ -202,8 +248,6 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { case CQ_CTRL_RUN_QKERNEL: { const int host_rank = CQ_MPI_HOST_RANK; recv_exec_params(&executor_handles[executor_id], host_rank); - printf("PRINTING!!!"); - print_ehp(executor_handles[executor_id]); insert_op(OP, executor_handles[executor_id]); break; } @@ -216,8 +260,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { device_wait_all_ops(); const int host_rank = CQ_MPI_HOST_RANK; send_exec_params(executor_handles[executor_id], host_rank); - free(executor_handles[executor_id]); - executor_handles[executor_id] = NULL; + device_free_exec(&executor_handles[executor_id]); break; } case CQ_CTRL_WAIT: { @@ -225,11 +268,11 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // (which is blocked and waiting). size_t num_ops = device_wait_all_ops(); const int host_rank = CQ_MPI_HOST_RANK; - printf("%s [dispatch]: sending num ops: %zu...\n", get_comm_source(), + cq_log("%s [dispatch]: sending num ops: %zu...\n", get_comm_source(), num_ops); MPI_Ssend(&num_ops, 1, MPI_UINT64_T, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); - printf("%s [dispatch]: sent num ops.\n", get_comm_source()); + cq_log("%s [dispatch]: sent num ops.\n", get_comm_source()); break; } case CQ_CTRL_ABORT: { @@ -269,12 +312,6 @@ void device_wait_comms(void) { pthread_mutex_unlock(&dev_comm.comm_lock); } -void host_device_final_sync(void) { - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - device_wait_comms(); - } -} - // ---------------------------------------------------------------------------- // Device Control Paramaters Comms // ---------------------------------------------------------------------------- @@ -316,7 +353,7 @@ void host_comm_params(const enum ctrl_code OP, void* params) { } void recv_alloc_params(device_alloc_params* params, int src) { - printf("%s [recv_alloc_params]: receiving...\n", get_comm_source()); + cq_log("%s [recv_alloc_params]: receiving...\n", get_comm_source()); const size_t params_size = sizeof(device_alloc_params); MPI_Status status; @@ -324,22 +361,22 @@ void recv_alloc_params(device_alloc_params* params, int src) { MPI_Recv((char*)params, params_size, MPI_BYTE, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, &status); - printf("%s [recv_alloc_params]: received.\n", get_comm_source()); + cq_log("%s [recv_alloc_params]: received.\n", get_comm_source()); print_alloc_params(params); } void send_alloc_params(const device_alloc_params* params, int dest) { - printf("%s [send_alloc_params]: sending...\n", get_comm_source()); + cq_log("%s [send_alloc_params]: sending...\n", get_comm_source()); const size_t params_size = sizeof(device_alloc_params); print_alloc_params(params); MPI_Ssend((char*)params, params_size, MPI_BYTE, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); - printf("%s [send_alloc_params]: sent.\n", get_comm_source()); + cq_log("%s [send_alloc_params]: sent.\n", get_comm_source()); } void recv_exec_params(cq_exec** ehp, int src) { // TODO: do validation - printf("%s [recv_exec_params]: receiving...\n", get_comm_source()); + cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); int msg_size; MPI_Status status; MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, &status); @@ -347,20 +384,20 @@ void recv_exec_params(cq_exec** ehp, int src) { // reserve space for all the date + currently unused members if (*ehp == NULL) { - printf("%s [recv_exec_params]: *ehp is NULL. Allocating on device.\n", + cq_log("%s [recv_exec_params]: *ehp is NULL. Allocating on device.\n", get_comm_source()); // ehp = (cq_exec*)malloc(msg_size + sizeof(pthread_mutex_t) + // sizeof(pthread_cond_t) + sizeof(void*)); *ehp = (cq_exec*)malloc(sizeof(cq_exec)); } else { - printf( + cq_log( "%s [recv_exec_params]: *ehp is already allocated. So I'm on host.\n", get_comm_source()); } if (recv_buffer == NULL || *ehp == NULL) { - printf("%s [recv_exec_params]: malloc failed. Exiting\n", + cq_log("%s [recv_exec_params]: malloc failed. Exiting\n", get_comm_source()); exit(-10); } @@ -396,7 +433,7 @@ void recv_exec_params(cq_exec** ehp, int src) { (*ehp)->fname = (char*)malloc(fname_size); if ((*ehp)->fname == NULL) { - printf("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", + cq_log("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", get_comm_source()); exit(-10); } @@ -409,14 +446,14 @@ void recv_exec_params(cq_exec** ehp, int src) { (*ehp)->qreg = (qubit*)malloc(qreg_size); if ((*ehp)->qreg == NULL) { - printf("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", + cq_log("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", get_comm_source()); exit(-10); } (*ehp)->creg = (cstate*)malloc(creg_size); if ((*ehp)->creg == NULL) { - printf("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", + cq_log("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", get_comm_source()); exit(-10); } @@ -430,12 +467,12 @@ void recv_exec_params(cq_exec** ehp, int src) { free(recv_buffer); - printf("%s [recv_exec_params]: received.\n", get_comm_source()); + cq_log("%s [recv_exec_params]: received.\n", get_comm_source()); print_ehp(*ehp); } void send_exec_params(cq_exec* ehp, int dest) { - printf("%s [send_exec_params]: sending...\n", get_comm_source()); + cq_log("%s [send_exec_params]: sending...\n", get_comm_source()); print_ehp(ehp); const size_t bool_size = sizeof(bool); @@ -506,7 +543,7 @@ void send_exec_params(cq_exec* ehp, int dest) { char* send_buffer = malloc(max_buffer_size); if (send_buffer == NULL) { - printf("Failed to allocate buffer for sending executor handle.\n"); + cq_log("Failed to allocate buffer for sending executor handle.\n"); exit(-1); } int position = 0; @@ -546,7 +583,34 @@ void send_exec_params(cq_exec* ehp, int dest) { MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); - printf("%s [send_exec_params]: sent.\n", get_comm_source()); + cq_log("%s [send_exec_params]: sent.\n", get_comm_source()); +} + +void device_free_exec(cq_exec** ehp) { + if ((*ehp)->fname != NULL) { + free((*ehp)->fname); + (*ehp)->fname = NULL; + } + + if ((*ehp)->qreg != NULL) { + free((*ehp)->qreg); + (*ehp)->qreg = NULL; + } + + if ((*ehp)->creg != NULL) { + free((*ehp)->creg); + (*ehp)->creg = NULL; + } + + if ((*ehp)->params != NULL) { + free((*ehp)->params); + (*ehp)->params = NULL; + } + + if ((*ehp) != NULL) { + free((*ehp)); + (*ehp) = NULL; + } } // ---------------------------------------------------------------------------- @@ -617,17 +681,17 @@ const char* op_to_str(const enum ctrl_code OP) { } void print_alloc_params(const device_alloc_params* params) { - printf("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", + cq_log("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", get_comm_source(), params->NQUBITS, params->qregistry_idx, params->status); } void print_ehp(const cq_exec* ehp) { if (ehp == NULL) { - printf("ehp is NULL\n"); + cq_log("ehp is NULL\n"); } - printf( + cq_log( "%s ehp details:\nexec_init: %d, complete: %d, halt: %d, STATUS: " "%d\nNQUBITS: " "%zu, completed_shots: %zu, expected_shots: %zu, NMEASURE: %zu\nfname: " @@ -637,13 +701,13 @@ void print_ehp(const cq_exec* ehp) { ehp->fname); for (size_t i = 0; i < ehp->nqubits; ++i) { - printf("qubit[%zu]: reg_idx: %zu, offset: %zu, N: %zu\n", i, + cq_log("qubit[%zu]: reg_idx: %zu, offset: %zu, N: %zu\n", i, ehp->qreg[i].registry_index, ehp->qreg[i].offset, ehp->qreg[i].N); } - printf("\ncreg:\n"); + cq_log("\ncreg:\n"); for (size_t i = 0; i < ehp->nmeasure; ++i) { - printf("cstate[%zu]: %d\n", i, ehp->creg[i]); + cq_log("cstate[%zu]: %d\n", i, ehp->creg[i]); } - printf("%s ehp details END\n\n", get_comm_source()); + cq_log("%s ehp details END\n\n", get_comm_source()); } diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index 35e8461..4c291e5 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -9,6 +9,8 @@ // ---------------------------------------------------------------------------- // Macros // ---------------------------------------------------------------------------- +#define CQ_MPI_IMPL_DEBUG 1 + #define CQ_MPI_HOST_RANK 0 #define CQ_MPI_DEVICE_RANK 1 #define CQ_MPI_COMMS_TAG 0 @@ -23,21 +25,6 @@ } \ } -// ---------------------------------------------------------------------------- -// Datatypes -// ---------------------------------------------------------------------------- - -enum params_type { - CQ_CTRL_PARAMS_UINT, // this will not be needed - CQ_CTRL_PARAMS_ALLOC, - CQ_CTRL_PARAMS_EXEC -}; - -struct ctrl_params { - enum params_type type; - void* data; -}; - /// /// initialises MPI environment both on host and on the device. /// @param VERBOSITY unsigned integer controlling diagnostic output. @@ -71,6 +58,11 @@ void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params); /// at the end it should recieve updated params if needed??? void mpi_host_wait_all_ops(void); +/// +/// wrapper around device_wait_comms, intended to be called from the host. +/// The intended caller of this function is cq_finalise only! +void host_device_final_sync(void); + // ---------------------------------------------------------------------------- // Device Comm Ops // ---------------------------------------------------------------------------- @@ -78,11 +70,27 @@ void mpi_host_wait_all_ops(void); /// /// wait for message with control operation from the host. The intended use of /// this function is to use it as a listener in the loop on the device. -void mpi_device_recv_ctrl_op(void); +// void mpi_device_recv_ctrl_op(void); + +/// +/// initialises on-device communication thread responsible for MPI messaging +/// with host. +/// @param VERBOSITY unsigned integer controlling diagnostic output. +void device_init_comms(const unsigned int VERBOSITY); + +/// +/// finalises on-device communication thread responsible for MPI messaging +/// with host. +/// @param VERBOSITY unsigned integer controlling diagnostic output. +void device_finalise_comms(const unsigned int VERBOSITY); /// /// starts listening for the incoming messages from the host. -void* device_listen(void*); +/// @param args arbitrary arguments to function to satisfy pthread function +/// signature +/// @return arbitrary return data to satisfy pthread function signature. +/// Currently always returns NULL. +void* device_listen(void* args); /// /// dispatches recieved control operation to the worker thread. @@ -90,17 +98,15 @@ void* device_listen(void*); void device_dispatch_ctrl_op(const enum ctrl_code OP); /// -/// blocks device comms thread and awaits for the worker to complete. +/// blocks device main thread and awaits for the worker to complete. size_t device_wait_all_ops(void); /// -/// blocks device master thread and awaits for the comms to complete +/// blocks device master thread and awaits for the comms to complete. +/// it needs to be called only once at the end to ensure that the main thread +/// does not clean-up and close MPI before work is done on the worker. void device_wait_comms(void); -/// -/// wrapper around device_wait_comms, intended to be called from the host. -/// The intended caller of this function is cq_finalise only! -void host_device_final_sync(void); // ---------------------------------------------------------------------------- // Device Control Paramaters Comms // ---------------------------------------------------------------------------- @@ -112,7 +118,7 @@ void host_device_final_sync(void); void host_comm_params(const enum ctrl_code OP, void* params); /// -/// recieves allocation parameters from the source. +/// receives allocation parameters from the source. /// @param[out] params reference to parameters to store the results of /// communication /// @param src source rank of incoming message @@ -124,14 +130,23 @@ void recv_alloc_params(device_alloc_params* params, int src); /// @param dest destination rank of outgoing message void send_alloc_params(const device_alloc_params* params, int dest); +/// +/// receives executor handle from the source. Also, if called on device, +/// allocates executor in on-device memory, which then needs to be freed using +/// device_free_exec. +/// @param[out] ehp executor handle used for host-device offloading +/// @param src source rank of incoming message void recv_exec_params(cq_exec** ehp, int src); + +/// sends updated executor handle to the destination. +/// @param[in] ehp executor handle used for host-device offloading +/// @param dest destination rank of outgoing message void send_exec_params(cq_exec* ehp, int dest); -// no! -void recv_qkern_name(char fname[__CQ_MAX_QKERN_NAME_LENGTH__], - size_t* fname_size, - int src); -void send_qkern_name(const char* fname, size_t fname_size, int dest); +/// +/// frees on-device memory pointed to by executor handle. +/// @param[in,out] ehp executor handle used for host-device offloading +void device_free_exec(cq_exec** ehp); // ---------------------------------------------------------------------------- // Helpers From fea761e3e1c26ee331ff5de71c4be98f549341b0 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 11 May 2026 17:06:16 +0100 Subject: [PATCH 11/38] Added executor halting and sync. Resolved memory leak when handling executor on device. Implemented multishot executors. --- mpi-test.c | 7 +- src/host-device/mpi_comms.c | 147 +++++++++++++++++++++++++++++------- src/host-device/mpi_comms.h | 9 +++ 3 files changed, 132 insertions(+), 31 deletions(-) diff --git a/mpi-test.c b/mpi-test.c index aa38367..1658e8a 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -33,7 +33,7 @@ int main() { // sleep(5); // return 0; const size_t NQUBITS = 10; - const size_t NSHOTS = 1; + const size_t NSHOTS = 10; const size_t NMEASURE = NQUBITS; cq_init(1); @@ -55,7 +55,10 @@ int main() { cq_exec eh; // a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); // wait_qrun(&eh); - s_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE); + // s_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE); + am_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS, &eh); + halt_qrun(&eh); + // wait_qrun(&eh); free_qureg(&qr); cq_finalise(1); diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 1ceb4ee..5f68f60 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -207,8 +207,6 @@ void* device_listen(void* args) { // by running op I mean modyfing the internal dev_ctrl fields // and then the worker thread handles the rest. void device_dispatch_ctrl_op(const enum ctrl_code OP) { - // TODO: needs to add extra CTRL ops for EXEC sync in opcodes.h - // then modyfing host_sync_exec and host_wait_exec switch (OP) { case CQ_CTRL_INIT: { initialise_device(cq_mpi_verbosity); @@ -257,12 +255,36 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { break; } case CQ_CTRL_WAIT_EXEC: { - device_wait_all_ops(); + if (executor_handles[executor_id] == NULL) { + cq_log( + "%s [dispatch][WAIT_EXEC]: device ehp is NULL. Returning. " + "Expect crash.\n", + get_comm_source()); + return; + } + comms_exec_wait(executor_handles[executor_id]); + // device_wait_all_ops(); const int host_rank = CQ_MPI_HOST_RANK; send_exec_params(executor_handles[executor_id], host_rank); device_free_exec(&executor_handles[executor_id]); break; } + case CQ_CTRL_SYNC_EXEC: { + // NOTE: open question, do we want to sync creg as well? + if (executor_handles[executor_id] == NULL) { + cq_log( + "%s [dispatch][SYNC_EXEC]: device ehp is NULL. Returning. " + "Expect crash.\n", + get_comm_source()); + return; + } + comms_exec_sync(executor_handles[executor_id]); + cq_log("%s [dispatch][SYNC_EXEC]: executor synced\n", get_comm_source()); + const int host_rank = CQ_MPI_HOST_RANK; + send_exec_params(executor_handles[executor_id], host_rank); + cq_log("%s [dispatch][SYNC_EXEC]: sent executor\n", get_comm_source()); + break; + } case CQ_CTRL_WAIT: { // wait for worker and send info to the host // (which is blocked and waiting). @@ -276,12 +298,22 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { break; } case CQ_CTRL_ABORT: { + if (executor_handles[executor_id] == NULL) { + cq_log( + "%s [dispatch][ABORT]: device ehp is NULL. Returning. " + "Expect crash.\n", + get_comm_source()); + return; + } + comms_exec_halt(executor_handles[executor_id]); + cq_log("%s [dispatch][ABORT]: executor halted\n", get_comm_source()); break; } case CQ_CTRL_IDLE: { break; } case CQ_CTRL_TEST: { + cq_log("%s [dispatch][TEST]: called test op\n", get_comm_source()); break; } default: { @@ -292,8 +324,8 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // NOTE: aka host_wait_all_ops from comm.c // really what I want to use is host_wait_all_ops implementation but it needs -// renaming and wrapping into function called host_send_ctrl_op and depending on -// either MPI or pthread implementation use this in different ctx. +// renaming and wrapping into function called host_send_ctrl_op and depending +// on either MPI or pthread implementation use this in different ctx. size_t device_wait_all_ops(void) { pthread_mutex_lock(&dev_ctrl.device_lock); while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { @@ -312,6 +344,44 @@ void device_wait_comms(void) { pthread_mutex_unlock(&dev_comm.comm_lock); } +// for those below I probably I want comms_exec_sync, comms_exec_wait, +// comms_exec_halt +// NOTE: really what I want to use is host_sync_exec +// implemenetation but it needs renaming and wrapping into function called +// host_send_ctrl_op and depending on either MPI or pthread implementation use +// this in different ctx. +size_t comms_exec_sync(cq_exec* const ehp) { + size_t completed_shots = 0; + pthread_mutex_lock(&(ehp->lock)); + completed_shots = ehp->completed_shots; + pthread_mutex_unlock(&(ehp->lock)); + return completed_shots; +} + +// NOTE: +// really what I want to use is host_wait_exec implemenetation but it needs +// renaming and wrapping into function called host_send_ctrl_op and depending +// on either MPI or pthread implementation use this in different ctx. +size_t comms_exec_wait(cq_exec* const ehp) { + pthread_mutex_lock(&(ehp->lock)); + while (!ehp->complete) { + pthread_cond_wait(&(ehp->cond_exec_complete), &(ehp->lock)); + } + pthread_mutex_unlock(&(ehp->lock)); + return ehp->completed_shots; +} + +// NOTE: +// really what I want to use is host_request_halt implemenetation but it needs +// renaming and wrapping into function called host_send_ctrl_op and depending +// on either MPI or pthread implementation use this in different ctx. +void comms_exec_halt(cq_exec* const ehp) { + pthread_mutex_lock(&(ehp->lock)); + ehp->halt = true; + pthread_mutex_unlock(&(ehp->lock)); + return; +} + // ---------------------------------------------------------------------------- // Device Control Paramaters Comms // ---------------------------------------------------------------------------- @@ -431,33 +501,44 @@ void recv_exec_params(cq_exec** ehp, int src) { CQ_MPI_COMM); fname_size *= sizeof(char); - (*ehp)->fname = (char*)malloc(fname_size); - if ((*ehp)->fname == NULL) { - cq_log("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", - get_comm_source()); - exit(-10); + const size_t qreg_size = sizeof(qubit) * (*ehp)->nqubits; + // NOTE: creg_size * expected_shots?? or completed_shots? + // host should get completed, device should get expected + // and similarly when sending + size_t num_shots = 0; + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + num_shots = (*ehp)->completed_shots; + } else { + num_shots = (*ehp)->expected_shots; } - MPI_Unpack(recv_buffer, msg_size, &position, (*ehp)->fname, fname_size, - MPI_CHAR, CQ_MPI_COMM); + const size_t creg_size = sizeof(cstate) * (*ehp)->nmeasure * num_shots; - const size_t qreg_size = sizeof(qubit) * (*ehp)->nqubits; - // NOTE: creg_size * expected_shots?? - const size_t creg_size = sizeof(cstate) * (*ehp)->nmeasure; + // when on host the resources are already allocated! + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + (*ehp)->fname = (char*)malloc(fname_size); + if ((*ehp)->fname == NULL) { + cq_log("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", + get_comm_source()); + exit(-10); + } - (*ehp)->qreg = (qubit*)malloc(qreg_size); - if ((*ehp)->qreg == NULL) { - cq_log("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", - get_comm_source()); - exit(-10); - } + (*ehp)->qreg = (qubit*)malloc(qreg_size); + if ((*ehp)->qreg == NULL) { + cq_log("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", + get_comm_source()); + exit(-10); + } - (*ehp)->creg = (cstate*)malloc(creg_size); - if ((*ehp)->creg == NULL) { - cq_log("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", - get_comm_source()); - exit(-10); + (*ehp)->creg = (cstate*)malloc(creg_size); + if ((*ehp)->creg == NULL) { + cq_log("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", + get_comm_source()); + exit(-10); + } } + MPI_Unpack(recv_buffer, msg_size, &position, (*ehp)->fname, fname_size, + MPI_CHAR, CQ_MPI_COMM); MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->qreg, qreg_size, MPI_BYTE, CQ_MPI_COMM); MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->creg, creg_size, @@ -533,8 +614,16 @@ void send_exec_params(cq_exec* ehp, int dest) { &member_size); // qreg max_buffer_size += member_size; - // NOTE: creg_size * expected_shots?? - const size_t creg_size = sizeof(cstate) * ehp->nmeasure; + // NOTE: creg_size * expected_shots?? or completed_shots? + // host should send expected, device should send completed + size_t num_shots = 0; + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + num_shots = ehp->expected_shots; + } else { + num_shots = ehp->completed_shots; + } + const size_t creg_size = sizeof(cstate) * ehp->nmeasure * num_shots; + MPI_Pack_size(creg_size, MPI_BYTE, CQ_MPI_COMM, &member_size); // creg max_buffer_size += member_size; @@ -706,7 +795,7 @@ void print_ehp(const cq_exec* ehp) { } cq_log("\ncreg:\n"); - for (size_t i = 0; i < ehp->nmeasure; ++i) { + for (size_t i = 0; i < ehp->nmeasure * ehp->expected_shots; ++i) { cq_log("cstate[%zu]: %d\n", i, ehp->creg[i]); } cq_log("%s ehp details END\n\n", get_comm_source()); diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index 4c291e5..7d3bd3e 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -178,4 +178,13 @@ void print_alloc_params(const device_alloc_params* params); /// prints members of the executor. /// @param[in] ehp reference to executor void print_ehp(const cq_exec* ehp); + +// ---------------------------------------------------------------------------- +// TODO: Need to go to comms.h and comms.c and use existing stuff to wrap around +// ---------------------------------------------------------------------------- + +size_t comms_exec_sync(cq_exec* const ehp); +size_t comms_exec_wait(cq_exec* const ehp); +void comms_exec_halt(cq_exec* const ehp); + #endif From 5a1137b8f3ea8e61b963d5290730e5623c6b264f Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 12 May 2026 11:01:18 +0100 Subject: [PATCH 12/38] Fixed memory leak. --- src/host-device/mpi_comms.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 5f68f60..8ed2111 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -672,6 +672,7 @@ void send_exec_params(cq_exec* ehp, int dest) { MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + free(send_buffer); cq_log("%s [send_exec_params]: sent.\n", get_comm_source()); } From 896c95cf90049c58016c5a0ede08ff78f75bf4fe Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Wed, 13 May 2026 11:47:08 +0100 Subject: [PATCH 13/38] Finalised ring buffer on device side which handles the queuing executors. --- mpi-test.c | 6 +- src/host-device/mpi_comms.c | 203 ++++++++++++++++++++++++++++++------ src/host-device/mpi_comms.h | 12 +++ 3 files changed, 187 insertions(+), 34 deletions(-) diff --git a/mpi-test.c b/mpi-test.c index 1658e8a..4d8edcd 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -4,6 +4,7 @@ #include "cq.h" #include "datatypes.h" #include "host_ops.h" +#include "src/host-device/comms.h" #include "src/host-device/mpi_comms.h" #include @@ -56,8 +57,9 @@ int main() { // a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); // wait_qrun(&eh); // s_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE); - am_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS, &eh); - halt_qrun(&eh); + sm_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS); + // am_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS, &eh); + // halt_qrun(&eh); // wait_qrun(&eh); free_qureg(&qr); diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 8ed2111..525b981 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -4,9 +4,7 @@ #include "src/host-device/comms.h" #include "src/host/opcodes.h" -#include #include -#include #include #include @@ -22,8 +20,10 @@ struct cq_mpi_env { }; static cq_exec* executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; -static int executor_id = 0; +// static size_t executor_id = 0; +static size_t global_exec_id_counter = -1; static struct cq_mpi_env mpi_env = {.rank = -1}; +static size_t num_active_executors = 0; struct communicator { bool comm_busy; @@ -56,6 +56,9 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { if (mpi_env.rank != CQ_MPI_HOST_RANK) { device_init_comms(VERBOSITY); + for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { + executor_handles[i] = NULL; + } } } @@ -68,7 +71,7 @@ void finalise_host_device_mpi(const unsigned int VERBOSITY) { } MPI_Finalize(); if (VERBOSITY > 0) { - cq_log("Finalised MPI.\n"); + cq_log("%s Finalised MPI.\n", get_comm_source()); } } @@ -217,6 +220,14 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // don't need to insert op into worker. // we just wait until worker is done and cleanup. device_wait_all_ops(); + for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { + if (executor_handles[i] != NULL) { + cq_log( + "%s [dispatch][FINALISE]: executor handle with id: %zu is still " + "active. Something went wrong!\n", + get_comm_source(), i); + } + } finalise_device(cq_mpi_verbosity); break; } @@ -224,12 +235,15 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // Alloc is blocking: we get params, run allocation and // send back the updated params. // Also, because it's blocking I don't need to worry about - // params lifetime (from the worker perspecitve) + // params lifetime (from the worker perspective) const int host_rank = CQ_MPI_HOST_RANK; device_alloc_params params = {0}; recv_alloc_params(¶ms, host_rank); + cq_log("%s [dispatch][ALLOC]: inserting op\n", get_comm_source()); insert_op(OP, ¶ms); + cq_log("%s [dispatch][ALLOC]: inserted op\n", get_comm_source()); device_wait_all_ops(); + cq_log("%s [dispatch][ALLOC]: waited for finish\n", get_comm_source()); send_alloc_params(¶ms, host_rank); break; } @@ -244,9 +258,38 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { break; } case CQ_CTRL_RUN_QKERNEL: { + if (num_active_executors >= __CQ_DEVICE_QUEUE_SIZE__) { + cq_log( + "%s [dispatch][RUN_QKERNEL]: You have oversubsribed the executor " + "queue. We allow up to %d " + "concurrent executors per device. Exiting", + get_comm_source(), __CQ_DEVICE_QUEUE_SIZE__); + exit(CQ_MPI_RUNTIME_ERROR); + } const int host_rank = CQ_MPI_HOST_RANK; - recv_exec_params(&executor_handles[executor_id], host_rank); - insert_op(OP, executor_handles[executor_id]); + cq_exec* tmp_exec = NULL; + recv_exec_params(&tmp_exec, host_rank); + // NOTE: this can be done here to free old one + // and allocate new one rather than in wait_exec + // device_free_exec(&executor_handles[tmp_exec->id]); + // actually if executor_handles[id] != NULL then it didn't finish and we + // should wait here until we are done. + // + // NOTE: 2 + // Now the question is should we block? + // 1. if we block here and wait for all ops to complete then + // in situation if host submits N_exe > QUEUE_SIZE, we lock here + // indefinitely + // ex: a_qrun(...) x QUEUE_SIZE + 1 (and no wait_qrun...) + // => deadlock + // 2. alternative would be wait to set exec_queue_full flag + // and wait until it is released + // 3. we can just exit and fail! -- most reasonable: + executor_handles[tmp_exec->id] = tmp_exec; + insert_op(OP, executor_handles[tmp_exec->id]); + ++num_active_executors; + // recv_exec_params(&executor_handles[executor_id], host_rank); + // insert_op(OP, executor_handles[executor_id]); break; } case CQ_CTRL_RUN_PQKERNEL: { @@ -255,32 +298,44 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { break; } case CQ_CTRL_WAIT_EXEC: { + const int host_rank = CQ_MPI_HOST_RANK; + const size_t executor_id = recv_exec_id(host_rank); if (executor_handles[executor_id] == NULL) { cq_log( "%s [dispatch][WAIT_EXEC]: device ehp is NULL. Returning. " - "Expect crash.\n", + "Exiting\n", get_comm_source()); - return; + exit(CQ_MPI_RUNTIME_ERROR); } - comms_exec_wait(executor_handles[executor_id]); - // device_wait_all_ops(); - const int host_rank = CQ_MPI_HOST_RANK; + // NOTE: This is commented out as it can cause a deadlock + // comms_exec_wait(executor_handles[executor_id]); + device_wait_all_ops(); send_exec_params(executor_handles[executor_id], host_rank); + // NOTE: This can be done when allocating new in offload + // i.e. clear old one and allocate new one device_free_exec(&executor_handles[executor_id]); + --num_active_executors; + if (num_active_executors < 0) { + cq_log( + "%s [dispatch][WAIT_EXEC]: The number of active executors is < 0! " + "Should not happen. Exiting", + get_comm_source()); + exit(CQ_MPI_RUNTIME_ERROR); + } break; } case CQ_CTRL_SYNC_EXEC: { - // NOTE: open question, do we want to sync creg as well? + const int host_rank = CQ_MPI_HOST_RANK; + const size_t executor_id = recv_exec_id(host_rank); if (executor_handles[executor_id] == NULL) { cq_log( "%s [dispatch][SYNC_EXEC]: device ehp is NULL. Returning. " - "Expect crash.\n", + "Exiting\n", get_comm_source()); - return; + exit(CQ_MPI_RUNTIME_ERROR); } comms_exec_sync(executor_handles[executor_id]); cq_log("%s [dispatch][SYNC_EXEC]: executor synced\n", get_comm_source()); - const int host_rank = CQ_MPI_HOST_RANK; send_exec_params(executor_handles[executor_id], host_rank); cq_log("%s [dispatch][SYNC_EXEC]: sent executor\n", get_comm_source()); break; @@ -298,12 +353,14 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { break; } case CQ_CTRL_ABORT: { + const int host_rank = CQ_MPI_HOST_RANK; + const size_t executor_id = recv_exec_id(host_rank); if (executor_handles[executor_id] == NULL) { cq_log( "%s [dispatch][ABORT]: device ehp is NULL. Returning. " - "Expect crash.\n", + "Exiting\n", get_comm_source()); - return; + exit(CQ_MPI_RUNTIME_ERROR); } comms_exec_halt(executor_handles[executor_id]); cq_log("%s [dispatch][ABORT]: executor halted\n", get_comm_source()); @@ -363,11 +420,18 @@ size_t comms_exec_sync(cq_exec* const ehp) { // renaming and wrapping into function called host_send_ctrl_op and depending // on either MPI or pthread implementation use this in different ctx. size_t comms_exec_wait(cq_exec* const ehp) { + cq_log("%s [comms_exec_wait]: started wait...\n", get_comm_source()); pthread_mutex_lock(&(ehp->lock)); + cq_log("%s [comms_exec_wait]: locked mutex\n", get_comm_source()); while (!ehp->complete) { + cq_log("%s [comms_exec_wait]: started loop\n", get_comm_source()); pthread_cond_wait(&(ehp->cond_exec_complete), &(ehp->lock)); + cq_log("%s [comms_exec_wait]: ending loop\n", get_comm_source()); } + cq_log("%s [comms_exec_wait]: done while loop\n", get_comm_source()); pthread_mutex_unlock(&(ehp->lock)); + cq_log("%s [comms_exec_wait]: unlocked mutex\n", get_comm_source()); + cq_log("%s [comms_exec_wait]: done waiting\n", get_comm_source()); return ehp->completed_shots; } @@ -413,9 +477,21 @@ void host_comm_params(const enum ctrl_code OP, void* params) { } case CQ_CTRL_WAIT_EXEC: { const int device_rank = CQ_MPI_DEVICE_RANK; + send_exec_id(((cq_exec*)params)->id, device_rank); + recv_exec_params(¶ms, device_rank); + break; + } + case CQ_CTRL_SYNC_EXEC: { + const int device_rank = CQ_MPI_DEVICE_RANK; + send_exec_id(((cq_exec*)params)->id, device_rank); recv_exec_params(¶ms, device_rank); break; } + case CQ_CTRL_ABORT: { + const int device_rank = CQ_MPI_DEVICE_RANK; + send_exec_id(((cq_exec*)params)->id, device_rank); + break; + } default: { break; } @@ -428,8 +504,8 @@ void recv_alloc_params(device_alloc_params* params, int src) { const size_t params_size = sizeof(device_alloc_params); MPI_Status status; - MPI_Recv((char*)params, params_size, MPI_BYTE, src, CQ_MPI_COMMS_TAG, - CQ_MPI_COMM, &status); + MPI_Recv(params, params_size, MPI_BYTE, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, + &status); cq_log("%s [recv_alloc_params]: received.\n", get_comm_source()); print_alloc_params(params); @@ -444,6 +520,21 @@ void send_alloc_params(const device_alloc_params* params, int dest) { cq_log("%s [send_alloc_params]: sent.\n", get_comm_source()); } +size_t recv_exec_id(const int src) { + cq_log("%s [recv_exec_id]: receiving...\n", get_comm_source()); + size_t id = -1; + MPI_Status status; + MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, &status); + cq_log("%s [recv_exec_id]: received id: %zu\n", get_comm_source(), id); + return id; +} + +void send_exec_id(const size_t id, const int dest) { + cq_log("%s [send_exec_id]: sending id: %zu...\n", get_comm_source(), id); + MPI_Ssend(&id, 1, MPI_UINT64_T, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + cq_log("%s [send_exec_id]: sent\n", get_comm_source()); +} + void recv_exec_params(cq_exec** ehp, int src) { // TODO: do validation cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); @@ -460,16 +551,19 @@ void recv_exec_params(cq_exec** ehp, int src) { // ehp = (cq_exec*)malloc(msg_size + sizeof(pthread_mutex_t) + // sizeof(pthread_cond_t) + sizeof(void*)); *ehp = (cq_exec*)malloc(sizeof(cq_exec)); + pthread_mutex_init(&(*ehp)->lock, NULL); + pthread_cond_init(&(*ehp)->cond_exec_complete, NULL); } else { cq_log( "%s [recv_exec_params]: *ehp is already allocated. So I'm on host.\n", get_comm_source()); } + pthread_mutex_lock(&(*ehp)->lock); if (recv_buffer == NULL || *ehp == NULL) { cq_log("%s [recv_exec_params]: malloc failed. Exiting\n", get_comm_source()); - exit(-10); + exit(CQ_MPI_MALLOC_ERROR); } MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_COMMS_TAG, @@ -479,6 +573,8 @@ void recv_exec_params(cq_exec** ehp, int src) { const size_t cq_status_size = sizeof(cq_status); // unpack int position = 0; + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->id, 1, MPI_UINT64_T, + CQ_MPI_COMM); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->exec_init, bool_size, MPI_BYTE, CQ_MPI_COMM); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->complete, bool_size, @@ -519,21 +615,21 @@ void recv_exec_params(cq_exec** ehp, int src) { if ((*ehp)->fname == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", get_comm_source()); - exit(-10); + exit(CQ_MPI_MALLOC_ERROR); } (*ehp)->qreg = (qubit*)malloc(qreg_size); if ((*ehp)->qreg == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", get_comm_source()); - exit(-10); + exit(CQ_MPI_MALLOC_ERROR); } (*ehp)->creg = (cstate*)malloc(creg_size); if ((*ehp)->creg == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", get_comm_source()); - exit(-10); + exit(CQ_MPI_MALLOC_ERROR); } } @@ -550,9 +646,11 @@ void recv_exec_params(cq_exec** ehp, int src) { cq_log("%s [recv_exec_params]: received.\n", get_comm_source()); print_ehp(*ehp); + pthread_mutex_unlock(&(*ehp)->lock); } void send_exec_params(cq_exec* ehp, int dest) { + pthread_mutex_lock(&ehp->lock); cq_log("%s [send_exec_params]: sending...\n", get_comm_source()); print_ehp(ehp); @@ -560,8 +658,11 @@ void send_exec_params(cq_exec* ehp, int dest) { const size_t cq_status_size = sizeof(cq_status); int max_buffer_size = 0; int member_size = 0; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + &max_buffer_size); // id MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM, - &max_buffer_size); // exec_init + &member_size); // exec_init + max_buffer_size += member_size; MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM, &member_size); // complete max_buffer_size += member_size; @@ -630,12 +731,14 @@ void send_exec_params(cq_exec* ehp, int dest) { // NOTE: sending ehp->params left for another day... it's for pqkerns - char* send_buffer = malloc(max_buffer_size); + void* send_buffer = malloc(max_buffer_size); if (send_buffer == NULL) { - cq_log("Failed to allocate buffer for sending executor handle.\n"); - exit(-1); + cq_log("Failed to allocate buffer for sending executor handle. Exiting\n"); + exit(CQ_MPI_MALLOC_ERROR); } int position = 0; + MPI_Pack(&ehp->id, 1, MPI_UINT64_T, send_buffer, max_buffer_size, &position, + CQ_MPI_COMM); MPI_Pack(&ehp->exec_init, bool_size, MPI_BYTE, send_buffer, max_buffer_size, &position, CQ_MPI_COMM); MPI_Pack(&ehp->complete, bool_size, MPI_BYTE, send_buffer, max_buffer_size, @@ -669,14 +772,28 @@ void send_exec_params(cq_exec* ehp, int dest) { // NOTE: ehp->prams left for another day MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); free(send_buffer); cq_log("%s [send_exec_params]: sent.\n", get_comm_source()); + pthread_mutex_unlock(&ehp->lock); } void device_free_exec(cq_exec** ehp) { + cq_log("%s [device_free_exec]: freeing the executor\n", get_comm_source()); + if (ehp == NULL) { + cq_log("%s [device_free_exec]: ehp is NULL\n", get_comm_source()); + return; + } + + if (*ehp == NULL) { + cq_log("%s [device_free_exec]: *ehp is NULL\n", get_comm_source()); + return; + } + + pthread_mutex_lock(&(*ehp)->lock); if ((*ehp)->fname != NULL) { free((*ehp)->fname); (*ehp)->fname = NULL; @@ -697,10 +814,15 @@ void device_free_exec(cq_exec** ehp) { (*ehp)->params = NULL; } + pthread_mutex_unlock(&(*ehp)->lock); + pthread_mutex_destroy(&(*ehp)->lock); + pthread_cond_destroy(&(*ehp)->cond_exec_complete); if ((*ehp) != NULL) { free((*ehp)); (*ehp) = NULL; } + + cq_log("%s [device_free_exec]: freed the executor\n", get_comm_source()); } // ---------------------------------------------------------------------------- @@ -763,6 +885,14 @@ const char* op_to_str(const enum ctrl_code OP) { return "CQ_CTRL_ABORT"; break; } + case CQ_CTRL_WAIT_EXEC: { + return "CQ_CTRL_WAIT_EXEC"; + break; + } + case CQ_CTRL_SYNC_EXEC: { + return "CQ_CTRL_SYNC_EXEC"; + break; + } default: { break; } @@ -782,13 +912,14 @@ void print_ehp(const cq_exec* ehp) { } cq_log( - "%s ehp details:\nexec_init: %d, complete: %d, halt: %d, STATUS: " + "%s ehp details:\nid: %zu, exec_init: %d, complete: %d, halt: %d, " + "STATUS: " "%d\nNQUBITS: " "%zu, completed_shots: %zu, expected_shots: %zu, NMEASURE: %zu\nfname: " "%s\nqreg:\n", - get_comm_source(), ehp->exec_init, ehp->complete, ehp->halt, ehp->status, - ehp->nqubits, ehp->completed_shots, ehp->expected_shots, ehp->nmeasure, - ehp->fname); + get_comm_source(), ehp->id, ehp->exec_init, ehp->complete, ehp->halt, + ehp->status, ehp->nqubits, ehp->completed_shots, ehp->expected_shots, + ehp->nmeasure, ehp->fname); for (size_t i = 0; i < ehp->nqubits; ++i) { cq_log("qubit[%zu]: reg_idx: %zu, offset: %zu, N: %zu\n", i, @@ -801,3 +932,11 @@ void print_ehp(const cq_exec* ehp) { } cq_log("%s ehp details END\n\n", get_comm_source()); } + +size_t assign_exec_id(void) { + // ++executor_id; + // return executor_id; + ++global_exec_id_counter; + global_exec_id_counter %= __CQ_DEVICE_QUEUE_SIZE__; + return global_exec_id_counter; +} diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index 7d3bd3e..c9d57ab 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -15,6 +15,10 @@ #define CQ_MPI_DEVICE_RANK 1 #define CQ_MPI_COMMS_TAG 0 +#define CQ_MPI_RUNTIME_ERROR -4 +#define CQ_MPI_MALLOC_ERROR -5 + +// device_wait_comms(); #define RUN_HOST_ONLY() \ { \ int rank = get_rank(); \ @@ -62,6 +66,8 @@ void mpi_host_wait_all_ops(void); /// wrapper around device_wait_comms, intended to be called from the host. /// The intended caller of this function is cq_finalise only! void host_device_final_sync(void); +// TODO: rename above with below +void host_device_sync_comms(void); // ---------------------------------------------------------------------------- // Device Comm Ops @@ -106,6 +112,7 @@ size_t device_wait_all_ops(void); /// it needs to be called only once at the end to ensure that the main thread /// does not clean-up and close MPI before work is done on the worker. void device_wait_comms(void); +// TODO: above can be static and in mpi_comms.c // ---------------------------------------------------------------------------- // Device Control Paramaters Comms @@ -130,6 +137,9 @@ void recv_alloc_params(device_alloc_params* params, int src); /// @param dest destination rank of outgoing message void send_alloc_params(const device_alloc_params* params, int dest); +size_t recv_exec_id(const int src); +void send_exec_id(const size_t id, const int dest); + /// /// receives executor handle from the source. Also, if called on device, /// allocates executor in on-device memory, which then needs to be freed using @@ -179,6 +189,8 @@ void print_alloc_params(const device_alloc_params* params); /// @param[in] ehp reference to executor void print_ehp(const cq_exec* ehp); +size_t assign_exec_id(void); + // ---------------------------------------------------------------------------- // TODO: Need to go to comms.h and comms.c and use existing stuff to wrap around // ---------------------------------------------------------------------------- From 2ea381f1c8553f8d3cda0e79f0ee633278a3bc0c Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Wed, 13 May 2026 15:54:07 +0100 Subject: [PATCH 14/38] [WIP]: started work on handling MPI on quantum workers. This commit can be treated as a working checkpoint. --- src/host-device/mpi_comms.c | 199 +++++++++++++++++++++++------------- src/host-device/mpi_comms.h | 22 +++- 2 files changed, 146 insertions(+), 75 deletions(-) diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 525b981..8bf19f3 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -6,6 +6,8 @@ #include +#include +#include #include #include #include @@ -13,17 +15,18 @@ #include #include -static const MPI_Comm CQ_MPI_COMM = MPI_COMM_WORLD; +static const MPI_Comm CQ_MPI_COMM_WORLD = MPI_COMM_WORLD; +static MPI_Comm CQ_MPI_SPLIT_COMM; struct cq_mpi_env { int rank; + unsigned int verbosity; }; static cq_exec* executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; -// static size_t executor_id = 0; static size_t global_exec_id_counter = -1; -static struct cq_mpi_env mpi_env = {.rank = -1}; static size_t num_active_executors = 0; +static struct cq_mpi_env mpi_env = {.rank = -1, .verbosity = 0}; struct communicator { bool comm_busy; @@ -33,27 +36,51 @@ struct communicator { }; static struct communicator dev_comm = {0}; -static unsigned int cq_mpi_verbosity = 0; static void cq_log(const char* format, ...) { - if (CQ_MPI_IMPL_DEBUG) { - va_list(args); - va_start(args, format); - vprintf(format, args); - } +#ifdef CQ_MPI_IMPL_DEBUG + va_list(args); + va_start(args, format); + vprintf(format, args); +#endif } void init_host_device_mpi(const unsigned int VERBOSITY) { - cq_mpi_verbosity = VERBOSITY; + mpi_env.verbosity = VERBOSITY; if (VERBOSITY > 0) { cq_log("Initialising MPI.\n"); } + + int nprocs, quest_nprocs, world_rank, quest_rank; + MPI_Comm comm_split; + MPI_Init(NULL, NULL); - MPI_Comm_rank(CQ_MPI_COMM, &mpi_env.rank); + + MPI_Comm_size(CQ_MPI_COMM_WORLD, &nprocs); + // TODO: check nproc - 2 (host + device) == power of 2 + + MPI_Comm_rank(CQ_MPI_COMM_WORLD, &world_rank); + MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); + const int QUANTUM_WORKER = world_rank > 1; + + // MPI_Comm_split(MPI_COMM_WORLD, QUANTUM_WORKER, world_rank, &comm_split); + // MPI_Comm_dup(comm_split, &CQ_MPI_COMM_WORLD); + // MPI_Comm_free(&comm_split); + + // MPI_Comm_split(CQ_MPI_COMM_WORLD_WORLD, QUANTUM_WORKER, world_rank, + // &CQ_MPI_COMM_WORLD); MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); + // MPI_Comm_size(CQ_MPI_COMM_WORLD, &quest_nprocs); + // cq_log("%s there are %d processes in quest comm\n", get_comm_source(), + // quest_nprocs); + if (VERBOSITY > 0) { cq_log("Initialised MPI.\n"); } + // if (is_quantum_worker()) { + // return; + // } + if (mpi_env.rank != CQ_MPI_HOST_RANK) { device_init_comms(VERBOSITY); for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { @@ -81,7 +108,7 @@ void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params) { const int device_rank = CQ_MPI_DEVICE_RANK; cq_log("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), op_to_str(OP)); - MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD); // send params based on OP host_comm_params(OP, params); cq_log("%s [send_ctrl_op]: sent %s\n", get_comm_source(), op_to_str(OP)); @@ -94,7 +121,7 @@ void mpi_host_wait_all_ops(void) { const int device_rank = CQ_MPI_DEVICE_RANK; MPI_Status status; MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_MPI_COMMS_TAG, - CQ_MPI_COMM, &status); + CQ_MPI_COMM_WORLD, &status); cq_log("%s [wait_all_ops]: all ops completed (num_ops: %zu)\n", get_comm_source(), num_ops); } @@ -185,14 +212,16 @@ void* device_listen(void* args) { pthread_mutex_unlock(&dev_comm.comm_lock); enum ctrl_code OP; + // device master (rank 0) gets from host from different comm const int host_rank = CQ_MPI_HOST_RANK; MPI_Status status; cq_log("%s [device_listen]: receiving OP...\n", get_comm_source()); - MPI_Recv(&OP, 1, MPI_INT, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, + MPI_Recv(&OP, 1, MPI_INT, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD, &status); cq_log("%s [device_listen]: received %s\n", get_comm_source(), op_to_str(OP)); + // rank 0 brodcast to rest of q-workers then all do the dispatch device_dispatch_ctrl_op(OP); pthread_mutex_lock(&dev_comm.comm_lock); @@ -202,7 +231,7 @@ void* device_listen(void* args) { } cq_log("%s closing connection.\n", get_comm_source()); - finalise_host_device_mpi(cq_mpi_verbosity); + finalise_host_device_mpi(mpi_env.verbosity); return NULL; } @@ -212,8 +241,8 @@ void* device_listen(void* args) { void device_dispatch_ctrl_op(const enum ctrl_code OP) { switch (OP) { case CQ_CTRL_INIT: { - initialise_device(cq_mpi_verbosity); - insert_op(OP, &cq_mpi_verbosity); + initialise_device(mpi_env.verbosity); + insert_op(OP, &mpi_env.verbosity); break; } case CQ_CTRL_FINALISE: { @@ -228,7 +257,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { get_comm_source(), i); } } - finalise_device(cq_mpi_verbosity); + finalise_device(mpi_env.verbosity); break; } case CQ_CTRL_ALLOC: { @@ -239,11 +268,8 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { const int host_rank = CQ_MPI_HOST_RANK; device_alloc_params params = {0}; recv_alloc_params(¶ms, host_rank); - cq_log("%s [dispatch][ALLOC]: inserting op\n", get_comm_source()); insert_op(OP, ¶ms); - cq_log("%s [dispatch][ALLOC]: inserted op\n", get_comm_source()); device_wait_all_ops(); - cq_log("%s [dispatch][ALLOC]: waited for finish\n", get_comm_source()); send_alloc_params(¶ms, host_rank); break; } @@ -348,7 +374,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { cq_log("%s [dispatch]: sending num ops: %zu...\n", get_comm_source(), num_ops); MPI_Ssend(&num_ops, 1, MPI_UINT64_T, host_rank, CQ_MPI_COMMS_TAG, - CQ_MPI_COMM); + CQ_MPI_COMM_WORLD); cq_log("%s [dispatch]: sent num ops.\n", get_comm_source()); break; } @@ -504,8 +530,8 @@ void recv_alloc_params(device_alloc_params* params, int src) { const size_t params_size = sizeof(device_alloc_params); MPI_Status status; - MPI_Recv(params, params_size, MPI_BYTE, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, - &status); + MPI_Recv(params, params_size, MPI_BYTE, src, CQ_MPI_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); cq_log("%s [recv_alloc_params]: received.\n", get_comm_source()); print_alloc_params(params); @@ -516,7 +542,7 @@ void send_alloc_params(const device_alloc_params* params, int dest) { const size_t params_size = sizeof(device_alloc_params); print_alloc_params(params); MPI_Ssend((char*)params, params_size, MPI_BYTE, dest, CQ_MPI_COMMS_TAG, - CQ_MPI_COMM); + CQ_MPI_COMM_WORLD); cq_log("%s [send_alloc_params]: sent.\n", get_comm_source()); } @@ -524,14 +550,15 @@ size_t recv_exec_id(const int src) { cq_log("%s [recv_exec_id]: receiving...\n", get_comm_source()); size_t id = -1; MPI_Status status; - MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, &status); + MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD, + &status); cq_log("%s [recv_exec_id]: received id: %zu\n", get_comm_source(), id); return id; } void send_exec_id(const size_t id, const int dest) { cq_log("%s [send_exec_id]: sending id: %zu...\n", get_comm_source(), id); - MPI_Ssend(&id, 1, MPI_UINT64_T, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + MPI_Ssend(&id, 1, MPI_UINT64_T, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD); cq_log("%s [send_exec_id]: sent\n", get_comm_source()); } @@ -540,7 +567,8 @@ void recv_exec_params(cq_exec** ehp, int src) { cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); int msg_size; MPI_Status status; - MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM, &status); + MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD, + &status); void* recv_buffer = malloc(msg_size); // reserve space for all the date + currently unused members @@ -567,34 +595,34 @@ void recv_exec_params(cq_exec** ehp, int src) { } MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_COMMS_TAG, - CQ_MPI_COMM, &status); + CQ_MPI_COMM_WORLD, &status); const size_t bool_size = sizeof(bool); const size_t cq_status_size = sizeof(cq_status); // unpack int position = 0; MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->id, 1, MPI_UINT64_T, - CQ_MPI_COMM); + CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->exec_init, bool_size, - MPI_BYTE, CQ_MPI_COMM); + MPI_BYTE, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->complete, bool_size, - MPI_BYTE, CQ_MPI_COMM); + MPI_BYTE, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->halt, bool_size, - MPI_BYTE, CQ_MPI_COMM); + MPI_BYTE, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->status, cq_status_size, - MPI_BYTE, CQ_MPI_COMM); + MPI_BYTE, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->nqubits, 1, - MPI_UINT64_T, CQ_MPI_COMM); + MPI_UINT64_T, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->completed_shots, 1, - MPI_UINT64_T, CQ_MPI_COMM); + MPI_UINT64_T, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->expected_shots, 1, - MPI_UINT64_T, CQ_MPI_COMM); + MPI_UINT64_T, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->nmeasure, 1, - MPI_UINT64_T, CQ_MPI_COMM); + MPI_UINT64_T, CQ_MPI_COMM_WORLD); size_t fname_size = 0; MPI_Unpack(recv_buffer, msg_size, &position, &fname_size, 1, MPI_UINT64_T, - CQ_MPI_COMM); + CQ_MPI_COMM_WORLD); fname_size *= sizeof(char); const size_t qreg_size = sizeof(qubit) * (*ehp)->nqubits; @@ -634,11 +662,11 @@ void recv_exec_params(cq_exec** ehp, int src) { } MPI_Unpack(recv_buffer, msg_size, &position, (*ehp)->fname, fname_size, - MPI_CHAR, CQ_MPI_COMM); + MPI_CHAR, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->qreg, qreg_size, - MPI_BYTE, CQ_MPI_COMM); + MPI_BYTE, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->creg, creg_size, - MPI_BYTE, CQ_MPI_COMM); + MPI_BYTE, CQ_MPI_COMM_WORLD); // NOTE: recv ehp->params left for another day... it's for pqkerns @@ -658,30 +686,30 @@ void send_exec_params(cq_exec* ehp, int dest) { const size_t cq_status_size = sizeof(cq_status); int max_buffer_size = 0; int member_size = 0; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, &max_buffer_size); // id - MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM, + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, &member_size); // exec_init max_buffer_size += member_size; - MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM, + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, &member_size); // complete max_buffer_size += member_size; - MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM, + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, &member_size); // halt max_buffer_size += member_size; - MPI_Pack_size(cq_status_size, MPI_BYTE, CQ_MPI_COMM, + MPI_Pack_size(cq_status_size, MPI_BYTE, CQ_MPI_COMM_WORLD, &member_size); // status max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, &member_size); // nqubits max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, &member_size); // completed_shots max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, &member_size); // expected_shots max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, &member_size); // nmeasure max_buffer_size += member_size; @@ -689,12 +717,12 @@ void send_exec_params(cq_exec* ehp, int dest) { // TODO: sending pthred stuff doesn't sound like a good idea... // sounds like UB // const size_t pthread_mutex_size = sizeof(pthread_mutex_t); - // MPI_Pack_size(pthread_mutex_size, MPI_BYTE, CQ_MPI_COMM, + // MPI_Pack_size(pthread_mutex_size, MPI_BYTE, CQ_MPI_COMM_WORLD, // &member_size); // lock // max_buffer_size += member_size; // // const size_t pthread_cond_size = sizeof(pthread_cond_t); - // MPI_Pack_size(pthread_cond_size, MPI_BYTE, CQ_MPI_COMM, + // MPI_Pack_size(pthread_cond_size, MPI_BYTE, CQ_MPI_COMM_WORLD, // &member_size); // cond_exec_complete // max_buffer_size += member_size; // ------------------------------------------------------------------------- @@ -703,15 +731,15 @@ void send_exec_params(cq_exec* ehp, int dest) { if (ehp->fname != NULL) { fname_size = strlen(ehp->fname) + 1; } - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM, + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, &member_size); // fname_size max_buffer_size += member_size; - MPI_Pack_size(fname_size, MPI_CHAR, CQ_MPI_COMM, + MPI_Pack_size(fname_size, MPI_CHAR, CQ_MPI_COMM_WORLD, &member_size); // fname max_buffer_size += member_size; const size_t qreg_size = sizeof(qubit) * ehp->nqubits; - MPI_Pack_size(qreg_size, MPI_BYTE, CQ_MPI_COMM, + MPI_Pack_size(qreg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, &member_size); // qreg max_buffer_size += member_size; @@ -725,7 +753,7 @@ void send_exec_params(cq_exec* ehp, int dest) { } const size_t creg_size = sizeof(cstate) * ehp->nmeasure * num_shots; - MPI_Pack_size(creg_size, MPI_BYTE, CQ_MPI_COMM, + MPI_Pack_size(creg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, &member_size); // creg max_buffer_size += member_size; @@ -738,43 +766,43 @@ void send_exec_params(cq_exec* ehp, int dest) { } int position = 0; MPI_Pack(&ehp->id, 1, MPI_UINT64_T, send_buffer, max_buffer_size, &position, - CQ_MPI_COMM); + CQ_MPI_COMM_WORLD); MPI_Pack(&ehp->exec_init, bool_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(&ehp->complete, bool_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(&ehp->halt, bool_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(&ehp->status, cq_status_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(&ehp->nqubits, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(&ehp->completed_shots, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(&ehp->expected_shots, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(&ehp->nmeasure, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); // NOTE: skip pthread stuff... MPI_Pack(&fname_size, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(ehp->fname, fname_size, MPI_CHAR, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(ehp->qreg, qreg_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); MPI_Pack(ehp->creg, creg_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM); + &position, CQ_MPI_COMM_WORLD); // NOTE: ehp->prams left for another day - MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM); + MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD); MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_COMMS_TAG, - CQ_MPI_COMM); + CQ_MPI_COMM_WORLD); free(send_buffer); cq_log("%s [send_exec_params]: sent.\n", get_comm_source()); @@ -829,9 +857,12 @@ void device_free_exec(cq_exec** ehp) { // Helpers // ---------------------------------------------------------------------------- -const char* get_comm_source() { +const char* get_comm_source(void) { if (mpi_env.rank == CQ_MPI_HOST_RANK) { return "Host:\t\t"; + + } else if (is_quantum_worker()) { + return "Quantum Worker:\t\t"; } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { return "Device:\t\t"; } else { @@ -940,3 +971,25 @@ size_t assign_exec_id(void) { global_exec_id_counter %= __CQ_DEVICE_QUEUE_SIZE__; return global_exec_id_counter; } + +int is_quantum_worker(void) { + int world_rank; + MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); + return world_rank > 1; +} + +MPI_Comm get_quest_comm(void) { + return CQ_MPI_COMM_WORLD; +} + +void foo() { + // initCustomMpiCommQuESTEnv(MPI_Comm questComm, int useGpuAccel, int + // useMultithread) + + // Basically what we want is we get MPI_COMM_WORLD + // we check that size - 2 (or host = 1 + n_dev) is power of 2 + // split it use one subcomm for host-device comms with 2 ranks + // the rest goes to quest and can be accessed from here using getter + // and then if build with MPI (both this and QuEST) call in control.c + // initCustomMpiCommQuESTEnv(get_quest_comm(), gpu, mthrd); +} diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index c9d57ab..bf7cf46 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -6,24 +6,34 @@ #include "src/host-device/comms.h" #include "src/host/opcodes.h" +#include + // ---------------------------------------------------------------------------- // Macros // ---------------------------------------------------------------------------- -#define CQ_MPI_IMPL_DEBUG 1 +#define CQ_MPI_IMPL_DEBUG + +// #define CQ_WITH_MPI_COMMS #define CQ_MPI_HOST_RANK 0 #define CQ_MPI_DEVICE_RANK 1 +#define CQ_MPI_DEVICE_MASTER_RANK 0 #define CQ_MPI_COMMS_TAG 0 #define CQ_MPI_RUNTIME_ERROR -4 #define CQ_MPI_MALLOC_ERROR -5 // device_wait_comms(); +// if (is_quantum_worker()) { +// return CQ_SUCCESS; +// } + #define RUN_HOST_ONLY() \ { \ int rank = get_rank(); \ - if (rank == -1) \ + if (rank == -1) { \ return CQ_ERROR; \ + } \ if (get_rank() != CQ_MPI_HOST_RANK) { \ return CQ_SUCCESS; \ } \ @@ -124,6 +134,9 @@ void device_wait_comms(void); /// @param[in,out] params void pointer to arbitrary params void host_comm_params(const enum ctrl_code OP, void* params); +// NOTE: Alloc params can check the results across thread +// before Device Master sends to Host (in send) +// in recv Master will broadcast to Q-workers /// /// receives allocation parameters from the source. /// @param[out] params reference to parameters to store the results of @@ -140,6 +153,8 @@ void send_alloc_params(const device_alloc_params* params, int dest); size_t recv_exec_id(const int src); void send_exec_id(const size_t id, const int dest); +// NOTE: Exec params can be send by Device Master to Host (in send) +// in recv Master will broadcast to Q-workers /// /// receives executor handle from the source. Also, if called on device, /// allocates executor in on-device memory, which then needs to be freed using @@ -191,6 +206,9 @@ void print_ehp(const cq_exec* ehp); size_t assign_exec_id(void); +int is_quantum_worker(void); +MPI_Comm get_quest_comm(void); + // ---------------------------------------------------------------------------- // TODO: Need to go to comms.h and comms.c and use existing stuff to wrap around // ---------------------------------------------------------------------------- From ba3b190ec6413e70a46aee3a6d65b2d2723cfde7 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Wed, 13 May 2026 16:02:46 +0100 Subject: [PATCH 15/38] Updated build options for conditional compilation. --- CMakeLists.txt | 21 +++++++++++++++++---- src/host-device/mpi_comms.h | 2 -- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ec1d35d..31660fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,10 +47,12 @@ option( ) message(STATUS "BUILD_WITH_MPI turned ${BUILD_WITH_MPI}.") -if (BUILD_WITH_MPI) - find_package(MPI REQUIRED) - include_directories(SYSTEM ${MPI_INCLUDE_PATH}) -endif() +option( + QUEST_WITH_MPI + "Flag to define if QuEST was built with MPI. This will affect how we do the communication." + OFF +) +message(STATUS "QUEST_WITH_MPI turned ${QUEST_WITH_MPI}.") # Dependencies @@ -62,6 +64,17 @@ find_package(QuEST REQUIRED) add_library(cq-simbe) add_library(CQ-SIMBE::cq-simbe ALIAS cq-simbe) +if (BUILD_WITH_MPI) + find_package(MPI REQUIRED) + include_directories(SYSTEM ${MPI_INCLUDE_PATH}) + target_compile_definitions(cq-simbe PRIVATE CQ_WITH_MPI_COMMS=1) +endif() + +if (BUILD_WITH_MPI AND QUEST_WITH_MPI) + target_compile_definitions(cq-simbe PRIVATE CQ_CONF_QUEST_WITH_MPI=1) +endif() + + target_compile_features(cq-simbe PRIVATE c_std_99) target_compile_options(cq-simbe PRIVATE -Wall) diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index bf7cf46..0838fcf 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -13,8 +13,6 @@ // ---------------------------------------------------------------------------- #define CQ_MPI_IMPL_DEBUG -// #define CQ_WITH_MPI_COMMS - #define CQ_MPI_HOST_RANK 0 #define CQ_MPI_DEVICE_RANK 1 #define CQ_MPI_DEVICE_MASTER_RANK 0 From 11d2c43a309f47ace8c95d4b9e8d422b4a448286 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Thu, 14 May 2026 12:51:54 +0100 Subject: [PATCH 16/38] [WIP]: seems that the most issues are resolved and now I need to implement executor comms. --- mpi-test.c | 22 ++-- src/host-device/mpi_comms.c | 240 ++++++++++++++++++++++++++---------- src/host-device/mpi_comms.h | 23 ++-- 3 files changed, 199 insertions(+), 86 deletions(-) diff --git a/mpi-test.c b/mpi-test.c index 4d8edcd..b6670b5 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -52,17 +52,17 @@ int main() { cstate cr[NMEASURE * NSHOTS]; init_creg(NMEASURE * NSHOTS, -1, cr); register_qkern(test_qkern); - - cq_exec eh; - // a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); - // wait_qrun(&eh); - // s_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE); - sm_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS); - // am_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS, &eh); - // halt_qrun(&eh); - // wait_qrun(&eh); - - free_qureg(&qr); + // + // cq_exec eh; + // // a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); + // // wait_qrun(&eh); + // // s_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE); + // sm_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS); + // // am_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS, &eh); + // // halt_qrun(&eh); + // // wait_qrun(&eh); + // + // free_qureg(&qr); cq_finalise(1); return 0; } diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 8bf19f3..5eee852 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -4,8 +4,10 @@ #include "src/host-device/comms.h" #include "src/host/opcodes.h" +#include #include +#include #include #include #include @@ -20,13 +22,16 @@ static MPI_Comm CQ_MPI_SPLIT_COMM; struct cq_mpi_env { int rank; + int subcomm_rank; unsigned int verbosity; }; static cq_exec* executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; static size_t global_exec_id_counter = -1; static size_t num_active_executors = 0; -static struct cq_mpi_env mpi_env = {.rank = -1, .verbosity = 0}; +static struct cq_mpi_env mpi_env = {.rank = -1, + .subcomm_rank = -1, + .verbosity = 0}; struct communicator { bool comm_busy; @@ -37,6 +42,8 @@ struct communicator { static struct communicator dev_comm = {0}; +static char worker_name[64] = {0}; + static void cq_log(const char* format, ...) { #ifdef CQ_MPI_IMPL_DEBUG va_list(args); @@ -46,41 +53,39 @@ static void cq_log(const char* format, ...) { } void init_host_device_mpi(const unsigned int VERBOSITY) { + assert(sizeof(enum ctrl_code) == sizeof(int)); mpi_env.verbosity = VERBOSITY; if (VERBOSITY > 0) { cq_log("Initialising MPI.\n"); } - int nprocs, quest_nprocs, world_rank, quest_rank; - MPI_Comm comm_split; + int nprocs; MPI_Init(NULL, NULL); MPI_Comm_size(CQ_MPI_COMM_WORLD, &nprocs); - // TODO: check nproc - 2 (host + device) == power of 2 + // TODO: check nproc - 1 == power of 2 + // TODO: for multi-device check that: + // (nproc - 1) == n_device * power of 2 - MPI_Comm_rank(CQ_MPI_COMM_WORLD, &world_rank); MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); - const int QUANTUM_WORKER = world_rank > 1; - // MPI_Comm_split(MPI_COMM_WORLD, QUANTUM_WORKER, world_rank, &comm_split); - // MPI_Comm_dup(comm_split, &CQ_MPI_COMM_WORLD); - // MPI_Comm_free(&comm_split); +#if CQ_CONF_QUEST_WITH_MPI + const int QUANTUM_WORKER = mpi_env.rank > 0; + MPI_Barrier(CQ_MPI_COMM_WORLD); + MPI_Comm_split(CQ_MPI_COMM_WORLD, QUANTUM_WORKER, mpi_env.rank, + &CQ_MPI_SPLIT_COMM); + MPI_Comm_rank(CQ_MPI_SPLIT_COMM, &mpi_env.subcomm_rank); + cq_log("%s in subcomm I'm rank %d\n", get_comm_source(), + mpi_env.subcomm_rank); - // MPI_Comm_split(CQ_MPI_COMM_WORLD_WORLD, QUANTUM_WORKER, world_rank, - // &CQ_MPI_COMM_WORLD); MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); - // MPI_Comm_size(CQ_MPI_COMM_WORLD, &quest_nprocs); - // cq_log("%s there are %d processes in quest comm\n", get_comm_source(), - // quest_nprocs); + sprintf(worker_name, "Quantum Worker [%d]:\t\t", mpi_env.subcomm_rank); +#endif if (VERBOSITY > 0) { cq_log("Initialised MPI.\n"); } - // if (is_quantum_worker()) { - // return; - // } - if (mpi_env.rank != CQ_MPI_HOST_RANK) { device_init_comms(VERBOSITY); for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { @@ -91,11 +96,13 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { void finalise_host_device_mpi(const unsigned int VERBOSITY) { if (mpi_env.rank != CQ_MPI_HOST_RANK) { + MPI_Barrier(CQ_MPI_SPLIT_COMM); device_finalise_comms(VERBOSITY); } if (VERBOSITY > 0) { cq_log("%s Finalising MPI.\n", get_comm_source()); } + MPI_Barrier(CQ_MPI_COMM_WORLD); MPI_Finalize(); if (VERBOSITY > 0) { cq_log("%s Finalised MPI.\n", get_comm_source()); @@ -108,7 +115,9 @@ void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params) { const int device_rank = CQ_MPI_DEVICE_RANK; cq_log("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), op_to_str(OP)); - MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD); + int op_comm_buffer = (int)OP; + MPI_Ssend(&op_comm_buffer, 1, MPI_INT, device_rank, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); // send params based on OP host_comm_params(OP, params); cq_log("%s [send_ctrl_op]: sent %s\n", get_comm_source(), op_to_str(OP)); @@ -120,7 +129,7 @@ void mpi_host_wait_all_ops(void) { size_t num_ops; const int device_rank = CQ_MPI_DEVICE_RANK; MPI_Status status; - MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_MPI_COMMS_TAG, + MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_MPI_WORLD_COMMS_TAG, CQ_MPI_COMM_WORLD, &status); cq_log("%s [wait_all_ops]: all ops completed (num_ops: %zu)\n", get_comm_source(), num_ops); @@ -128,6 +137,7 @@ void mpi_host_wait_all_ops(void) { void host_device_final_sync(void) { if (mpi_env.rank != CQ_MPI_HOST_RANK) { + cq_log("%s [final_sync]: simply waiting\n", get_comm_source()); device_wait_comms(); } } @@ -212,16 +222,49 @@ void* device_listen(void* args) { pthread_mutex_unlock(&dev_comm.comm_lock); enum ctrl_code OP; - // device master (rank 0) gets from host from different comm - const int host_rank = CQ_MPI_HOST_RANK; - MPI_Status status; + int op_comm_buffer; + cq_log("%s [device_listen]: receiving OP...\n", get_comm_source()); - MPI_Recv(&OP, 1, MPI_INT, host_rank, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD, - &status); +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from different comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { +#endif + + const int host_rank = CQ_MPI_HOST_RANK; + MPI_Status status; + MPI_Recv(&op_comm_buffer, 1, MPI_INT, host_rank, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); +#if CQ_CONF_QUEST_WITH_MPI + // rank 0 brodcast to rest of q-workers then all do the dispatch + } + cq_log("%s [device_listen]: Starting Bcast\n", get_comm_source()); + // NOTE: doing custom bcast because I have MPI errors on my machine + // when calling MPICH Bcast! + MPI_Barrier(CQ_MPI_SPLIT_COMM); + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + int subcomm_size = 0; + MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); + + for (size_t i = 1; i < subcomm_size; ++i) { + MPI_Ssend(&op_comm_buffer, 1, MPI_INT, i, CQ_MPI_SUBCOMMS_TAG, + CQ_MPI_SPLIT_COMM); + } + } else { + MPI_Status status; + MPI_Recv(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); + } + MPI_Barrier(CQ_MPI_SPLIT_COMM); + + // MPI_Bcast(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, + // CQ_MPI_SPLIT_COMM); + cq_log("%s [device_listen]: Finished Bcast\n", get_comm_source()); +#endif + + OP = (enum ctrl_code)op_comm_buffer; cq_log("%s [device_listen]: received %s\n", get_comm_source(), op_to_str(OP)); - // rank 0 brodcast to rest of q-workers then all do the dispatch device_dispatch_ctrl_op(OP); pthread_mutex_lock(&dev_comm.comm_lock); @@ -232,6 +275,14 @@ void* device_listen(void* args) { cq_log("%s closing connection.\n", get_comm_source()); finalise_host_device_mpi(mpi_env.verbosity); + + pthread_join(dev_ctrl.device_thread, NULL); + dev_ctrl.device_busy = false; + pthread_cond_destroy(&dev_ctrl.cond_device_busy); + pthread_cond_destroy(&dev_ctrl.cond_queue_empty); + pthread_cond_destroy(&dev_ctrl.cond_queue_full); + pthread_mutex_destroy(&dev_ctrl.device_lock); + return NULL; } @@ -243,6 +294,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { case CQ_CTRL_INIT: { initialise_device(mpi_env.verbosity); insert_op(OP, &mpi_env.verbosity); + device_wait_all_ops(); break; } case CQ_CTRL_FINALISE: { @@ -257,7 +309,14 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { get_comm_source(), i); } } - finalise_device(mpi_env.verbosity); + + insert_op(OP, &mpi_env.verbosity); + device_wait_all_ops(); + pthread_mutex_lock(&dev_ctrl.device_lock); + dev_ctrl.run_device = false; + pthread_mutex_unlock(&dev_ctrl.device_lock); + + // finalise_device(mpi_env.verbosity); break; } case CQ_CTRL_ALLOC: { @@ -269,6 +328,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { device_alloc_params params = {0}; recv_alloc_params(¶ms, host_rank); insert_op(OP, ¶ms); + cq_log("%s [dispatch][ALLOC]: inserted op\n", get_comm_source()); device_wait_all_ops(); send_alloc_params(¶ms, host_rank); break; @@ -369,13 +429,21 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { case CQ_CTRL_WAIT: { // wait for worker and send info to the host // (which is blocked and waiting). + cq_log("%s [dispatch][WAIT]: Started waiting\n", get_comm_source()); size_t num_ops = device_wait_all_ops(); - const int host_rank = CQ_MPI_HOST_RANK; - cq_log("%s [dispatch]: sending num ops: %zu...\n", get_comm_source(), - num_ops); - MPI_Ssend(&num_ops, 1, MPI_UINT64_T, host_rank, CQ_MPI_COMMS_TAG, - CQ_MPI_COMM_WORLD); - cq_log("%s [dispatch]: sent num ops.\n", get_comm_source()); + cq_log("%s [dispatch][WAIT]: Finished waiting\n", get_comm_source()); +#if CQ_CONF_QUEST_WITH_MPI + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { +#endif + const int host_rank = CQ_MPI_HOST_RANK; + cq_log("%s [dispatch]: sending num ops: %zu...\n", get_comm_source(), + num_ops); + MPI_Ssend(&num_ops, 1, MPI_UINT64_T, host_rank, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); + cq_log("%s [dispatch]: sent num ops.\n", get_comm_source()); +#if CQ_CONF_QUEST_WITH_MPI + } +#endif break; } case CQ_CTRL_ABORT: { @@ -411,9 +479,13 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // on either MPI or pthread implementation use this in different ctx. size_t device_wait_all_ops(void) { pthread_mutex_lock(&dev_ctrl.device_lock); + cq_log("%s [device_wait_all_ops]: Entering loop, num ops: %d\n", + get_comm_source(), dev_ctrl.num_ops); while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); } + cq_log("%s [device_wait_all_ops]: Exited loop, num ops: %d\n", + get_comm_source(), dev_ctrl.num_ops); pthread_mutex_unlock(&dev_ctrl.device_lock); return dev_ctrl.num_ops; @@ -477,6 +549,7 @@ void comms_exec_halt(cq_exec* const ehp) { // ---------------------------------------------------------------------------- void host_comm_params(const enum ctrl_code OP, void* params) { + const int device_rank = CQ_MPI_DEVICE_RANK; switch (OP) { case CQ_CTRL_INIT: { break; @@ -485,36 +558,30 @@ void host_comm_params(const enum ctrl_code OP, void* params) { break; } case CQ_CTRL_ALLOC: { - const int device_rank = CQ_MPI_DEVICE_RANK; send_alloc_params(params, device_rank); recv_alloc_params(params, device_rank); break; } case CQ_CTRL_DEALLOC: { - const int device_rank = CQ_MPI_DEVICE_RANK; send_alloc_params(params, device_rank); recv_alloc_params(params, device_rank); break; } case CQ_CTRL_RUN_QKERNEL: { - const int device_rank = CQ_MPI_DEVICE_RANK; send_exec_params(params, device_rank); break; } case CQ_CTRL_WAIT_EXEC: { - const int device_rank = CQ_MPI_DEVICE_RANK; send_exec_id(((cq_exec*)params)->id, device_rank); recv_exec_params(¶ms, device_rank); break; } case CQ_CTRL_SYNC_EXEC: { - const int device_rank = CQ_MPI_DEVICE_RANK; send_exec_id(((cq_exec*)params)->id, device_rank); recv_exec_params(¶ms, device_rank); break; } case CQ_CTRL_ABORT: { - const int device_rank = CQ_MPI_DEVICE_RANK; send_exec_id(((cq_exec*)params)->id, device_rank); break; } @@ -530,27 +597,66 @@ void recv_alloc_params(device_alloc_params* params, int src) { const size_t params_size = sizeof(device_alloc_params); MPI_Status status; - MPI_Recv(params, params_size, MPI_BYTE, src, CQ_MPI_COMMS_TAG, - CQ_MPI_COMM_WORLD, &status); +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { +#endif + + MPI_Recv(params, params_size, MPI_BYTE, src, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); + +#if CQ_CONF_QUEST_WITH_MPI + // rank 0 brodcast params to rest of q-workers + } + if (mpi_env.rank != 0) { + // NOTE: doing custom bcast because I have MPI errors on my machine + // when calling MPICH Bcast! + MPI_Barrier(CQ_MPI_SPLIT_COMM); + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + int subcomm_size = 0; + MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); + + for (size_t i = 1; i < subcomm_size; ++i) { + MPI_Ssend(params, params_size, MPI_BYTE, i, CQ_MPI_SUBCOMMS_TAG, + CQ_MPI_SPLIT_COMM); + } + } else { + MPI_Recv(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); + } + // MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, + // CQ_MPI_SPLIT_COMM); + } +#endif cq_log("%s [recv_alloc_params]: received.\n", get_comm_source()); print_alloc_params(params); } void send_alloc_params(const device_alloc_params* params, int dest) { - cq_log("%s [send_alloc_params]: sending...\n", get_comm_source()); - const size_t params_size = sizeof(device_alloc_params); - print_alloc_params(params); - MPI_Ssend((char*)params, params_size, MPI_BYTE, dest, CQ_MPI_COMMS_TAG, - CQ_MPI_COMM_WORLD); - cq_log("%s [send_alloc_params]: sent.\n", get_comm_source()); +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + cq_log("%s [send_alloc_params]: sending...\n", get_comm_source()); + const size_t params_size = sizeof(device_alloc_params); + print_alloc_params(params); + MPI_Ssend(params, params_size, MPI_BYTE, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); + cq_log("%s [send_alloc_params]: sent.\n", get_comm_source()); + +#if CQ_CONF_QUEST_WITH_MPI + } +#endif } size_t recv_exec_id(const int src) { cq_log("%s [recv_exec_id]: receiving...\n", get_comm_source()); size_t id = -1; MPI_Status status; - MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD, + MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_WORLD_COMMS_TAG, CQ_MPI_COMM_WORLD, &status); cq_log("%s [recv_exec_id]: received id: %zu\n", get_comm_source(), id); return id; @@ -558,7 +664,8 @@ size_t recv_exec_id(const int src) { void send_exec_id(const size_t id, const int dest) { cq_log("%s [send_exec_id]: sending id: %zu...\n", get_comm_source(), id); - MPI_Ssend(&id, 1, MPI_UINT64_T, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD); + MPI_Ssend(&id, 1, MPI_UINT64_T, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); cq_log("%s [send_exec_id]: sent\n", get_comm_source()); } @@ -567,8 +674,8 @@ void recv_exec_params(cq_exec** ehp, int src) { cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); int msg_size; MPI_Status status; - MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD, - &status); + MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); void* recv_buffer = malloc(msg_size); // reserve space for all the date + currently unused members @@ -583,7 +690,8 @@ void recv_exec_params(cq_exec** ehp, int src) { pthread_cond_init(&(*ehp)->cond_exec_complete, NULL); } else { cq_log( - "%s [recv_exec_params]: *ehp is already allocated. So I'm on host.\n", + "%s [recv_exec_params]: *ehp is already allocated. So I'm on " + "host.\n", get_comm_source()); } pthread_mutex_lock(&(*ehp)->lock); @@ -594,7 +702,7 @@ void recv_exec_params(cq_exec** ehp, int src) { exit(CQ_MPI_MALLOC_ERROR); } - MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_COMMS_TAG, + MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_WORLD_COMMS_TAG, CQ_MPI_COMM_WORLD, &status); const size_t bool_size = sizeof(bool); @@ -799,9 +907,10 @@ void send_exec_params(cq_exec* ehp, int dest) { // NOTE: ehp->prams left for another day - MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_COMMS_TAG, CQ_MPI_COMM_WORLD); + MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); - MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_COMMS_TAG, + MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_WORLD_COMMS_TAG, CQ_MPI_COMM_WORLD); free(send_buffer); @@ -859,12 +968,11 @@ void device_free_exec(cq_exec** ehp) { const char* get_comm_source(void) { if (mpi_env.rank == CQ_MPI_HOST_RANK) { - return "Host:\t\t"; - - } else if (is_quantum_worker()) { - return "Quantum Worker:\t\t"; + return "Host:\t\t\t\t"; } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { - return "Device:\t\t"; + return "Device:\t\t\t\t"; + } else if (is_quantum_worker()) { + return worker_name; } else { return ""; } @@ -946,7 +1054,8 @@ void print_ehp(const cq_exec* ehp) { "%s ehp details:\nid: %zu, exec_init: %d, complete: %d, halt: %d, " "STATUS: " "%d\nNQUBITS: " - "%zu, completed_shots: %zu, expected_shots: %zu, NMEASURE: %zu\nfname: " + "%zu, completed_shots: %zu, expected_shots: %zu, NMEASURE: " + "%zu\nfname: " "%s\nqreg:\n", get_comm_source(), ehp->id, ehp->exec_init, ehp->complete, ehp->halt, ehp->status, ehp->nqubits, ehp->completed_shots, ehp->expected_shots, @@ -972,14 +1081,15 @@ size_t assign_exec_id(void) { return global_exec_id_counter; } +int is_device(void) { + return mpi_env.rank != CQ_MPI_HOST_RANK; +} int is_quantum_worker(void) { - int world_rank; - MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); - return world_rank > 1; + return mpi_env.rank > 0; } MPI_Comm get_quest_comm(void) { - return CQ_MPI_COMM_WORLD; + return CQ_MPI_SPLIT_COMM; } void foo() { diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index 0838fcf..c361358 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -16,7 +16,8 @@ #define CQ_MPI_HOST_RANK 0 #define CQ_MPI_DEVICE_RANK 1 #define CQ_MPI_DEVICE_MASTER_RANK 0 -#define CQ_MPI_COMMS_TAG 0 +#define CQ_MPI_WORLD_COMMS_TAG 0 +#define CQ_MPI_SUBCOMMS_TAG 0 #define CQ_MPI_RUNTIME_ERROR -4 #define CQ_MPI_MALLOC_ERROR -5 @@ -26,15 +27,15 @@ // return CQ_SUCCESS; // } -#define RUN_HOST_ONLY() \ - { \ - int rank = get_rank(); \ - if (rank == -1) { \ - return CQ_ERROR; \ - } \ - if (get_rank() != CQ_MPI_HOST_RANK) { \ - return CQ_SUCCESS; \ - } \ +#define RUN_HOST_ONLY() \ + { \ + int rank = get_rank(); \ + if (rank == -1) { \ + return CQ_ERROR; \ + } \ + if (is_device()) { \ + return CQ_SUCCESS; \ + } \ } /// @@ -204,6 +205,8 @@ void print_ehp(const cq_exec* ehp); size_t assign_exec_id(void); +int is_device(void); + int is_quantum_worker(void); MPI_Comm get_quest_comm(void); From 03f88ce9a8f9d2f0296b186b5f965961ae9263b7 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Thu, 14 May 2026 13:14:54 +0100 Subject: [PATCH 17/38] Implemented full MPI-based CQ --- src/host-device/mpi_comms.c | 387 ++++++++++++++++++++++-------------- 1 file changed, 242 insertions(+), 145 deletions(-) diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 5eee852..7c19f16 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -599,16 +599,17 @@ void recv_alloc_params(device_alloc_params* params, int src) { #if CQ_CONF_QUEST_WITH_MPI // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { #endif MPI_Recv(params, params_size, MPI_BYTE, src, CQ_MPI_WORLD_COMMS_TAG, CQ_MPI_COMM_WORLD, &status); #if CQ_CONF_QUEST_WITH_MPI - // rank 0 brodcast params to rest of q-workers } - if (mpi_env.rank != 0) { + // device-rank 0 brodcast params to rest of q-workers + if (mpi_env.rank != CQ_MPI_HOST_RANK) { // NOTE: doing custom bcast because I have MPI errors on my machine // when calling MPICH Bcast! MPI_Barrier(CQ_MPI_SPLIT_COMM); @@ -624,6 +625,7 @@ void recv_alloc_params(device_alloc_params* params, int src) { MPI_Recv(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); } + MPI_Barrier(CQ_MPI_SPLIT_COMM); // MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, // CQ_MPI_SPLIT_COMM); } @@ -656,17 +658,60 @@ size_t recv_exec_id(const int src) { cq_log("%s [recv_exec_id]: receiving...\n", get_comm_source()); size_t id = -1; MPI_Status status; - MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_WORLD_COMMS_TAG, CQ_MPI_COMM_WORLD, - &status); + +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); + +#if CQ_CONF_QUEST_WITH_MPI + } + // device-rank 0 brodcast params to rest of q-workers + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + // NOTE: doing custom bcast because I have MPI errors on my machine + // when calling MPICH Bcast! + MPI_Barrier(CQ_MPI_SPLIT_COMM); + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + int subcomm_size = 0; + MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); + + for (size_t i = 1; i < subcomm_size; ++i) { + MPI_Ssend(&id, 1, MPI_UINT64_T, i, CQ_MPI_SUBCOMMS_TAG, + CQ_MPI_SPLIT_COMM); + } + } else { + MPI_Recv(&id, 1, MPI_UINT64_T, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); + } + MPI_Barrier(CQ_MPI_SPLIT_COMM); + // MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, + // CQ_MPI_SPLIT_COMM); + } +#endif + cq_log("%s [recv_exec_id]: received id: %zu\n", get_comm_source(), id); return id; } void send_exec_id(const size_t id, const int dest) { - cq_log("%s [send_exec_id]: sending id: %zu...\n", get_comm_source(), id); - MPI_Ssend(&id, 1, MPI_UINT64_T, dest, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD); - cq_log("%s [send_exec_id]: sent\n", get_comm_source()); +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + cq_log("%s [send_exec_id]: sending id: %zu...\n", get_comm_source(), id); + MPI_Ssend(&id, 1, MPI_UINT64_T, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); + cq_log("%s [send_exec_id]: sent\n", get_comm_source()); + +#if CQ_CONF_QUEST_WITH_MPI + } +#endif } void recv_exec_params(cq_exec** ehp, int src) { @@ -674,8 +719,27 @@ void recv_exec_params(cq_exec** ehp, int src) { cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); int msg_size; MPI_Status status; - MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD, &status); + +#if CQ_CONF_QUEST_WITH_MPI + // device-master (rank 0) gets from host from world comm + // or host gets from device-master + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); + +#if CQ_CONF_QUEST_WITH_MPI + } + // device-rank 0 brodcast params to rest of q-workers + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + MPI_Barrier(CQ_MPI_SPLIT_COMM); + MPI_Bcast(&msg_size, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SPLIT_COMM); + } +#endif + void* recv_buffer = malloc(msg_size); // reserve space for all the date + currently unused members @@ -702,8 +766,25 @@ void recv_exec_params(cq_exec** ehp, int src) { exit(CQ_MPI_MALLOC_ERROR); } - MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD, &status); +#if CQ_CONF_QUEST_WITH_MPI + // device-master (rank 0) gets from host from world comm + // or host gets from device-master + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); + +#if CQ_CONF_QUEST_WITH_MPI + } + // device-rank 0 brodcast params to rest of q-workers + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + MPI_Barrier(CQ_MPI_SPLIT_COMM); + MPI_Bcast(recv_buffer, msg_size, MPI_PACKED, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SPLIT_COMM); + } +#endif const size_t bool_size = sizeof(bool); const size_t cq_status_size = sizeof(cq_status); @@ -737,12 +818,13 @@ void recv_exec_params(cq_exec** ehp, int src) { // NOTE: creg_size * expected_shots?? or completed_shots? // host should get completed, device should get expected // and similarly when sending - size_t num_shots = 0; - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - num_shots = (*ehp)->completed_shots; - } else { - num_shots = (*ehp)->expected_shots; - } + // size_t num_shots = 0; + // if (mpi_env.rank == CQ_MPI_HOST_RANK) { + // num_shots = (*ehp)->completed_shots; + // } else { + // num_shots = (*ehp)->expected_shots; + // } + const size_t num_shots = (*ehp)->expected_shots; const size_t creg_size = sizeof(cstate) * (*ehp)->nmeasure * num_shots; // when on host the resources are already allocated! @@ -786,136 +868,151 @@ void recv_exec_params(cq_exec** ehp, int src) { } void send_exec_params(cq_exec* ehp, int dest) { - pthread_mutex_lock(&ehp->lock); - cq_log("%s [send_exec_params]: sending...\n", get_comm_source()); - print_ehp(ehp); - - const size_t bool_size = sizeof(bool); - const size_t cq_status_size = sizeof(cq_status); - int max_buffer_size = 0; - int member_size = 0; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &max_buffer_size); // id - MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // exec_init - max_buffer_size += member_size; - MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // complete - max_buffer_size += member_size; - MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // halt - max_buffer_size += member_size; - MPI_Pack_size(cq_status_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // status - max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // nqubits - max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // completed_shots - max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // expected_shots - max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // nmeasure - max_buffer_size += member_size; - - // ------------------------------------------------------------------------- - // TODO: sending pthred stuff doesn't sound like a good idea... - // sounds like UB - // const size_t pthread_mutex_size = sizeof(pthread_mutex_t); - // MPI_Pack_size(pthread_mutex_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - // &member_size); // lock - // max_buffer_size += member_size; - // - // const size_t pthread_cond_size = sizeof(pthread_cond_t); - // MPI_Pack_size(pthread_cond_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - // &member_size); // cond_exec_complete - // max_buffer_size += member_size; - // ------------------------------------------------------------------------- - - size_t fname_size = 0; - if (ehp->fname != NULL) { - fname_size = strlen(ehp->fname) + 1; - } - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // fname_size - max_buffer_size += member_size; - MPI_Pack_size(fname_size, MPI_CHAR, CQ_MPI_COMM_WORLD, - &member_size); // fname - max_buffer_size += member_size; - - const size_t qreg_size = sizeof(qubit) * ehp->nqubits; - MPI_Pack_size(qreg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // qreg - max_buffer_size += member_size; - - // NOTE: creg_size * expected_shots?? or completed_shots? - // host should send expected, device should send completed - size_t num_shots = 0; - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - num_shots = ehp->expected_shots; - } else { - num_shots = ehp->completed_shots; - } - const size_t creg_size = sizeof(cstate) * ehp->nmeasure * num_shots; +// if running with MPI QuEST, we don't need to communicate +// with quantum workers because the results from QuEST +// (e.g. measurements) should be already synchronised. +// so only device-master communicates its state to host. +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif - MPI_Pack_size(creg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // creg - max_buffer_size += member_size; + pthread_mutex_lock(&ehp->lock); + cq_log("%s [send_exec_params]: sending...\n", get_comm_source()); + print_ehp(ehp); + + const size_t bool_size = sizeof(bool); + const size_t cq_status_size = sizeof(cq_status); + int max_buffer_size = 0; + int member_size = 0; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &max_buffer_size); // id + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // exec_init + max_buffer_size += member_size; + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // complete + max_buffer_size += member_size; + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // halt + max_buffer_size += member_size; + MPI_Pack_size(cq_status_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // status + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // nqubits + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // completed_shots + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // expected_shots + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // nmeasure + max_buffer_size += member_size; + + // ------------------------------------------------------------------------- + // TODO: sending pthred stuff doesn't sound like a good idea... + // sounds like UB + // const size_t pthread_mutex_size = sizeof(pthread_mutex_t); + // MPI_Pack_size(pthread_mutex_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + // &member_size); // lock + // max_buffer_size += member_size; + // + // const size_t pthread_cond_size = sizeof(pthread_cond_t); + // MPI_Pack_size(pthread_cond_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + // &member_size); // cond_exec_complete + // max_buffer_size += member_size; + // ------------------------------------------------------------------------- + + size_t fname_size = 0; + if (ehp->fname != NULL) { + fname_size = strlen(ehp->fname) + 1; + } + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // fname_size + max_buffer_size += member_size; + MPI_Pack_size(fname_size, MPI_CHAR, CQ_MPI_COMM_WORLD, + &member_size); // fname + max_buffer_size += member_size; + + const size_t qreg_size = sizeof(qubit) * ehp->nqubits; + MPI_Pack_size(qreg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // qreg + max_buffer_size += member_size; + + // NOTE: creg_size * expected_shots?? or completed_shots? + // host should send expected, device should send completed + // size_t num_shots = 0; + // if (mpi_env.rank == CQ_MPI_HOST_RANK) { + // num_shots = ehp->expected_shots; + //} else { + // num_shots = ehp->completed_shots; + //} + const size_t num_shots = ehp->expected_shots; + const size_t creg_size = sizeof(cstate) * ehp->nmeasure * num_shots; + + MPI_Pack_size(creg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // creg + max_buffer_size += member_size; + + // NOTE: sending ehp->params left for another day... it's for pqkerns + + void* send_buffer = malloc(max_buffer_size); + if (send_buffer == NULL) { + cq_log( + "Failed to allocate buffer for sending executor handle. Exiting\n"); + exit(CQ_MPI_MALLOC_ERROR); + } + int position = 0; + MPI_Pack(&ehp->id, 1, MPI_UINT64_T, send_buffer, max_buffer_size, &position, + CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->exec_init, bool_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->complete, bool_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->halt, bool_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->status, cq_status_size, MPI_BYTE, send_buffer, + max_buffer_size, &position, CQ_MPI_COMM_WORLD); + + MPI_Pack(&ehp->nqubits, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->completed_shots, 1, MPI_UINT64_T, send_buffer, + max_buffer_size, &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->expected_shots, 1, MPI_UINT64_T, send_buffer, + max_buffer_size, &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->nmeasure, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + + // NOTE: skip pthread stuff... + + MPI_Pack(&fname_size, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(ehp->fname, fname_size, MPI_CHAR, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + + MPI_Pack(ehp->qreg, qreg_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(ehp->creg, creg_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + + // NOTE: ehp->prams left for another day + + MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); - // NOTE: sending ehp->params left for another day... it's for pqkerns + MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); - void* send_buffer = malloc(max_buffer_size); - if (send_buffer == NULL) { - cq_log("Failed to allocate buffer for sending executor handle. Exiting\n"); - exit(CQ_MPI_MALLOC_ERROR); + free(send_buffer); + cq_log("%s [send_exec_params]: sent.\n", get_comm_source()); + pthread_mutex_unlock(&ehp->lock); +#if CQ_CONF_QUEST_WITH_MPI } - int position = 0; - MPI_Pack(&ehp->id, 1, MPI_UINT64_T, send_buffer, max_buffer_size, &position, - CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->exec_init, bool_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->complete, bool_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->halt, bool_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->status, cq_status_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - - MPI_Pack(&ehp->nqubits, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->completed_shots, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->expected_shots, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->nmeasure, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - - // NOTE: skip pthread stuff... - - MPI_Pack(&fname_size, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(ehp->fname, fname_size, MPI_CHAR, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - - MPI_Pack(ehp->qreg, qreg_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(ehp->creg, creg_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - - // NOTE: ehp->prams left for another day - - MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD); - - MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD); - - free(send_buffer); - cq_log("%s [send_exec_params]: sent.\n", get_comm_source()); - pthread_mutex_unlock(&ehp->lock); +#endif } void device_free_exec(cq_exec** ehp) { From e6dd7fe973d0bb1c76a11ed06b0271f69004b267 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Fri, 15 May 2026 17:17:20 +0100 Subject: [PATCH 18/38] Restructured project and created comms module. --- CMakeLists.txt | 2 +- include/datatypes.h | 1 + mpi-test.c | 24 +- src/device/control.c | 8 +- src/host-device/CMakeLists.txt | 6 +- src/host-device/{comms.c => comms.c.OLD} | 8 +- src/host-device/comms.h | 53 +- src/host-device/comms.h.OLD | 56 ++ src/host-device/comms/CMakeLists.txt | 16 + src/host-device/comms/comms.c | 69 ++ src/host-device/comms/comms_core.c | 204 ++++ src/host-device/comms/comms_core.h | 27 + src/host-device/comms/comms_mpi.c | 1161 ++++++++++++++++++++++ src/host-device/comms/comms_mpi.h | 150 +++ src/host-device/comms/env.c.CHANGES | 70 ++ src/host-device/kernel_utils.c | 6 + src/host/host_ops.c | 17 +- src/host/opcodes.h | 7 +- 18 files changed, 1845 insertions(+), 40 deletions(-) rename src/host-device/{comms.c => comms.c.OLD} (98%) create mode 100644 src/host-device/comms.h.OLD create mode 100644 src/host-device/comms/CMakeLists.txt create mode 100644 src/host-device/comms/comms.c create mode 100644 src/host-device/comms/comms_core.c create mode 100644 src/host-device/comms/comms_core.h create mode 100644 src/host-device/comms/comms_mpi.c create mode 100644 src/host-device/comms/comms_mpi.h create mode 100644 src/host-device/comms/env.c.CHANGES diff --git a/CMakeLists.txt b/CMakeLists.txt index 31660fe..d69b6f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,7 @@ message(STATUS "BUILD_WITH_MPI turned ${BUILD_WITH_MPI}.") option( QUEST_WITH_MPI "Flag to define if QuEST was built with MPI. This will affect how we do the communication." - OFF + ON ) message(STATUS "QUEST_WITH_MPI turned ${QUEST_WITH_MPI}.") diff --git a/include/datatypes.h b/include/datatypes.h index d74886f..f44ed30 100644 --- a/include/datatypes.h +++ b/include/datatypes.h @@ -27,6 +27,7 @@ typedef struct qubit { } qubit; typedef struct exec { + size_t id; bool exec_init; bool complete; bool halt; diff --git a/mpi-test.c b/mpi-test.c index b6670b5..7807adf 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -5,7 +5,7 @@ #include "datatypes.h" #include "host_ops.h" #include "src/host-device/comms.h" -#include "src/host-device/mpi_comms.h" +// #include "src/host-device/mpi_comms.h" #include @@ -52,17 +52,17 @@ int main() { cstate cr[NMEASURE * NSHOTS]; init_creg(NMEASURE * NSHOTS, -1, cr); register_qkern(test_qkern); - // - // cq_exec eh; - // // a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); - // // wait_qrun(&eh); - // // s_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE); - // sm_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS); - // // am_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS, &eh); - // // halt_qrun(&eh); - // // wait_qrun(&eh); - // - // free_qureg(&qr); + + cq_exec eh; + // a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); + // wait_qrun(&eh); + // s_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE); + sm_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS); + // am_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS, &eh); + // halt_qrun(&eh); + // wait_qrun(&eh); + + free_qureg(&qr); cq_finalise(1); return 0; } diff --git a/src/device/control.c b/src/device/control.c index acd4628..02b384b 100644 --- a/src/device/control.c +++ b/src/device/control.c @@ -6,8 +6,10 @@ #include "utils.h" #include "kernel_utils.h" #include "resources.h" + #include "quest/include/environment.h" + cq_status (*control_registry[8])(void *) = { initialise_simulator, abort_current_kernel, @@ -30,7 +32,7 @@ cq_status initialise_simulator(void * par) { printf("Initialising QuEST.\n"); } - initQuESTEnv(); + init_quest_env(); if (*pVERBOSITY > 0) { reportQuESTEnv(); @@ -64,6 +66,10 @@ cq_status finalise_simulator(void * par) { finalizeQuESTEnv(); + if (*pVERBOSITY > 0) { + printf("QuEST finalised\n"); + } + // isQuESTEnvInit returns 1 for true, 0 for false if (!isQuESTEnvInit()) { status = CQ_SUCCESS; diff --git a/src/host-device/CMakeLists.txt b/src/host-device/CMakeLists.txt index d6c006e..e9fd43f 100644 --- a/src/host-device/CMakeLists.txt +++ b/src/host-device/CMakeLists.txt @@ -1,6 +1,8 @@ +add_subdirectory(comms) + target_sources(cq-simbe PRIVATE - comms.c +# comms.c kernel_utils.c - mpi_comms.c +# mpi_comms.c ) diff --git a/src/host-device/comms.c b/src/host-device/comms.c.OLD similarity index 98% rename from src/host-device/comms.c rename to src/host-device/comms.c.OLD index f81045e..49252aa 100644 --- a/src/host-device/comms.c +++ b/src/host-device/comms.c.OLD @@ -30,9 +30,9 @@ int initialise_device(const unsigned int VERBOSITY) { pthread_create(&dev_ctrl.device_thread, NULL, &device_control_thread, NULL); - unsigned int verbosity = VERBOSITY; - host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); - host_wait_all_ops(); + //unsigned int verbosity = VERBOSITY; + //host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); + //host_wait_all_ops(); return 0; } @@ -201,4 +201,4 @@ int finalise_device(const unsigned int VERBOSITY) { pthread_mutex_destroy(&dev_ctrl.device_lock); return 0; -} \ No newline at end of file +} diff --git a/src/host-device/comms.h b/src/host-device/comms.h index 537677d..f768901 100644 --- a/src/host-device/comms.h +++ b/src/host-device/comms.h @@ -1,13 +1,20 @@ -#ifndef HOST_DEVICE_COMMS_H -#define HOST_DEVICE_COMMS_H +#ifndef CQ_HOST_DEVICE_COMMS_H +#define CQ_HOST_DEVICE_COMMS_H -#include #include +#include #include "datatypes.h" #include "src/host/opcodes.h" #define __CQ_DEVICE_QUEUE_SIZE__ 16 +#define RUN_HOST_ONLY() \ + { \ + if (is_device()) { \ + return CQ_SUCCESS; \ + } \ + } + struct dev_link { bool run_device; pthread_t device_thread; @@ -15,7 +22,7 @@ struct dev_link { bool device_busy; pthread_cond_t cond_device_busy; - + size_t num_ops; pthread_cond_t cond_queue_empty; pthread_cond_t cond_queue_full; @@ -23,7 +30,7 @@ struct dev_link { size_t next_op_in; size_t next_op_out; enum ctrl_code op_buffer[__CQ_DEVICE_QUEUE_SIZE__]; - void * op_params_buffer[__CQ_DEVICE_QUEUE_SIZE__]; + void* op_params_buffer[__CQ_DEVICE_QUEUE_SIZE__]; }; typedef struct device_alloc_params { @@ -34,23 +41,39 @@ typedef struct device_alloc_params { extern struct dev_link dev_ctrl; +// TODO: pth as in original comms.c int initialise_device(const unsigned int VERBOSITY); -size_t host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params); +// TODO: pth wrapper around insert_op +size_t host_send_ctrl_op(const enum ctrl_code OP, void* ctrl_params); -size_t host_sync_exec(cq_exec * const ehp); +// TODO: pth wrapper around device_wait_all_ops +size_t host_wait_all_ops(void); -size_t host_wait_exec(cq_exec * const ehp); +size_t device_sync_exec(const cq_status STATUS, + const size_t SHOT, + cstate const* const RESULT, + cq_exec* ehp); -void host_request_halt(cq_exec * const ehp); +// TODO: Implement for pthreads +void host_device_sync_comms(void); -size_t host_wait_all_ops(); +// TODO: pth as in original comms.c +int finalise_device(const unsigned int VERBOSITY); -size_t device_sync_exec(const cq_status STATUS, const size_t SHOT, - cstate const * const RESULT, cq_exec * ehp); +/// generates and assigns id to the executor that is used for host-device +/// communication. +/// @return new executor id +size_t assign_exec_id(void); -void * device_control_thread(void *); +/// +/// check if MPI process is assigned to the device. +/// @return true if the MPI process is device process. false by default (when CQ +/// built without MPI) +bool is_device(void); -int finalise_device(const unsigned int VERBOSITY); +/// +/// initialises correct QuEST environment depending on the build opotions. +void init_quest_env(void); -#endif \ No newline at end of file +#endif diff --git a/src/host-device/comms.h.OLD b/src/host-device/comms.h.OLD new file mode 100644 index 0000000..537677d --- /dev/null +++ b/src/host-device/comms.h.OLD @@ -0,0 +1,56 @@ +#ifndef HOST_DEVICE_COMMS_H +#define HOST_DEVICE_COMMS_H + +#include +#include +#include "datatypes.h" +#include "src/host/opcodes.h" + +#define __CQ_DEVICE_QUEUE_SIZE__ 16 + +struct dev_link { + bool run_device; + pthread_t device_thread; + pthread_mutex_t device_lock; + + bool device_busy; + pthread_cond_t cond_device_busy; + + size_t num_ops; + pthread_cond_t cond_queue_empty; + pthread_cond_t cond_queue_full; + + size_t next_op_in; + size_t next_op_out; + enum ctrl_code op_buffer[__CQ_DEVICE_QUEUE_SIZE__]; + void * op_params_buffer[__CQ_DEVICE_QUEUE_SIZE__]; +}; + +typedef struct device_alloc_params { + const size_t NQUBITS; + size_t qregistry_idx; + cq_status status; +} device_alloc_params; + +extern struct dev_link dev_ctrl; + +int initialise_device(const unsigned int VERBOSITY); + +size_t host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params); + +size_t host_sync_exec(cq_exec * const ehp); + +size_t host_wait_exec(cq_exec * const ehp); + +void host_request_halt(cq_exec * const ehp); + +size_t host_wait_all_ops(); + +size_t device_sync_exec(const cq_status STATUS, const size_t SHOT, + cstate const * const RESULT, cq_exec * ehp); + +void * device_control_thread(void *); + +int finalise_device(const unsigned int VERBOSITY); + +#endif \ No newline at end of file diff --git a/src/host-device/comms/CMakeLists.txt b/src/host-device/comms/CMakeLists.txt new file mode 100644 index 0000000..60343b7 --- /dev/null +++ b/src/host-device/comms/CMakeLists.txt @@ -0,0 +1,16 @@ +target_sources(cq-simbe + PRIVATE + comms_core.c +) + +if (BUILD_WITH_MPI) + target_sources(cq-simbe + PRIVATE + comms_mpi.c + ) +else() + target_sources(cq-simbe + PRIVATE + comms.c + ) +endif() diff --git a/src/host-device/comms/comms.c b/src/host-device/comms/comms.c new file mode 100644 index 0000000..724e60c --- /dev/null +++ b/src/host-device/comms/comms.c @@ -0,0 +1,69 @@ +#include "src/host-device/comms.h" + +#include "comms_core.h" + +#include "quest/include/environment.h" + +#include + +int initialise_device(const unsigned int VERBOSITY) { + init_device_controls(VERBOSITY); + unsigned int verbosity = VERBOSITY; + host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); + host_wait_all_ops(); + return 0; +} + +size_t host_send_ctrl_op(const enum ctrl_code OP, void* ctrl_params) { + switch (OP) { + case CQ_CTRL_SYNC_EXEC: { + return comms_exec_sync(ctrl_params); + break; + } + case CQ_CTRL_WAIT_EXEC: { + return comms_exec_wait(ctrl_params); + break; + } + case CQ_CTRL_ABORT: { + comms_exec_halt(ctrl_params); + return 0; + break; + } + default: { + break; + } + } + return insert_op(OP, ctrl_params); +} + +size_t host_wait_all_ops(void) { + return device_wait_all_ops(); +} + +void host_device_sync_comms(void) {} + +int finalise_device(const unsigned int VERBOSITY) { + // Politely wait for the device to finish its current business + // otherwise setting dev_ctrl.run_device = false might break + // some stuff, and this should only be called in cq_finalise() + host_wait_all_ops(); + + if (VERBOSITY > 0) { + printf("Finalising device.\n"); + } + + stop_device(); + unsigned int verbosity = VERBOSITY; + host_send_ctrl_op(CQ_CTRL_FINALISE, &verbosity); + finalise_device_controls(VERBOSITY); + + return 0; +} + +bool is_device(void) { + return false; +} + +void init_quest_env(void) { + initQuESTEnv(); +} diff --git a/src/host-device/comms/comms_core.c b/src/host-device/comms/comms_core.c new file mode 100644 index 0000000..79960ee --- /dev/null +++ b/src/host-device/comms/comms_core.c @@ -0,0 +1,204 @@ +#include "comms_core.h" + +#include "../comms.h" +#include "src/device/control.h" + +#include +#include + +static size_t global_exec_id_counter = -1; + +int init_device_controls(const unsigned int VERBOSITY) { + if (VERBOSITY > 0) { + printf("Initialising device.\n"); + } + pthread_mutex_init(&dev_ctrl.device_lock, NULL); + pthread_cond_init(&dev_ctrl.cond_device_busy, NULL); + pthread_cond_init(&dev_ctrl.cond_queue_empty, NULL); + pthread_cond_init(&dev_ctrl.cond_queue_full, NULL); + + dev_ctrl.run_device = true; + dev_ctrl.device_busy = true; + dev_ctrl.num_ops = 0; + dev_ctrl.next_op_in = 0; + dev_ctrl.next_op_out = 0; + + for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { + dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; + dev_ctrl.op_params_buffer[i] = NULL; + } + + pthread_create(&dev_ctrl.device_thread, NULL, &device_control_thread, NULL); + + //unsigned int verbosity = VERBOSITY; + //host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); + //host_wait_all_ops(); + + return 0; + +} + +void stop_device(void) { + pthread_mutex_lock(&dev_ctrl.device_lock); + dev_ctrl.run_device = false; + pthread_mutex_unlock(&dev_ctrl.device_lock); +} + +int finalise_device_controls(const unsigned int VERBOSITY) { + pthread_join(dev_ctrl.device_thread, NULL); + + dev_ctrl.device_busy = false; + + pthread_cond_destroy(&dev_ctrl.cond_device_busy); + pthread_cond_destroy(&dev_ctrl.cond_queue_empty); + pthread_cond_destroy(&dev_ctrl.cond_queue_full); + pthread_mutex_destroy(&dev_ctrl.device_lock); + + return 0; +} + +// NOTE: aka host_send_ctrl_op from comm.c +// really what I want to use is host_send_ctrl_op implemenetation but it needs +// renaming and wrapping into function called host_send_ctrl_op and depending on +// either MPI or pthread implementation use this in different ctx. +size_t insert_op(const enum ctrl_code OP, void* ctrl_params) { + pthread_mutex_lock(&dev_ctrl.device_lock); + + while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { + // the control queue is full! + // we'll wait for it to not be full + pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); + } + + dev_ctrl.op_buffer[dev_ctrl.next_op_in] = OP; + dev_ctrl.op_params_buffer[dev_ctrl.next_op_in] = ctrl_params; + ++dev_ctrl.num_ops; + + // It's a ring buffer! + // advance next_op_in then mod out buffer size + ++dev_ctrl.next_op_in; + dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; + + pthread_cond_signal(&dev_ctrl.cond_queue_empty); + pthread_mutex_unlock(&dev_ctrl.device_lock); + + return dev_ctrl.num_ops; +} + +size_t comms_exec_sync(cq_exec* const ehp) { + size_t completed_shots = 0; + pthread_mutex_lock(&(ehp->lock)); + completed_shots = ehp->completed_shots; + pthread_mutex_unlock(&(ehp->lock)); + return completed_shots; +} + +size_t comms_exec_wait(cq_exec* const ehp) { + pthread_mutex_lock(&(ehp->lock)); + while (!ehp->complete) { + pthread_cond_wait(&(ehp->cond_exec_complete), &(ehp->lock)); + } + pthread_mutex_unlock(&(ehp->lock)); + return ehp->completed_shots; +} + +void comms_exec_halt(cq_exec* const ehp) { + pthread_mutex_lock(&(ehp->lock)); + ehp->halt = true; + pthread_mutex_unlock(&(ehp->lock)); + return; +} + +// NOTE: aka host_wait_all_ops from comm.c +// really what I want to use is host_wait_all_ops implementation but it needs +// renaming and wrapping into function called host_send_ctrl_op and depending +// on either MPI or pthread implementation use this in different ctx. +size_t device_wait_all_ops(void) { + pthread_mutex_lock(&dev_ctrl.device_lock); + while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { + pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); + } + + pthread_mutex_unlock(&dev_ctrl.device_lock); + return dev_ctrl.num_ops; +} + +void * device_control_thread(void * par) { + enum ctrl_code current_op = CQ_CTRL_IDLE; + void * current_op_params = NULL; + + // run_device set to FALSE at cq_finalise + while (dev_ctrl.run_device) { + pthread_mutex_lock(&dev_ctrl.device_lock); + + while(dev_ctrl.num_ops <= 0) { + // wait for a new op to be posted + dev_ctrl.device_busy = false; + pthread_cond_signal(&dev_ctrl.cond_device_busy); + pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); + } + + dev_ctrl.device_busy = true; + + // take the next op and params out of the dev_ctrl buffer, and then tidy up the dev_ctrl buffer + current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; + current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; + dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; + dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; + + // decrease the number of queued operations and advance next_op_out + --dev_ctrl.num_ops; + ++dev_ctrl.next_op_out; + dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; + + // signal that the queue is no longer full and then relinquish mutex + pthread_cond_signal(&dev_ctrl.cond_queue_full); + pthread_mutex_unlock(&dev_ctrl.device_lock); + + control_registry[current_op](current_op_params); + } + + pthread_mutex_unlock(&dev_ctrl.device_lock); + + return NULL; +} + +size_t device_sync_exec(const cq_status STATUS, const size_t SHOT, +cstate const * const RESULT, cq_exec * ehp) { + pthread_mutex_lock(&ehp->lock); + + if (STATUS == CQ_EARLY_SUCCESS) { + // generally speaking we should respect the kernel-provided + // status code, but CQ_EARLY_SUCCESS really means CQ_SUCCESS + // but needed to be != so we could break execution + ehp->status = CQ_SUCCESS; + } else { + ehp->status = STATUS; + } + + ehp->completed_shots += 1; + + // copy local result register to exec + cstate * dest_creg = ehp->creg + SHOT * ehp->nmeasure; + memcpy(dest_creg, RESULT, ehp->nmeasure * sizeof(cstate)); + + // check if the whole execution is done + if ( + ehp->completed_shots == ehp->expected_shots + || STATUS != CQ_SUCCESS + || ehp->halt + ) { + ehp->complete = true; + pthread_cond_signal(&(ehp->cond_exec_complete)); + } + + pthread_mutex_unlock(&ehp->lock); + + return SHOT; +} + +size_t assign_exec_id(void) { + ++global_exec_id_counter; + global_exec_id_counter %= __CQ_DEVICE_QUEUE_SIZE__; + return global_exec_id_counter; +} diff --git a/src/host-device/comms/comms_core.h b/src/host-device/comms/comms_core.h new file mode 100644 index 0000000..8eaaf77 --- /dev/null +++ b/src/host-device/comms/comms_core.h @@ -0,0 +1,27 @@ +#ifndef CQ_HOST_DEVICE_COMMS_CORE_H +#define CQ_HOST_DEVICE_COMMS_CORE_H + +#include "datatypes.h" +#include "src/host/opcodes.h" + +#include + +int init_device_controls(const unsigned int VERBOSITY); + +void stop_device(void); + +int finalise_device_controls(const unsigned int VERBOSITY); + +size_t insert_op(const enum ctrl_code OP, void* ctrl_params); + +size_t comms_exec_sync(cq_exec* const ehp); + +size_t comms_exec_wait(cq_exec* const ehp); + +void comms_exec_halt(cq_exec* const ehp); + +size_t device_wait_all_ops(void); + +void * device_control_thread(void *); + +#endif diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c new file mode 100644 index 0000000..0a5e3ed --- /dev/null +++ b/src/host-device/comms/comms_mpi.c @@ -0,0 +1,1161 @@ +#include "comms_mpi.h" +#include "comms_core.h" +#include "src/host-device/comms.h" + +#include "datatypes.h" +#include "src/host/opcodes.h" + +#include "quest/include/environment.h" +#include "quest/include/subcommunicator.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +// ---------------------------------------------------------------------------- +// Macros +// ---------------------------------------------------------------------------- +#define CQ_MPI_IMPL_DEBUG + +#define CQ_MPI_HOST_RANK 0 +#define CQ_MPI_DEVICE_RANK 1 +#define CQ_MPI_DEVICE_MASTER_RANK 0 +#define CQ_MPI_WORLD_COMMS_TAG 0 +#define CQ_MPI_SUBCOMMS_TAG 0 + +#define CQ_MPI_RUNTIME_ERROR -4 +#define CQ_MPI_MALLOC_ERROR -5 + +static const MPI_Comm CQ_MPI_COMM_WORLD = MPI_COMM_WORLD; +static MPI_Comm CQ_MPI_SPLIT_COMM; + +struct cq_mpi_env { + int rank; + int subcomm_rank; + unsigned int verbosity; +}; + +static cq_exec* executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; +static size_t num_active_executors = 0; +static struct cq_mpi_env mpi_env = {.rank = -1, + .subcomm_rank = -1, + .verbosity = 0}; + +struct communicator { + bool comm_busy; + pthread_t device_comm_thread; + pthread_cond_t cond_comm_busy; + pthread_mutex_t comm_lock; +}; + +static struct communicator dev_comm = {0}; + +static char worker_name[64] = {0}; + +static void cq_log(const char* format, ...) { +#ifdef CQ_MPI_IMPL_DEBUG + va_list(args); + va_start(args, format); + vprintf(format, args); +#endif +} + +int initialise_device(const unsigned int VERBOSITY) { + init_host_device_mpi(VERBOSITY); + RUN_HOST_ONLY(); + unsigned int verbosity = VERBOSITY; + host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); + host_wait_all_ops(); + return 0; +} + +size_t host_send_ctrl_op(const enum ctrl_code OP, void* params) { + const int device_rank = CQ_MPI_DEVICE_RANK; + cq_log("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), + op_to_str(OP)); + int op_comm_buffer = (int)OP; + MPI_Ssend(&op_comm_buffer, 1, MPI_INT, device_rank, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); + // send params based on OP + host_comm_params(OP, params); + cq_log("%s [send_ctrl_op]: sent %s\n", get_comm_source(), op_to_str(OP)); + return 0; +} + +size_t host_wait_all_ops(void) { + cq_log("%s [wait_all_ops]: waiting...\n", get_comm_source()); + host_send_ctrl_op(CQ_CTRL_WAIT, NULL); + size_t num_ops; + const int device_rank = CQ_MPI_DEVICE_RANK; + MPI_Status status; + MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); + cq_log("%s [wait_all_ops]: all ops completed (num_ops: %zu)\n", + get_comm_source(), num_ops); + return 0; +} + +void host_device_sync_comms(void) { + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + cq_log("%s [final_sync]: simply waiting\n", get_comm_source()); + device_wait_comms(); + } +} + +int finalise_device(const unsigned int VERBOSITY) { + host_device_sync_comms(); + // RUN_HOST_ONLY(); + + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + host_wait_all_ops(); + + if (VERBOSITY > 0) { + printf("Finalising device.\n"); + } + + unsigned int verbosity = VERBOSITY; + host_send_ctrl_op(CQ_CTRL_FINALISE, &verbosity); + } + + finalise_host_device_mpi(VERBOSITY); + + return 0; +} + +bool is_device(void) { + if (mpi_env.rank < 0) { + cq_log("%s [is_device]: rank is < 0 => MPI not initialised. Exiting!\n", + get_comm_source()); + exit(CQ_MPI_RUNTIME_ERROR); + } + + return mpi_env.rank != CQ_MPI_HOST_RANK; +} + +void init_quest_env(void) { +#if CQ_WITH_MPI_COMMS && CQ_CONF_QUEST_WITH_MPI + if (is_quantum_worker()) { + initCustomMpiCommQuESTEnv(get_quest_comm(), 0, 0); + } +#endif +#ifndef CQ_CONF_QUEST_WITH_MPI + initQuESTEnv(); +#endif +} + +// ---------------------------------------------------------------------------- +// Device Comm Ops +// ---------------------------------------------------------------------------- + +void init_host_device_mpi(const unsigned int VERBOSITY) { + mpi_env.verbosity = VERBOSITY; + if (VERBOSITY > 0) { + cq_log("Initialising MPI.\n"); + } + + int nprocs; + + MPI_Init(NULL, NULL); + + MPI_Comm_size(CQ_MPI_COMM_WORLD, &nprocs); + validate_nproc(nprocs); + // TODO: for multi-device check that: + // (nproc - 1) == n_device * power of 2 + + MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); + +#if CQ_CONF_QUEST_WITH_MPI + const int QUANTUM_WORKER = mpi_env.rank > 0; + MPI_Barrier(CQ_MPI_COMM_WORLD); + MPI_Comm_split(CQ_MPI_COMM_WORLD, QUANTUM_WORKER, mpi_env.rank, + &CQ_MPI_SPLIT_COMM); + MPI_Comm_rank(CQ_MPI_SPLIT_COMM, &mpi_env.subcomm_rank); + cq_log("%s in subcomm I'm rank %d\n", get_comm_source(), + mpi_env.subcomm_rank); + + sprintf(worker_name, "Quantum Worker [%d]:\t\t", mpi_env.subcomm_rank); +#endif + + if (VERBOSITY > 0) { + cq_log("Initialised MPI.\n"); + } + + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + device_init_comms(VERBOSITY); + for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { + executor_handles[i] = NULL; + } + } +} + +void finalise_host_device_mpi(const unsigned int VERBOSITY) { + // if (mpi_env.rank != CQ_MPI_HOST_RANK) { + // MPI_Barrier(CQ_MPI_SPLIT_COMM); + // device_finalise_comms(VERBOSITY); + // } + if (VERBOSITY > 0) { + cq_log("%s Finalising MPI.\n", get_comm_source()); + } + MPI_Barrier(CQ_MPI_COMM_WORLD); + MPI_Finalize(); + if (VERBOSITY > 0) { + cq_log("%s Finalised MPI.\n", get_comm_source()); + } +} + +void device_init_comms(const unsigned int VERBOSITY) { + if (VERBOSITY > 0) { + cq_log( + "%s [device_init_comms]: setting up on-device communication thread.\n", + get_comm_source()); + } + dev_ctrl.run_device = true; + dev_comm.comm_busy = true; + + pthread_mutex_init(&dev_comm.comm_lock, NULL); + pthread_cond_init(&dev_comm.cond_comm_busy, NULL); + pthread_create(&dev_comm.device_comm_thread, NULL, &device_listen, NULL); + + if (VERBOSITY > 0) { + cq_log("%s [device_init_comms]: on-device communication thread set up.\n", + get_comm_source()); + } +} + +void device_finalise_comms(const unsigned int VERBOSITY) { + if (VERBOSITY > 0) { + cq_log( + "%s [device_finalise_comms]: finalising on-device communication " + "thread.\n", + get_comm_source()); + } + + pthread_join(dev_comm.device_comm_thread, NULL); + dev_comm.comm_busy = false; + pthread_cond_destroy(&dev_comm.cond_comm_busy); + pthread_mutex_destroy(&dev_comm.comm_lock); + if (VERBOSITY > 0) { + cq_log( + "%s [device_finalise_comms]: on-device communication thread closed.\n", + get_comm_source()); + } +} + +void* device_listen(void* args) { + // run_device set to FALSE when OP == CQ_CTRL_FINALISE + cq_log("%s started listening...\n", get_comm_source()); + while (dev_ctrl.run_device) { + pthread_mutex_lock(&dev_comm.comm_lock); + dev_comm.comm_busy = true; + pthread_cond_signal(&dev_comm.cond_comm_busy); + pthread_mutex_unlock(&dev_comm.comm_lock); + + enum ctrl_code OP; + int op_comm_buffer; + + cq_log("%s [device_listen]: receiving OP...\n", get_comm_source()); +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from different comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { +#endif + + const int host_rank = CQ_MPI_HOST_RANK; + MPI_Status status; + MPI_Recv(&op_comm_buffer, 1, MPI_INT, host_rank, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); +#if CQ_CONF_QUEST_WITH_MPI + // rank 0 brodcast to rest of q-workers then all do the dispatch + } + cq_log("%s [device_listen]: Starting Bcast\n", get_comm_source()); + // NOTE: doing custom bcast because I have MPI errors on my machine + // when calling MPICH Bcast! + MPI_Barrier(CQ_MPI_SPLIT_COMM); + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + int subcomm_size = 0; + MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); + + for (size_t i = 1; i < subcomm_size; ++i) { + MPI_Ssend(&op_comm_buffer, 1, MPI_INT, i, CQ_MPI_SUBCOMMS_TAG, + CQ_MPI_SPLIT_COMM); + } + } else { + MPI_Status status; + MPI_Recv(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); + } + MPI_Barrier(CQ_MPI_SPLIT_COMM); + + // MPI_Bcast(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, + // CQ_MPI_SPLIT_COMM); + cq_log("%s [device_listen]: Finished Bcast\n", get_comm_source()); +#endif + + OP = (enum ctrl_code)op_comm_buffer; + cq_log("%s [device_listen]: received %s\n", get_comm_source(), + op_to_str(OP)); + + device_dispatch_ctrl_op(OP); + + pthread_mutex_lock(&dev_comm.comm_lock); + dev_comm.comm_busy = false; + pthread_cond_signal(&dev_comm.cond_comm_busy); + pthread_mutex_unlock(&dev_comm.comm_lock); + } + cq_log("%s closing connection.\n", get_comm_source()); + + // finalise_host_device_mpi(mpi_env.verbosity); + device_wait_all_ops(); + finalise_device_controls(mpi_env.verbosity); + + return NULL; +} + +// optionally get params and run op +// by running op I mean modyfing the internal dev_ctrl fields +// and then the worker thread handles the rest. +void device_dispatch_ctrl_op(const enum ctrl_code OP) { + switch (OP) { + case CQ_CTRL_INIT: { + // initialise_device(mpi_env.verbosity); + init_device_controls(mpi_env.verbosity); + insert_op(OP, &mpi_env.verbosity); + device_wait_all_ops(); + break; + } + case CQ_CTRL_FINALISE: { + // don't need to insert op into worker. + // we just wait until worker is done and cleanup. + device_wait_all_ops(); + for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { + if (executor_handles[i] != NULL) { + cq_log( + "%s [dispatch][FINALISE]: executor handle with id: %zu is still " + "active. Something went wrong!\n", + get_comm_source(), i); + } + } + + // this is like finalise_device in original comms.c + insert_op(OP, &mpi_env.verbosity); + device_wait_all_ops(); // this was uncommented + stop_device(); + + // finalise_device(mpi_env.verbosity); + break; + } + case CQ_CTRL_ALLOC: { + // Alloc is blocking: we get params, run allocation and + // send back the updated params. + // Also, because it's blocking I don't need to worry about + // params lifetime (from the worker perspective) + const int host_rank = CQ_MPI_HOST_RANK; + device_alloc_params params = {0}; + recv_alloc_params(¶ms, host_rank); + insert_op(OP, ¶ms); + cq_log("%s [dispatch][ALLOC]: inserted op\n", get_comm_source()); + device_wait_all_ops(); + send_alloc_params(¶ms, host_rank); + break; + } + case CQ_CTRL_DEALLOC: { + // Same as Alloc + const int host_rank = CQ_MPI_HOST_RANK; + device_alloc_params params = {0}; + recv_alloc_params(¶ms, host_rank); + insert_op(OP, ¶ms); + device_wait_all_ops(); + send_alloc_params(¶ms, host_rank); + break; + } + case CQ_CTRL_RUN_QKERNEL: { + if (num_active_executors >= __CQ_DEVICE_QUEUE_SIZE__) { + cq_log( + "%s [dispatch][RUN_QKERNEL]: You have oversubsribed the executor " + "queue. We allow up to %d " + "concurrent executors per device. Exiting", + get_comm_source(), __CQ_DEVICE_QUEUE_SIZE__); + exit(CQ_MPI_RUNTIME_ERROR); + } + const int host_rank = CQ_MPI_HOST_RANK; + cq_exec* tmp_exec = NULL; + recv_exec_params(&tmp_exec, host_rank); + // NOTE: this can be done here to free old one + // and allocate new one rather than in wait_exec + // device_free_exec(&executor_handles[tmp_exec->id]); + // actually if executor_handles[id] != NULL then it didn't finish and we + // should wait here until we are done. + // + // NOTE: 2 + // Now the question is should we block? + // 1. if we block here and wait for all ops to complete then + // in situation if host submits N_exe > QUEUE_SIZE, we lock here + // indefinitely + // ex: a_qrun(...) x QUEUE_SIZE + 1 (and no wait_qrun...) + // => deadlock + // 2. alternative would be wait to set exec_queue_full flag + // and wait until it is released + // 3. we can just exit and fail! -- most reasonable: + executor_handles[tmp_exec->id] = tmp_exec; + insert_op(OP, executor_handles[tmp_exec->id]); + ++num_active_executors; + // recv_exec_params(&executor_handles[executor_id], host_rank); + // insert_op(OP, executor_handles[executor_id]); + break; + } + case CQ_CTRL_RUN_PQKERNEL: { + // recv params + // insert_op + break; + } + case CQ_CTRL_WAIT_EXEC: { + const int host_rank = CQ_MPI_HOST_RANK; + const size_t executor_id = recv_exec_id(host_rank); + if (executor_handles[executor_id] == NULL) { + cq_log( + "%s [dispatch][WAIT_EXEC]: device ehp is NULL. Returning. " + "Exiting\n", + get_comm_source()); + exit(CQ_MPI_RUNTIME_ERROR); + } + // NOTE: This is commented out as it can cause a deadlock + // comms_exec_wait(executor_handles[executor_id]); + device_wait_all_ops(); + send_exec_params(executor_handles[executor_id], host_rank); + // NOTE: This can be done when allocating new in offload + // i.e. clear old one and allocate new one + device_free_exec(&executor_handles[executor_id]); + --num_active_executors; + if (num_active_executors < 0) { + cq_log( + "%s [dispatch][WAIT_EXEC]: The number of active executors is < 0! " + "Should not happen. Exiting", + get_comm_source()); + exit(CQ_MPI_RUNTIME_ERROR); + } + break; + } + case CQ_CTRL_SYNC_EXEC: { + const int host_rank = CQ_MPI_HOST_RANK; + const size_t executor_id = recv_exec_id(host_rank); + if (executor_handles[executor_id] == NULL) { + cq_log( + "%s [dispatch][SYNC_EXEC]: device ehp is NULL. Returning. " + "Exiting\n", + get_comm_source()); + exit(CQ_MPI_RUNTIME_ERROR); + } + comms_exec_sync(executor_handles[executor_id]); + cq_log("%s [dispatch][SYNC_EXEC]: executor synced\n", get_comm_source()); + send_exec_params(executor_handles[executor_id], host_rank); + cq_log("%s [dispatch][SYNC_EXEC]: sent executor\n", get_comm_source()); + break; + } + case CQ_CTRL_WAIT: { + // wait for worker and send info to the host + // (which is blocked and waiting). + cq_log("%s [dispatch][WAIT]: Started waiting\n", get_comm_source()); + size_t num_ops = device_wait_all_ops(); + cq_log("%s [dispatch][WAIT]: Finished waiting\n", get_comm_source()); +#if CQ_CONF_QUEST_WITH_MPI + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { +#endif + const int host_rank = CQ_MPI_HOST_RANK; + cq_log("%s [dispatch]: sending num ops: %zu...\n", get_comm_source(), + num_ops); + MPI_Ssend(&num_ops, 1, MPI_UINT64_T, host_rank, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); + cq_log("%s [dispatch]: sent num ops.\n", get_comm_source()); +#if CQ_CONF_QUEST_WITH_MPI + } +#endif + break; + } + case CQ_CTRL_ABORT: { + const int host_rank = CQ_MPI_HOST_RANK; + const size_t executor_id = recv_exec_id(host_rank); + if (executor_handles[executor_id] == NULL) { + cq_log( + "%s [dispatch][ABORT]: device ehp is NULL. Returning. " + "Exiting\n", + get_comm_source()); + exit(CQ_MPI_RUNTIME_ERROR); + } + comms_exec_halt(executor_handles[executor_id]); + cq_log("%s [dispatch][ABORT]: executor halted\n", get_comm_source()); + break; + } + case CQ_CTRL_IDLE: { + break; + } + case CQ_CTRL_TEST: { + cq_log("%s [dispatch][TEST]: called test op\n", get_comm_source()); + break; + } + default: { + break; + } + } +} + +void device_wait_comms(void) { + pthread_mutex_lock(&dev_comm.comm_lock); + while (dev_comm.comm_busy) { + pthread_cond_wait(&dev_comm.cond_comm_busy, &dev_comm.comm_lock); + } + pthread_mutex_unlock(&dev_comm.comm_lock); +} + +// ---------------------------------------------------------------------------- +// Device Control Paramaters Comms +// ---------------------------------------------------------------------------- + +void host_comm_params(const enum ctrl_code OP, void* params) { + const int device_rank = CQ_MPI_DEVICE_RANK; + switch (OP) { + case CQ_CTRL_INIT: { + break; + } + case CQ_CTRL_FINALISE: { + break; + } + case CQ_CTRL_ALLOC: { + send_alloc_params(params, device_rank); + recv_alloc_params(params, device_rank); + break; + } + case CQ_CTRL_DEALLOC: { + send_alloc_params(params, device_rank); + recv_alloc_params(params, device_rank); + break; + } + case CQ_CTRL_RUN_QKERNEL: { + send_exec_params(params, device_rank); + break; + } + case CQ_CTRL_WAIT_EXEC: { + send_exec_id(((cq_exec*)params)->id, device_rank); + recv_exec_params(¶ms, device_rank); + break; + } + case CQ_CTRL_SYNC_EXEC: { + send_exec_id(((cq_exec*)params)->id, device_rank); + recv_exec_params(¶ms, device_rank); + break; + } + case CQ_CTRL_ABORT: { + send_exec_id(((cq_exec*)params)->id, device_rank); + break; + } + default: { + break; + } + } +} + +void recv_alloc_params(device_alloc_params* params, int src) { + cq_log("%s [recv_alloc_params]: receiving...\n", get_comm_source()); + + const size_t params_size = sizeof(device_alloc_params); + MPI_Status status; + +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + MPI_Recv(params, params_size, MPI_BYTE, src, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); + +#if CQ_CONF_QUEST_WITH_MPI + } + // device-rank 0 brodcast params to rest of q-workers + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + // NOTE: doing custom bcast because I have MPI errors on my machine + // when calling MPICH Bcast! + MPI_Barrier(CQ_MPI_SPLIT_COMM); + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + int subcomm_size = 0; + MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); + + for (size_t i = 1; i < subcomm_size; ++i) { + MPI_Ssend(params, params_size, MPI_BYTE, i, CQ_MPI_SUBCOMMS_TAG, + CQ_MPI_SPLIT_COMM); + } + } else { + MPI_Recv(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); + } + MPI_Barrier(CQ_MPI_SPLIT_COMM); + // MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, + // CQ_MPI_SPLIT_COMM); + } +#endif + + cq_log("%s [recv_alloc_params]: received.\n", get_comm_source()); + print_alloc_params(params); +} + +void send_alloc_params(const device_alloc_params* params, int dest) { +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + cq_log("%s [send_alloc_params]: sending...\n", get_comm_source()); + const size_t params_size = sizeof(device_alloc_params); + print_alloc_params(params); + MPI_Ssend(params, params_size, MPI_BYTE, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); + cq_log("%s [send_alloc_params]: sent.\n", get_comm_source()); + +#if CQ_CONF_QUEST_WITH_MPI + } +#endif +} + +size_t recv_exec_id(const int src) { + cq_log("%s [recv_exec_id]: receiving...\n", get_comm_source()); + size_t id = -1; + MPI_Status status; + +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); + +#if CQ_CONF_QUEST_WITH_MPI + } + // device-rank 0 brodcast params to rest of q-workers + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + // NOTE: doing custom bcast because I have MPI errors on my machine + // when calling MPICH Bcast! + MPI_Barrier(CQ_MPI_SPLIT_COMM); + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + int subcomm_size = 0; + MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); + + for (size_t i = 1; i < subcomm_size; ++i) { + MPI_Ssend(&id, 1, MPI_UINT64_T, i, CQ_MPI_SUBCOMMS_TAG, + CQ_MPI_SPLIT_COMM); + } + } else { + MPI_Recv(&id, 1, MPI_UINT64_T, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); + } + MPI_Barrier(CQ_MPI_SPLIT_COMM); + // MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, + // CQ_MPI_SPLIT_COMM); + } +#endif + + cq_log("%s [recv_exec_id]: received id: %zu\n", get_comm_source(), id); + return id; +} + +void send_exec_id(const size_t id, const int dest) { +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + cq_log("%s [send_exec_id]: sending id: %zu...\n", get_comm_source(), id); + MPI_Ssend(&id, 1, MPI_UINT64_T, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); + cq_log("%s [send_exec_id]: sent\n", get_comm_source()); + +#if CQ_CONF_QUEST_WITH_MPI + } +#endif +} + +void recv_exec_params(cq_exec** ehp, int src) { + // TODO: do validation + cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); + int msg_size; + MPI_Status status; + +#if CQ_CONF_QUEST_WITH_MPI + // device-master (rank 0) gets from host from world comm + // or host gets from device-master + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); + +#if CQ_CONF_QUEST_WITH_MPI + } + // device-rank 0 brodcast params to rest of q-workers + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + MPI_Barrier(CQ_MPI_SPLIT_COMM); + MPI_Bcast(&msg_size, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SPLIT_COMM); + } +#endif + + void* recv_buffer = malloc(msg_size); + + // reserve space for all the date + currently unused members + if (*ehp == NULL) { + cq_log("%s [recv_exec_params]: *ehp is NULL. Allocating on device.\n", + get_comm_source()); + + // ehp = (cq_exec*)malloc(msg_size + sizeof(pthread_mutex_t) + + // sizeof(pthread_cond_t) + sizeof(void*)); + *ehp = (cq_exec*)malloc(sizeof(cq_exec)); + pthread_mutex_init(&(*ehp)->lock, NULL); + pthread_cond_init(&(*ehp)->cond_exec_complete, NULL); + } else { + cq_log( + "%s [recv_exec_params]: *ehp is already allocated. So I'm on " + "host.\n", + get_comm_source()); + } + pthread_mutex_lock(&(*ehp)->lock); + + if (recv_buffer == NULL || *ehp == NULL) { + cq_log("%s [recv_exec_params]: malloc failed. Exiting\n", + get_comm_source()); + exit(CQ_MPI_MALLOC_ERROR); + } + +#if CQ_CONF_QUEST_WITH_MPI + // device-master (rank 0) gets from host from world comm + // or host gets from device-master + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD, &status); + +#if CQ_CONF_QUEST_WITH_MPI + } + // device-rank 0 brodcast params to rest of q-workers + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + MPI_Barrier(CQ_MPI_SPLIT_COMM); + MPI_Bcast(recv_buffer, msg_size, MPI_PACKED, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SPLIT_COMM); + } +#endif + + const size_t bool_size = sizeof(bool); + const size_t cq_status_size = sizeof(cq_status); + // unpack + int position = 0; + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->id, 1, MPI_UINT64_T, + CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->exec_init, bool_size, + MPI_BYTE, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->complete, bool_size, + MPI_BYTE, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->halt, bool_size, + MPI_BYTE, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->status, cq_status_size, + MPI_BYTE, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->nqubits, 1, + MPI_UINT64_T, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->completed_shots, 1, + MPI_UINT64_T, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->expected_shots, 1, + MPI_UINT64_T, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->nmeasure, 1, + MPI_UINT64_T, CQ_MPI_COMM_WORLD); + + size_t fname_size = 0; + MPI_Unpack(recv_buffer, msg_size, &position, &fname_size, 1, MPI_UINT64_T, + CQ_MPI_COMM_WORLD); + fname_size *= sizeof(char); + + const size_t qreg_size = sizeof(qubit) * (*ehp)->nqubits; + // NOTE: creg_size * expected_shots?? or completed_shots? + // host should get completed, device should get expected + // and similarly when sending + // size_t num_shots = 0; + // if (mpi_env.rank == CQ_MPI_HOST_RANK) { + // num_shots = (*ehp)->completed_shots; + // } else { + // num_shots = (*ehp)->expected_shots; + // } + const size_t num_shots = (*ehp)->expected_shots; + const size_t creg_size = sizeof(cstate) * (*ehp)->nmeasure * num_shots; + + // when on host the resources are already allocated! + if (mpi_env.rank != CQ_MPI_HOST_RANK) { + (*ehp)->fname = (char*)malloc(fname_size); + if ((*ehp)->fname == NULL) { + cq_log("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", + get_comm_source()); + exit(CQ_MPI_MALLOC_ERROR); + } + + (*ehp)->qreg = (qubit*)malloc(qreg_size); + if ((*ehp)->qreg == NULL) { + cq_log("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", + get_comm_source()); + exit(CQ_MPI_MALLOC_ERROR); + } + + (*ehp)->creg = (cstate*)malloc(creg_size); + if ((*ehp)->creg == NULL) { + cq_log("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", + get_comm_source()); + exit(CQ_MPI_MALLOC_ERROR); + } + } + + MPI_Unpack(recv_buffer, msg_size, &position, (*ehp)->fname, fname_size, + MPI_CHAR, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->qreg, qreg_size, + MPI_BYTE, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->creg, creg_size, + MPI_BYTE, CQ_MPI_COMM_WORLD); + + // NOTE: recv ehp->params left for another day... it's for pqkerns + + free(recv_buffer); + + cq_log("%s [recv_exec_params]: received.\n", get_comm_source()); + print_ehp(*ehp); + pthread_mutex_unlock(&(*ehp)->lock); +} + +void send_exec_params(cq_exec* ehp, int dest) { +// if running with MPI QuEST, we don't need to communicate +// with quantum workers because the results from QuEST +// (e.g. measurements) should be already synchronised. +// so only device-master communicates its state to host. +#if CQ_CONF_QUEST_WITH_MPI + // device master (rank 0) gets from host from world comm + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || + mpi_env.rank == CQ_MPI_HOST_RANK) { +#endif + + pthread_mutex_lock(&ehp->lock); + cq_log("%s [send_exec_params]: sending...\n", get_comm_source()); + print_ehp(ehp); + + const size_t bool_size = sizeof(bool); + const size_t cq_status_size = sizeof(cq_status); + int max_buffer_size = 0; + int member_size = 0; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &max_buffer_size); // id + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // exec_init + max_buffer_size += member_size; + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // complete + max_buffer_size += member_size; + MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // halt + max_buffer_size += member_size; + MPI_Pack_size(cq_status_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // status + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // nqubits + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // completed_shots + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // expected_shots + max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // nmeasure + max_buffer_size += member_size; + + // ------------------------------------------------------------------------- + // TODO: sending pthred stuff doesn't sound like a good idea... + // sounds like UB + // const size_t pthread_mutex_size = sizeof(pthread_mutex_t); + // MPI_Pack_size(pthread_mutex_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + // &member_size); // lock + // max_buffer_size += member_size; + // + // const size_t pthread_cond_size = sizeof(pthread_cond_t); + // MPI_Pack_size(pthread_cond_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + // &member_size); // cond_exec_complete + // max_buffer_size += member_size; + // ------------------------------------------------------------------------- + + size_t fname_size = 0; + if (ehp->fname != NULL) { + fname_size = strlen(ehp->fname) + 1; + } + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, + &member_size); // fname_size + max_buffer_size += member_size; + MPI_Pack_size(fname_size, MPI_CHAR, CQ_MPI_COMM_WORLD, + &member_size); // fname + max_buffer_size += member_size; + + const size_t qreg_size = sizeof(qubit) * ehp->nqubits; + MPI_Pack_size(qreg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // qreg + max_buffer_size += member_size; + + // NOTE: creg_size * expected_shots?? or completed_shots? + // host should send expected, device should send completed + // size_t num_shots = 0; + // if (mpi_env.rank == CQ_MPI_HOST_RANK) { + // num_shots = ehp->expected_shots; + //} else { + // num_shots = ehp->completed_shots; + //} + const size_t num_shots = ehp->expected_shots; + const size_t creg_size = sizeof(cstate) * ehp->nmeasure * num_shots; + + MPI_Pack_size(creg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, + &member_size); // creg + max_buffer_size += member_size; + + // NOTE: sending ehp->params left for another day... it's for pqkerns + + void* send_buffer = malloc(max_buffer_size); + if (send_buffer == NULL) { + cq_log( + "Failed to allocate buffer for sending executor handle. Exiting\n"); + exit(CQ_MPI_MALLOC_ERROR); + } + int position = 0; + MPI_Pack(&ehp->id, 1, MPI_UINT64_T, send_buffer, max_buffer_size, &position, + CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->exec_init, bool_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->complete, bool_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->halt, bool_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->status, cq_status_size, MPI_BYTE, send_buffer, + max_buffer_size, &position, CQ_MPI_COMM_WORLD); + + MPI_Pack(&ehp->nqubits, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->completed_shots, 1, MPI_UINT64_T, send_buffer, + max_buffer_size, &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->expected_shots, 1, MPI_UINT64_T, send_buffer, + max_buffer_size, &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->nmeasure, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + + // NOTE: skip pthread stuff... + + MPI_Pack(&fname_size, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(ehp->fname, fname_size, MPI_CHAR, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + + MPI_Pack(ehp->qreg, qreg_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + MPI_Pack(ehp->creg, creg_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); + + // NOTE: ehp->prams left for another day + + MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); + + MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_WORLD_COMMS_TAG, + CQ_MPI_COMM_WORLD); + + free(send_buffer); + cq_log("%s [send_exec_params]: sent.\n", get_comm_source()); + pthread_mutex_unlock(&ehp->lock); +#if CQ_CONF_QUEST_WITH_MPI + } +#endif +} + +void device_free_exec(cq_exec** ehp) { + cq_log("%s [device_free_exec]: freeing the executor\n", get_comm_source()); + if (ehp == NULL) { + cq_log("%s [device_free_exec]: ehp is NULL\n", get_comm_source()); + return; + } + + if (*ehp == NULL) { + cq_log("%s [device_free_exec]: *ehp is NULL\n", get_comm_source()); + return; + } + + pthread_mutex_lock(&(*ehp)->lock); + if ((*ehp)->fname != NULL) { + free((*ehp)->fname); + (*ehp)->fname = NULL; + } + + if ((*ehp)->qreg != NULL) { + free((*ehp)->qreg); + (*ehp)->qreg = NULL; + } + + if ((*ehp)->creg != NULL) { + free((*ehp)->creg); + (*ehp)->creg = NULL; + } + + if ((*ehp)->params != NULL) { + free((*ehp)->params); + (*ehp)->params = NULL; + } + + pthread_mutex_unlock(&(*ehp)->lock); + pthread_mutex_destroy(&(*ehp)->lock); + pthread_cond_destroy(&(*ehp)->cond_exec_complete); + if ((*ehp) != NULL) { + free((*ehp)); + (*ehp) = NULL; + } + + cq_log("%s [device_free_exec]: freed the executor\n", get_comm_source()); +} + +// ---------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------- + +const char* get_comm_source(void) { + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + return "Host:\t\t\t\t"; + } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { + return "Device:\t\t\t\t"; + } else if (is_quantum_worker()) { + return worker_name; + } else { + return ""; + } +} + +const char* op_to_str(const enum ctrl_code OP) { + switch (OP) { + case CQ_CTRL_IDLE: { + return "CQ_CTRL_IDLE"; + break; + } + case CQ_CTRL_ALLOC: { + return "CQ_CTRL_ALLOC"; + break; + } + case CQ_CTRL_DEALLOC: { + return "CQ_CTRL_DEALLOC"; + break; + } + case CQ_CTRL_INIT: { + return "CQ_CTRL_INIT"; + break; + } + case CQ_CTRL_FINALISE: { + return "CQ_CTRL_FINALISE"; + break; + } + case CQ_CTRL_WAIT: { + return "CQ_CTRL_WAIT"; + break; + } + case CQ_CTRL_RUN_QKERNEL: { + return "CQ_CTRL_RUN_QKERNEL"; + break; + } + case CQ_CTRL_RUN_PQKERNEL: { + return "CQ_CTRL_RUN_PQKERNEL"; + break; + } + case CQ_CTRL_TEST: { + return "CQ_CTRL_TEST"; + break; + } + case CQ_CTRL_ABORT: { + return "CQ_CTRL_ABORT"; + break; + } + case CQ_CTRL_WAIT_EXEC: { + return "CQ_CTRL_WAIT_EXEC"; + break; + } + case CQ_CTRL_SYNC_EXEC: { + return "CQ_CTRL_SYNC_EXEC"; + break; + } + default: { + break; + } + } + return ""; +} + +void print_alloc_params(const device_alloc_params* params) { + cq_log("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", + get_comm_source(), params->NQUBITS, params->qregistry_idx, + params->status); +} + +void print_ehp(const cq_exec* ehp) { + if (ehp == NULL) { + cq_log("ehp is NULL\n"); + } + + cq_log( + "%s ehp details:\nid: %zu, exec_init: %d, complete: %d, halt: %d, " + "STATUS: " + "%d\nNQUBITS: " + "%zu, completed_shots: %zu, expected_shots: %zu, NMEASURE: " + "%zu\nfname: " + "%s\nqreg:\n", + get_comm_source(), ehp->id, ehp->exec_init, ehp->complete, ehp->halt, + ehp->status, ehp->nqubits, ehp->completed_shots, ehp->expected_shots, + ehp->nmeasure, ehp->fname); + + for (size_t i = 0; i < ehp->nqubits; ++i) { + cq_log("qubit[%zu]: reg_idx: %zu, offset: %zu, N: %zu\n", i, + ehp->qreg[i].registry_index, ehp->qreg[i].offset, ehp->qreg[i].N); + } + cq_log("\ncreg:\n"); + + for (size_t i = 0; i < ehp->nmeasure * ehp->expected_shots; ++i) { + cq_log("cstate[%zu]: %d\n", i, ehp->creg[i]); + } + cq_log("%s ehp details END\n\n", get_comm_source()); +} + +bool is_quantum_worker(void) { + return mpi_env.rank > 0; +} + +MPI_Comm get_quest_comm(void) { + return CQ_MPI_SPLIT_COMM; +} + +void validate_nproc(int nproc) { + const int device_nproc = nproc - 1; + if (!((device_nproc > 0) && ((device_nproc & (device_nproc - 1)) == 0))) { + cq_log( + "Incorrect number of MPI processes. The (N - 1) should be power of " + "2!\n"); + exit(CQ_MPI_RUNTIME_ERROR); + } +} + +#undef CQ_MPI_IMPL_DEBUG +#undef CQ_MPI_HOST_RANK +#undef CQ_MPI_DEVICE_RANK +#undef CQ_MPI_DEVICE_MASTER_RANK +#undef CQ_MPI_WORLD_COMMS_TAG +#undef CQ_MPI_SUBCOMMS_TAG +#undef CQ_MPI_RUNTIME_ERROR +#undef CQ_MPI_MALLOC_ERROR diff --git a/src/host-device/comms/comms_mpi.h b/src/host-device/comms/comms_mpi.h new file mode 100644 index 0000000..b46ae78 --- /dev/null +++ b/src/host-device/comms/comms_mpi.h @@ -0,0 +1,150 @@ +#ifndef CQ_HOST_DEVICE_COMMS_MPI_H +#define CQ_HOST_DEVICE_COMMS_MPI_H + +#include "datatypes.h" +#include "src/host-device/comms.h" +#include "src/host/opcodes.h" + +#include + +#include +#include + +/// +/// initialises MPI environment both on host and on the device. +/// @param VERBOSITY unsigned integer controlling diagnostic output. +void init_host_device_mpi(const unsigned int VERBOSITY); + +/// +/// finalises MPI environment both on host and on the device. +/// @param VERBOSITY unsigned integer controlling diagnostic output. +void finalise_host_device_mpi(const unsigned int VERBOSITY); + +// ---------------------------------------------------------------------------- +// Device Comm Ops +// ---------------------------------------------------------------------------- + +/// +/// initialises on-device communication thread responsible for MPI messaging +/// with host. +/// @param VERBOSITY unsigned integer controlling diagnostic output. +void device_init_comms(const unsigned int VERBOSITY); + +/// +/// finalises on-device communication thread responsible for MPI messaging +/// with host. +/// @param VERBOSITY unsigned integer controlling diagnostic output. +void device_finalise_comms(const unsigned int VERBOSITY); + +/// +/// starts listening for the incoming messages from the host. +/// @param args arbitrary arguments to function to satisfy pthread function +/// signature +/// @return arbitrary return data to satisfy pthread function signature. +/// Currently always returns NULL. +void* device_listen(void* args); + +/// +/// dispatches recieved control operation to the worker thread. +/// @param OP an enum argument specifying CQ_CTRL_OP +void device_dispatch_ctrl_op(const enum ctrl_code OP); + +/// +/// blocks device master thread and awaits for the comms to complete. +/// it needs to be called only once at the end to ensure that the main thread +/// does not clean-up and close MPI before work is done on the worker. +void device_wait_comms(void); + +// ---------------------------------------------------------------------------- +// Device Control Paramaters Comms +// ---------------------------------------------------------------------------- + +/// +/// communicates control parameters between host and device. +/// @param OP an enum argument specifying CQ_CTRL_OP +/// @param[in,out] params void pointer to arbitrary params +void host_comm_params(const enum ctrl_code OP, void* params); + +/// +/// receives allocation parameters from the source. +/// @param[out] params reference to parameters to store the results of +/// communication +/// @param src source rank of incoming message +void recv_alloc_params(device_alloc_params* params, int src); + +/// +/// sends allocation parameters to the destination. +/// @param[in] params reference to parameters to communicate +/// @param dest destination rank of outgoing message +void send_alloc_params(const device_alloc_params* params, int dest); + +size_t recv_exec_id(const int src); +void send_exec_id(const size_t id, const int dest); + +/// +/// receives executor handle from the source. Also, if called on device, +/// allocates executor in on-device memory, which then needs to be freed using +/// device_free_exec. +/// @param[out] ehp executor handle used for host-device offloading +/// @param src source rank of incoming message +void recv_exec_params(cq_exec** ehp, int src); + +/// sends updated executor handle to the destination. +/// @param[in] ehp executor handle used for host-device offloading +/// @param dest destination rank of outgoing message +void send_exec_params(cq_exec* ehp, int dest); + +/// +/// frees on-device memory pointed to by executor handle. +/// @param[in,out] ehp executor handle used for host-device offloading +void device_free_exec(cq_exec** ehp); + +// ---------------------------------------------------------------------------- +// Helpers +// ---------------------------------------------------------------------------- + +/// +/// returns the name of either host or the device. Used for printing +/// diagnostics. +/// @return name of the device (or host). +const char* get_comm_source(void); + +/// +/// returns the current MPI rank. +/// @return MPI rank of a given process. +int get_rank(void); + +/// +/// converts CQ control code to string. +/// @param OP an enum argument specifying CQ_CTRL_OP +/// @return string represntation of OP. +const char* op_to_str(const enum ctrl_code OP); + +/// +/// prints values of the alloc parameters. +/// @param[in] params reference to parameters +void print_alloc_params(const device_alloc_params* params); + +/// +/// prints members of the executor. +/// @param[in] ehp reference to executor +void print_ehp(const cq_exec* ehp); + +// +/// +/// check if MPI process is a quantum worker (not resposible for host-device +/// communication). +/// @return +bool is_quantum_worker(void); + +/// +/// accessor to the subcommunicator intended for QuEST simulation. +/// @return MPI Subcommunicator used to initialise QuEST environment +MPI_Comm get_quest_comm(void); + +/// validates the number of MPI processes to meet QuEST constraints. +/// Exits program if check failed. +/// @param nproc number of MPI processes +void validate_nproc(int nproc); + +#endif diff --git a/src/host-device/comms/env.c.CHANGES b/src/host-device/comms/env.c.CHANGES new file mode 100644 index 0000000..6f6210e --- /dev/null +++ b/src/host-device/comms/env.c.CHANGES @@ -0,0 +1,70 @@ +#include +#include +#include +#include "datatypes.h" +#include "src/host-device/comms.h" +#include "src/host-device/mpi_comms.h" +#include "opcodes.h" +#include "env.h" + +struct cq_environment cq_env = { + .initialised = false, + .finalised = false +}; + +struct dev_link dev_ctrl; + +cq_status cq_init(const unsigned int VERBOSITY) { + cq_status status = CQ_SUCCESS; + //init_host_device_mpi(VERBOSITY); + //RUN_HOST_ONLY(); + + if (!cq_env.finalised) { + if (!cq_env.initialised) { + if (VERBOSITY > 0) { + printf("Initialising CQ Simulated Backend library. QuEST environment report to follow.\n\n"); + } + + initialise_device(VERBOSITY); + RUN_HOST_ONLY(); + mpi_host_send_ctrl_op(CQ_CTRL_INIT, &VERBOSITY); + mpi_host_wait_all_ops(); + + cq_env.initialised = true; + } else { + if (VERBOSITY > 0) { + printf("CQ-SimBE is already initialised. No need to do it again.\n"); + } + status = CQ_WARNING; + } + } else { + printf("CQ-SimBE cannot be reinitialised once finalised! This would break QuEST.\n"); + status = CQ_ERROR; + } + + return status; +} + +cq_status cq_finalise(const unsigned int VERBOSITY) { + host_device_sync_comms(); + RUN_HOST_ONLY(); + cq_status status = CQ_SUCCESS; + + if (!cq_env.finalised) { + if (VERBOSITY > 0) { + printf("Host finalising\n"); + } + + mpi_host_wait_all_ops(); + mpi_host_send_ctrl_op(CQ_CTRL_FINALISE, &VERBOSITY); + + cq_env.finalised = true; + } else { + if (VERBOSITY > 0) { + printf("CQ-SimBE is already finalised. No need to do it again.\n"); + } + status = CQ_WARNING; + } + finalise_host_device_mpi(VERBOSITY); + return status; +} diff --git a/src/host-device/kernel_utils.c b/src/host-device/kernel_utils.c index 5afc0a7..c2b9562 100644 --- a/src/host-device/kernel_utils.c +++ b/src/host-device/kernel_utils.c @@ -4,6 +4,10 @@ #include #include "kernel_utils.h" +//#include "mpi_comms.h" +#include "src/host-device/comms.h" +#include + struct qkern_registry qk_reg; struct pqkern_registry pqk_reg; @@ -29,6 +33,7 @@ cq_status register_qkern(qkern kernel) { } } + host_device_sync_comms(); return status; } @@ -105,6 +110,7 @@ cq_status find_pqkern_name(pqkern const PQK, char ** fname) { } void init_exec_handle(const size_t NQUBITS, const size_t NSHOTS, const size_t NMEASURE, cq_exec * ehp) { + ehp->id = assign_exec_id(); ehp->exec_init = true; ehp->complete = false; ehp->halt = false; diff --git a/src/host/host_ops.c b/src/host/host_ops.c index 3cc98fb..39a9e0f 100644 --- a/src/host/host_ops.c +++ b/src/host/host_ops.c @@ -4,6 +4,8 @@ #include "kernel_utils.h" #include "src/host-device/comms.h" +//#include "src/host-device/mpi_comms.h" +#include // Resource Management cq_status alloc_qubit(qubit ** qhp) { @@ -11,6 +13,7 @@ cq_status alloc_qubit(qubit ** qhp) { } cq_status alloc_qureg(qubit ** qrp, size_t N) { + RUN_HOST_ONLY(); cq_status status = CQ_SUCCESS; // check qr is NULL @@ -61,6 +64,7 @@ cq_status free_qubit(qubit ** qhp) { } cq_status free_qureg(qubit ** qrp) { + RUN_HOST_ONLY(); if (*qrp == NULL) return CQ_WARNING; device_alloc_params dealloc_params = { @@ -84,6 +88,7 @@ cq_status free_qureg(qubit ** qrp) { cq_status s_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, cstate * crp, const size_t NMEASURE) { + RUN_HOST_ONLY(); cq_status status = CQ_ERROR; cq_exec exec_handle; @@ -102,6 +107,7 @@ cstate * crp, const size_t NMEASURE) { cq_status a_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, cstate * crp, const size_t NMEASURE, cq_exec * const ehp) { + RUN_HOST_ONLY(); cq_status status = CQ_ERROR; char * fname = NULL; @@ -148,6 +154,7 @@ cstate * const crp, const size_t NMEASURE, const size_t NSHOTS) { cq_status am_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, cstate * const crp, const size_t NMEASURE, const size_t NSHOTS, cq_exec * const ehp) { + RUN_HOST_ONLY(); cq_status status = CQ_ERROR; char * fname = NULL; @@ -182,16 +189,19 @@ cq_exec * const ehp) { cq_status sync_qrun(cq_exec * const ehp) { cq_status status = CQ_ERROR; if (ehp != NULL && ehp->exec_init) { - host_sync_exec(ehp); + //host_sync_exec(ehp); + host_send_ctrl_op(CQ_CTRL_SYNC_EXEC, ehp); status = CQ_SUCCESS; } return status; } cq_status wait_qrun(cq_exec * const ehp) { + RUN_HOST_ONLY(); cq_status status = CQ_ERROR; if (ehp != NULL && ehp->exec_init) { - host_wait_exec(ehp); + //host_wait_exec(ehp); + host_send_ctrl_op(CQ_CTRL_WAIT_EXEC, ehp); finalise_exec_handle(ehp); status = CQ_SUCCESS; } else if (ehp != NULL && ehp->expected_shots == 0) { @@ -204,7 +214,8 @@ cq_status wait_qrun(cq_exec * const ehp) { cq_status halt_qrun(cq_exec * const ehp) { cq_status status = CQ_ERROR; if (ehp != NULL && ehp->exec_init) { - host_request_halt(ehp); + //host_request_halt(ehp); + host_send_ctrl_op(CQ_CTRL_ABORT, ehp); status = wait_qrun(ehp); } return status; diff --git a/src/host/opcodes.h b/src/host/opcodes.h index b723065..c8965e5 100644 --- a/src/host/opcodes.h +++ b/src/host/opcodes.h @@ -10,7 +10,10 @@ enum ctrl_code { CQ_CTRL_DEALLOC, CQ_CTRL_RUN_QKERNEL, CQ_CTRL_RUN_PQKERNEL, - CQ_CTRL_TEST + CQ_CTRL_TEST, + CQ_CTRL_WAIT, // added in MPI extension + CQ_CTRL_WAIT_EXEC, // ditto + CQ_CTRL_SYNC_EXEC, // ditto }; -#endif \ No newline at end of file +#endif From a3f078f665e8af712fa0698b1a474eb41d5558a7 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Fri, 15 May 2026 17:18:01 +0100 Subject: [PATCH 19/38] Removed old files. --- src/host-device/comms.c.OLD | 204 ------------------------------------ src/host-device/comms.h.OLD | 56 ---------- 2 files changed, 260 deletions(-) delete mode 100644 src/host-device/comms.c.OLD delete mode 100644 src/host-device/comms.h.OLD diff --git a/src/host-device/comms.c.OLD b/src/host-device/comms.c.OLD deleted file mode 100644 index 49252aa..0000000 --- a/src/host-device/comms.c.OLD +++ /dev/null @@ -1,204 +0,0 @@ -#include -#include -#include -#include -#include "datatypes.h" -#include "kernel_utils.h" -#include "comms.h" -#include "src/host/opcodes.h" -#include "src/device/control.h" - -int initialise_device(const unsigned int VERBOSITY) { - if (VERBOSITY > 0) { - printf("Initialising device.\n"); - } - pthread_mutex_init(&dev_ctrl.device_lock, NULL); - pthread_cond_init(&dev_ctrl.cond_device_busy, NULL); - pthread_cond_init(&dev_ctrl.cond_queue_empty, NULL); - pthread_cond_init(&dev_ctrl.cond_queue_full, NULL); - - dev_ctrl.run_device = true; - dev_ctrl.device_busy = true; - dev_ctrl.num_ops = 0; - dev_ctrl.next_op_in = 0; - dev_ctrl.next_op_out = 0; - - for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { - dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; - dev_ctrl.op_params_buffer[i] = NULL; - } - - pthread_create(&dev_ctrl.device_thread, NULL, &device_control_thread, NULL); - - //unsigned int verbosity = VERBOSITY; - //host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); - //host_wait_all_ops(); - - return 0; -} - -size_t host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params) { - pthread_mutex_lock(&dev_ctrl.device_lock); - - while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { - // the control queue is full! - // we'll wait for it to not be full - pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); - } - - dev_ctrl.op_buffer[dev_ctrl.next_op_in] = OP; - dev_ctrl.op_params_buffer[dev_ctrl.next_op_in] = ctrl_params; - ++dev_ctrl.num_ops; - - // It's a ring buffer! - // advance next_op_in then mod out buffer size - ++dev_ctrl.next_op_in; - dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; - - pthread_cond_signal(&dev_ctrl.cond_queue_empty); - pthread_mutex_unlock(&dev_ctrl.device_lock); - - return dev_ctrl.num_ops; -} - -size_t host_sync_exec(cq_exec * const ehp) { - size_t completed_shots = 0; - pthread_mutex_lock(&(ehp->lock)); - completed_shots = ehp->completed_shots; - pthread_mutex_unlock(&(ehp->lock)); - return completed_shots; -} - -size_t host_wait_exec(cq_exec * const ehp) { - pthread_mutex_lock(&(ehp->lock)); - while (!ehp->complete) { - pthread_cond_wait( - &(ehp->cond_exec_complete), - &(ehp->lock) - ); - } - pthread_mutex_unlock(&(ehp->lock)); - return ehp->completed_shots; -} - -void host_request_halt(cq_exec * const ehp) { - pthread_mutex_lock(&(ehp->lock)); - ehp->halt = true; - pthread_mutex_unlock(&(ehp->lock)); - return; -} - -size_t host_wait_all_ops() { - pthread_mutex_lock(&dev_ctrl.device_lock); - while(dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { - pthread_cond_wait( - &dev_ctrl.cond_device_busy, - &dev_ctrl.device_lock - ); - } - pthread_mutex_unlock(&dev_ctrl.device_lock); - return dev_ctrl.num_ops; -} - -size_t device_sync_exec(const cq_status STATUS, const size_t SHOT, -cstate const * const RESULT, cq_exec * ehp) { - pthread_mutex_lock(&ehp->lock); - - if (STATUS == CQ_EARLY_SUCCESS) { - // generally speaking we should respect the kernel-provided - // status code, but CQ_EARLY_SUCCESS really means CQ_SUCCESS - // but needed to be != so we could break execution - ehp->status = CQ_SUCCESS; - } else { - ehp->status = STATUS; - } - - ehp->completed_shots += 1; - - // copy local result register to exec - cstate * dest_creg = ehp->creg + SHOT * ehp->nmeasure; - memcpy(dest_creg, RESULT, ehp->nmeasure * sizeof(cstate)); - - // check if the whole execution is done - if ( - ehp->completed_shots == ehp->expected_shots - || STATUS != CQ_SUCCESS - || ehp->halt - ) { - ehp->complete = true; - pthread_cond_signal(&(ehp->cond_exec_complete)); - } - - pthread_mutex_unlock(&ehp->lock); - - return SHOT; -} - -void * device_control_thread(void * par) { - enum ctrl_code current_op = CQ_CTRL_IDLE; - void * current_op_params = NULL; - - // run_device set to FALSE at cq_finalise - while (dev_ctrl.run_device) { - pthread_mutex_lock(&dev_ctrl.device_lock); - - while(dev_ctrl.num_ops <= 0) { - // wait for a new op to be posted - dev_ctrl.device_busy = false; - pthread_cond_signal(&dev_ctrl.cond_device_busy); - pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); - } - - dev_ctrl.device_busy = true; - - // take the next op and params out of the dev_ctrl buffer, and then tidy up the dev_ctrl buffer - current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; - current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; - dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; - dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; - - // decrease the number of queued operations and advance next_op_out - --dev_ctrl.num_ops; - ++dev_ctrl.next_op_out; - dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; - - // signal that the queue is no longer full and then relinquish mutex - pthread_cond_signal(&dev_ctrl.cond_queue_full); - pthread_mutex_unlock(&dev_ctrl.device_lock); - - control_registry[current_op](current_op_params); - } - - pthread_mutex_unlock(&dev_ctrl.device_lock); - - return NULL; -} - -int finalise_device(const unsigned int VERBOSITY) { - // Politely wait for the device to finish its current business - // otherwise setting dev_ctrl.run_device = false might break - // some stuff, and this should only be called in cq_finalise() - host_wait_all_ops(); - - if (VERBOSITY > 0) { - printf("Finalising device.\n"); - } - - pthread_mutex_lock(&dev_ctrl.device_lock); - dev_ctrl.run_device = false; - pthread_mutex_unlock(&dev_ctrl.device_lock); - - unsigned int verbosity = VERBOSITY; - host_send_ctrl_op(CQ_CTRL_FINALISE, &verbosity); - - pthread_join(dev_ctrl.device_thread, NULL); - - dev_ctrl.device_busy = false; - - pthread_cond_destroy(&dev_ctrl.cond_device_busy); - pthread_cond_destroy(&dev_ctrl.cond_queue_empty); - pthread_cond_destroy(&dev_ctrl.cond_queue_full); - pthread_mutex_destroy(&dev_ctrl.device_lock); - - return 0; -} diff --git a/src/host-device/comms.h.OLD b/src/host-device/comms.h.OLD deleted file mode 100644 index 537677d..0000000 --- a/src/host-device/comms.h.OLD +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef HOST_DEVICE_COMMS_H -#define HOST_DEVICE_COMMS_H - -#include -#include -#include "datatypes.h" -#include "src/host/opcodes.h" - -#define __CQ_DEVICE_QUEUE_SIZE__ 16 - -struct dev_link { - bool run_device; - pthread_t device_thread; - pthread_mutex_t device_lock; - - bool device_busy; - pthread_cond_t cond_device_busy; - - size_t num_ops; - pthread_cond_t cond_queue_empty; - pthread_cond_t cond_queue_full; - - size_t next_op_in; - size_t next_op_out; - enum ctrl_code op_buffer[__CQ_DEVICE_QUEUE_SIZE__]; - void * op_params_buffer[__CQ_DEVICE_QUEUE_SIZE__]; -}; - -typedef struct device_alloc_params { - const size_t NQUBITS; - size_t qregistry_idx; - cq_status status; -} device_alloc_params; - -extern struct dev_link dev_ctrl; - -int initialise_device(const unsigned int VERBOSITY); - -size_t host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params); - -size_t host_sync_exec(cq_exec * const ehp); - -size_t host_wait_exec(cq_exec * const ehp); - -void host_request_halt(cq_exec * const ehp); - -size_t host_wait_all_ops(); - -size_t device_sync_exec(const cq_status STATUS, const size_t SHOT, - cstate const * const RESULT, cq_exec * ehp); - -void * device_control_thread(void *); - -int finalise_device(const unsigned int VERBOSITY); - -#endif \ No newline at end of file From ed240b1ac480b6397c9e3d5a3b9fc9cc4be83890 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 18 May 2026 11:32:45 +0100 Subject: [PATCH 20/38] Cleaned up code, added extra process validation when built with MPI. --- .gitignore | 4 ++ CMakeLists.txt | 10 ++-- mpi-test.c | 17 ------- src/host-device/CMakeLists.txt | 2 - src/host-device/comms/CMakeLists.txt | 2 +- src/host-device/comms/comms_mpi.c | 26 ++++++++--- src/host-device/mpi_comms.c | 69 +++++++++++++++++++--------- src/host-device/mpi_comms.h | 59 ++++++++++-------------- 8 files changed, 101 insertions(+), 88 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c048201 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +build +build/* +.cache +.clang-format* diff --git a/CMakeLists.txt b/CMakeLists.txt index d69b6f2..c7b7646 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,16 +43,16 @@ option( option( BUILD_WITH_MPI "Build with MPI-based communication. Turned ON by default." - ON + OFF ) message(STATUS "BUILD_WITH_MPI turned ${BUILD_WITH_MPI}.") option( - QUEST_WITH_MPI + QUEST_BUILT_WITH_MPI "Flag to define if QuEST was built with MPI. This will affect how we do the communication." - ON + OFF ) -message(STATUS "QUEST_WITH_MPI turned ${QUEST_WITH_MPI}.") +message(STATUS "QUEST_BUILT_WITH_MPI turned ${QUEST_BUILT_WITH_MPI}.") # Dependencies @@ -70,7 +70,7 @@ if (BUILD_WITH_MPI) target_compile_definitions(cq-simbe PRIVATE CQ_WITH_MPI_COMMS=1) endif() -if (BUILD_WITH_MPI AND QUEST_WITH_MPI) +if (BUILD_WITH_MPI AND QUEST_BUILT_WITH_MPI) target_compile_definitions(cq-simbe PRIVATE CQ_CONF_QUEST_WITH_MPI=1) endif() diff --git a/mpi-test.c b/mpi-test.c index 7807adf..fc38080 100644 --- a/mpi-test.c +++ b/mpi-test.c @@ -1,13 +1,5 @@ -#include #include -#include #include "cq.h" -#include "datatypes.h" -#include "host_ops.h" -#include "src/host-device/comms.h" -// #include "src/host-device/mpi_comms.h" - -#include cq_status test_qkern(const size_t NQUBITS, qubit* qr, @@ -24,15 +16,6 @@ cq_status test_qkern(const size_t NQUBITS, } int main() { - // int rank; - // MPI_Init(NULL, NULL); - // MPI_Comm_rank(MPI_COMM_WORLD, &rank); - // printf("Hello from rank %d\n", rank); - // MPI_Finalize(); - // volatile int foo = 1; - // while (foo) - // sleep(5); - // return 0; const size_t NQUBITS = 10; const size_t NSHOTS = 10; const size_t NMEASURE = NQUBITS; diff --git a/src/host-device/CMakeLists.txt b/src/host-device/CMakeLists.txt index e9fd43f..7f5392a 100644 --- a/src/host-device/CMakeLists.txt +++ b/src/host-device/CMakeLists.txt @@ -2,7 +2,5 @@ add_subdirectory(comms) target_sources(cq-simbe PRIVATE -# comms.c kernel_utils.c -# mpi_comms.c ) diff --git a/src/host-device/comms/CMakeLists.txt b/src/host-device/comms/CMakeLists.txt index 60343b7..8c49b0d 100644 --- a/src/host-device/comms/CMakeLists.txt +++ b/src/host-device/comms/CMakeLists.txt @@ -12,5 +12,5 @@ else() target_sources(cq-simbe PRIVATE comms.c - ) + ) endif() diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index 0a5e3ed..d8621e7 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -164,11 +164,11 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { MPI_Init(NULL, NULL); MPI_Comm_size(CQ_MPI_COMM_WORLD, &nprocs); - validate_nproc(nprocs); + MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); + // TODO: for multi-device check that: // (nproc - 1) == n_device * power of 2 - - MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); + validate_nproc(nprocs); #if CQ_CONF_QUEST_WITH_MPI const int QUANTUM_WORKER = mpi_env.rank > 0; @@ -1143,12 +1143,26 @@ MPI_Comm get_quest_comm(void) { void validate_nproc(int nproc) { const int device_nproc = nproc - 1; +#if CQ_CONF_QUEST_WITH_MPI if (!((device_nproc > 0) && ((device_nproc & (device_nproc - 1)) == 0))) { - cq_log( - "Incorrect number of MPI processes. The (N - 1) should be power of " - "2!\n"); + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + cq_log( + "Incorrect number of MPI processes. The (N - 1) should be power of " + "2!\n"); + } + exit(CQ_MPI_RUNTIME_ERROR); + } +#endif +#ifndef CQ_CONF_QUEST_WITH_MPI + if (device_nproc != 1) { + if (mpi_env.rank == CQ_MPI_HOST_RANK) { + cq_log( + "Incorrect number of MPI processes. If QuEST uses multi-threading " + "only, there should be only 2 MPI processes used for CQ!\n"); + } exit(CQ_MPI_RUNTIME_ERROR); } +#endif } #undef CQ_MPI_IMPL_DEBUG diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c index 7c19f16..82939aa 100644 --- a/src/host-device/mpi_comms.c +++ b/src/host-device/mpi_comms.c @@ -4,12 +4,8 @@ #include "src/host-device/comms.h" #include "src/host/opcodes.h" -#include #include -#include -#include -#include #include #include #include @@ -17,6 +13,20 @@ #include #include +// ---------------------------------------------------------------------------- +// Macros +// ---------------------------------------------------------------------------- +#define CQ_MPI_IMPL_DEBUG + +#define CQ_MPI_HOST_RANK 0 +#define CQ_MPI_DEVICE_RANK 1 +#define CQ_MPI_DEVICE_MASTER_RANK 0 +#define CQ_MPI_WORLD_COMMS_TAG 0 +#define CQ_MPI_SUBCOMMS_TAG 0 + +#define CQ_MPI_RUNTIME_ERROR -4 +#define CQ_MPI_MALLOC_ERROR -5 + static const MPI_Comm CQ_MPI_COMM_WORLD = MPI_COMM_WORLD; static MPI_Comm CQ_MPI_SPLIT_COMM; @@ -53,7 +63,6 @@ static void cq_log(const char* format, ...) { } void init_host_device_mpi(const unsigned int VERBOSITY) { - assert(sizeof(enum ctrl_code) == sizeof(int)); mpi_env.verbosity = VERBOSITY; if (VERBOSITY > 0) { cq_log("Initialising MPI.\n"); @@ -64,11 +73,10 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { MPI_Init(NULL, NULL); MPI_Comm_size(CQ_MPI_COMM_WORLD, &nprocs); - // TODO: check nproc - 1 == power of 2 + MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); // TODO: for multi-device check that: // (nproc - 1) == n_device * power of 2 - - MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); + validate_nproc(nprocs); #if CQ_CONF_QUEST_WITH_MPI const int QUANTUM_WORKER = mpi_env.rank > 0; @@ -109,8 +117,6 @@ void finalise_host_device_mpi(const unsigned int VERBOSITY) { } } -// void mpi_host_send_ctrl_op(const enum ctrl_code OP, -// struct ctrl_params* params) { void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params) { const int device_rank = CQ_MPI_DEVICE_RANK; cq_log("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), @@ -135,7 +141,7 @@ void mpi_host_wait_all_ops(void) { get_comm_source(), num_ops); } -void host_device_final_sync(void) { +void host_device_sync_comms(void) { if (mpi_env.rank != CQ_MPI_HOST_RANK) { cq_log("%s [final_sync]: simply waiting\n", get_comm_source()); device_wait_comms(); @@ -1178,9 +1184,10 @@ size_t assign_exec_id(void) { return global_exec_id_counter; } -int is_device(void) { +bool is_device(void) { return mpi_env.rank != CQ_MPI_HOST_RANK; } + int is_quantum_worker(void) { return mpi_env.rank > 0; } @@ -1189,14 +1196,34 @@ MPI_Comm get_quest_comm(void) { return CQ_MPI_SPLIT_COMM; } -void foo() { - // initCustomMpiCommQuESTEnv(MPI_Comm questComm, int useGpuAccel, int - // useMultithread) +void validate_nproc(int nproc) { + const int device_nproc = nproc - 1; - // Basically what we want is we get MPI_COMM_WORLD - // we check that size - 2 (or host = 1 + n_dev) is power of 2 - // split it use one subcomm for host-device comms with 2 ranks - // the rest goes to quest and can be accessed from here using getter - // and then if build with MPI (both this and QuEST) call in control.c - // initCustomMpiCommQuESTEnv(get_quest_comm(), gpu, mthrd); +#if CQ_CONF_QUEST_WITH_MPI + if (!((device_nproc > 0) && ((device_nproc & (device_nproc - 1)) == 0))) { + cq_log( + "Incorrect number of MPI processes. If QuEST was built with MPI, the " + "(N - 1) should be power of " + "2!\n"); + exit(CQ_MPI_RUNTIME_ERROR); + } +#endif +#ifndef CQ_CONF_QUEST_WITH_MPI + cq_log("NPROC: %d\n", nproc); + if (device_nproc != 1) { + cq_log( + "Incorrect number of MPI processes. If QuEST uses multi-threading " + "only, there should be only 2 MPI processes used for CQ!\n"); + exit(CQ_MPI_RUNTIME_ERROR); + } +#endif } + +#undef CQ_MPI_IMPL_DEBUG +#undef CQ_MPI_HOST_RANK +#undef CQ_MPI_DEVICE_RANK +#undef CQ_MPI_DEVICE_MASTER_RANK +#undef CQ_MPI_WORLD_COMMS_TAG +#undef CQ_MPI_SUBCOMMS_TAG +#undef CQ_MPI_RUNTIME_ERROR +#undef CQ_MPI_MALLOC_ERROR diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h index c361358..01e40d6 100644 --- a/src/host-device/mpi_comms.h +++ b/src/host-device/mpi_comms.h @@ -1,26 +1,13 @@ #ifndef CQ_HOST_DEVICE_MPI_COMMS_H #define CQ_HOST_DEVICE_MPI_COMMS_H -#include #include "datatypes.h" #include "src/host-device/comms.h" #include "src/host/opcodes.h" #include -// ---------------------------------------------------------------------------- -// Macros -// ---------------------------------------------------------------------------- -#define CQ_MPI_IMPL_DEBUG - -#define CQ_MPI_HOST_RANK 0 -#define CQ_MPI_DEVICE_RANK 1 -#define CQ_MPI_DEVICE_MASTER_RANK 0 -#define CQ_MPI_WORLD_COMMS_TAG 0 -#define CQ_MPI_SUBCOMMS_TAG 0 - -#define CQ_MPI_RUNTIME_ERROR -4 -#define CQ_MPI_MALLOC_ERROR -5 +#include // device_wait_comms(); // if (is_quantum_worker()) { @@ -52,14 +39,6 @@ void finalise_host_device_mpi(const unsigned int VERBOSITY); // Host Comm Ops // ---------------------------------------------------------------------------- -/// -/// sends the control operation to the device. -/// @param OP an enum argument specifying CQ_CTRL_OP -/// @param params datatype argument storing arbitrary data and data type, useful -/// for a given control operation -// void mpi_host_send_ctrl_op(const enum ctrl_code OP, struct ctrl_params* -// params); - /// /// sends the control operation to the device. /// @param OP an enum argument specifying CQ_CTRL_OP @@ -68,25 +47,17 @@ void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params); /// /// blocks and waits for the operations to complete on the device. -/// at the end it should recieve updated params if needed??? void mpi_host_wait_all_ops(void); /// /// wrapper around device_wait_comms, intended to be called from the host. -/// The intended caller of this function is cq_finalise only! -void host_device_final_sync(void); -// TODO: rename above with below +/// The intended caller of this function is cq_finalise and register_(p)qkern! void host_device_sync_comms(void); // ---------------------------------------------------------------------------- // Device Comm Ops // ---------------------------------------------------------------------------- -/// -/// wait for message with control operation from the host. The intended use of -/// this function is to use it as a listener in the loop on the device. -// void mpi_device_recv_ctrl_op(void); - /// /// initialises on-device communication thread responsible for MPI messaging /// with host. @@ -114,6 +85,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP); /// /// blocks device main thread and awaits for the worker to complete. +/// @return number of remaining operations which should be 0 size_t device_wait_all_ops(void); /// @@ -133,9 +105,6 @@ void device_wait_comms(void); /// @param[in,out] params void pointer to arbitrary params void host_comm_params(const enum ctrl_code OP, void* params); -// NOTE: Alloc params can check the results across thread -// before Device Master sends to Host (in send) -// in recv Master will broadcast to Q-workers /// /// receives allocation parameters from the source. /// @param[out] params reference to parameters to store the results of @@ -152,8 +121,6 @@ void send_alloc_params(const device_alloc_params* params, int dest); size_t recv_exec_id(const int src); void send_exec_id(const size_t id, const int dest); -// NOTE: Exec params can be send by Device Master to Host (in send) -// in recv Master will broadcast to Q-workers /// /// receives executor handle from the source. Also, if called on device, /// allocates executor in on-device memory, which then needs to be freed using @@ -203,13 +170,33 @@ void print_alloc_params(const device_alloc_params* params); /// @param[in] ehp reference to executor void print_ehp(const cq_exec* ehp); +/// +/// generates and assigns id to the executor that is used for host-device +/// communication. +/// @return new executor id size_t assign_exec_id(void); +/// +/// check if MPI process is assigned to the device. +/// @return int is_device(void); +/// +/// check if MPI process is a quantum worker (not resposible for host-device +/// communication). +/// @return int is_quantum_worker(void); + +/// +/// accessor to the subcommunicator intended for QuEST simulation. +/// @return MPI Subcommunicator used to initialise QuEST environment MPI_Comm get_quest_comm(void); +/// validates the number of MPI processes to meet QuEST constraints. +/// Exits program if check failed. +/// @param nproc number of MPI processes +void validate_nproc(int nproc); + // ---------------------------------------------------------------------------- // TODO: Need to go to comms.h and comms.c and use existing stuff to wrap around // ---------------------------------------------------------------------------- From bc0de318b4c08453caa3af926e40fa5361aad1ec Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 18 May 2026 12:02:25 +0100 Subject: [PATCH 21/38] Removed unused files. Modified conditional compilation for the debug logging. Minor code cleanup. --- CMakeLists.txt | 6 +- src/host-device/comms/comms_mpi.c | 39 +- src/host-device/comms/comms_mpi.h | 9 + src/host-device/mpi_comms.c | 1229 ----------------------------- src/host-device/mpi_comms.c.OLD | 801 ------------------- src/host-device/mpi_comms.h | 208 ----- src/host-device/mpi_comms.h.OLD | 112 --- 7 files changed, 29 insertions(+), 2375 deletions(-) delete mode 100644 src/host-device/mpi_comms.c delete mode 100644 src/host-device/mpi_comms.c.OLD delete mode 100644 src/host-device/mpi_comms.h delete mode 100644 src/host-device/mpi_comms.h.OLD diff --git a/CMakeLists.txt b/CMakeLists.txt index c7b7646..037be9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,10 @@ add_library(CQ-SIMBE::cq-simbe ALIAS cq-simbe) if (BUILD_WITH_MPI) find_package(MPI REQUIRED) include_directories(SYSTEM ${MPI_INCLUDE_PATH}) - target_compile_definitions(cq-simbe PRIVATE CQ_WITH_MPI_COMMS=1) + target_compile_definitions(cq-simbe PRIVATE + CQ_WITH_MPI_COMMS=1 + $<$:DEBUG_MODE> + ) endif() if (BUILD_WITH_MPI AND QUEST_BUILT_WITH_MPI) @@ -78,6 +81,7 @@ endif() target_compile_features(cq-simbe PRIVATE c_std_99) target_compile_options(cq-simbe PRIVATE -Wall) + target_include_directories(cq-simbe PUBLIC $ diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index d8621e7..f86066e 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -5,12 +5,16 @@ #include "datatypes.h" #include "src/host/opcodes.h" +#ifndef CQ_CONF_QUEST_WITH_MPI #include "quest/include/environment.h" +#endif + +#if CQ_CONF_QUEST_WITH_MPI #include "quest/include/subcommunicator.h" +#endif #include -#include #include #include #include @@ -21,8 +25,6 @@ // ---------------------------------------------------------------------------- // Macros // ---------------------------------------------------------------------------- -#define CQ_MPI_IMPL_DEBUG - #define CQ_MPI_HOST_RANK 0 #define CQ_MPI_DEVICE_RANK 1 #define CQ_MPI_DEVICE_MASTER_RANK 0 @@ -56,10 +58,10 @@ struct communicator { static struct communicator dev_comm = {0}; -static char worker_name[64] = {0}; +static char debug_worker_name[64] = {0}; static void cq_log(const char* format, ...) { -#ifdef CQ_MPI_IMPL_DEBUG +#ifdef DEBUG_MODE va_list(args); va_start(args, format); vprintf(format, args); @@ -110,7 +112,6 @@ void host_device_sync_comms(void) { int finalise_device(const unsigned int VERBOSITY) { host_device_sync_comms(); - // RUN_HOST_ONLY(); if (mpi_env.rank == CQ_MPI_HOST_RANK) { host_wait_all_ops(); @@ -179,7 +180,7 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { cq_log("%s in subcomm I'm rank %d\n", get_comm_source(), mpi_env.subcomm_rank); - sprintf(worker_name, "Quantum Worker [%d]:\t\t", mpi_env.subcomm_rank); + sprintf(debug_worker_name, "Quantum Worker [%d]:\t\t", mpi_env.subcomm_rank); #endif if (VERBOSITY > 0) { @@ -195,10 +196,6 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { } void finalise_host_device_mpi(const unsigned int VERBOSITY) { - // if (mpi_env.rank != CQ_MPI_HOST_RANK) { - // MPI_Barrier(CQ_MPI_SPLIT_COMM); - // device_finalise_comms(VERBOSITY); - // } if (VERBOSITY > 0) { cq_log("%s Finalising MPI.\n", get_comm_source()); } @@ -309,7 +306,6 @@ void* device_listen(void* args) { } cq_log("%s closing connection.\n", get_comm_source()); - // finalise_host_device_mpi(mpi_env.verbosity); device_wait_all_ops(); finalise_device_controls(mpi_env.verbosity); @@ -322,15 +318,13 @@ void* device_listen(void* args) { void device_dispatch_ctrl_op(const enum ctrl_code OP) { switch (OP) { case CQ_CTRL_INIT: { - // initialise_device(mpi_env.verbosity); init_device_controls(mpi_env.verbosity); insert_op(OP, &mpi_env.verbosity); device_wait_all_ops(); break; } case CQ_CTRL_FINALISE: { - // don't need to insert op into worker. - // we just wait until worker is done and cleanup. + // we wait until worker is done and cleanup. device_wait_all_ops(); for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { if (executor_handles[i] != NULL) { @@ -343,10 +337,8 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // this is like finalise_device in original comms.c insert_op(OP, &mpi_env.verbosity); - device_wait_all_ops(); // this was uncommented + device_wait_all_ops(); stop_device(); - - // finalise_device(mpi_env.verbosity); break; } case CQ_CTRL_ALLOC: { @@ -404,8 +396,6 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { executor_handles[tmp_exec->id] = tmp_exec; insert_op(OP, executor_handles[tmp_exec->id]); ++num_active_executors; - // recv_exec_params(&executor_handles[executor_id], host_rank); - // insert_op(OP, executor_handles[executor_id]); break; } case CQ_CTRL_RUN_PQKERNEL: { @@ -539,13 +529,15 @@ void host_comm_params(const enum ctrl_code OP, void* params) { break; } case CQ_CTRL_WAIT_EXEC: { + cq_exec* exec_params = (cq_exec*)params; send_exec_id(((cq_exec*)params)->id, device_rank); - recv_exec_params(¶ms, device_rank); + recv_exec_params(&exec_params, device_rank); break; } case CQ_CTRL_SYNC_EXEC: { + cq_exec* exec_params = (cq_exec*)params; send_exec_id(((cq_exec*)params)->id, device_rank); - recv_exec_params(¶ms, device_rank); + recv_exec_params(&exec_params, device_rank); break; } case CQ_CTRL_ABORT: { @@ -1036,7 +1028,7 @@ const char* get_comm_source(void) { } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { return "Device:\t\t\t\t"; } else if (is_quantum_worker()) { - return worker_name; + return debug_worker_name; } else { return ""; } @@ -1165,7 +1157,6 @@ void validate_nproc(int nproc) { #endif } -#undef CQ_MPI_IMPL_DEBUG #undef CQ_MPI_HOST_RANK #undef CQ_MPI_DEVICE_RANK #undef CQ_MPI_DEVICE_MASTER_RANK diff --git a/src/host-device/comms/comms_mpi.h b/src/host-device/comms/comms_mpi.h index b46ae78..456f891 100644 --- a/src/host-device/comms/comms_mpi.h +++ b/src/host-device/comms/comms_mpi.h @@ -78,7 +78,16 @@ void recv_alloc_params(device_alloc_params* params, int src); /// @param dest destination rank of outgoing message void send_alloc_params(const device_alloc_params* params, int dest); +/// +/// receives executor id used for matching results with correct executor. +/// @param src source rank of incoming message +/// @return executor id size_t recv_exec_id(const int src); + +/// +/// sends executor id used for matching results with correct executor. +/// @param id executor id to be sent +/// @param dest destination rank of outgoing message void send_exec_id(const size_t id, const int dest); /// diff --git a/src/host-device/mpi_comms.c b/src/host-device/mpi_comms.c deleted file mode 100644 index 82939aa..0000000 --- a/src/host-device/mpi_comms.c +++ /dev/null @@ -1,1229 +0,0 @@ -#include "mpi_comms.h" - -#include "datatypes.h" -#include "src/host-device/comms.h" -#include "src/host/opcodes.h" - -#include - -#include -#include -#include -#include -#include -#include - -// ---------------------------------------------------------------------------- -// Macros -// ---------------------------------------------------------------------------- -#define CQ_MPI_IMPL_DEBUG - -#define CQ_MPI_HOST_RANK 0 -#define CQ_MPI_DEVICE_RANK 1 -#define CQ_MPI_DEVICE_MASTER_RANK 0 -#define CQ_MPI_WORLD_COMMS_TAG 0 -#define CQ_MPI_SUBCOMMS_TAG 0 - -#define CQ_MPI_RUNTIME_ERROR -4 -#define CQ_MPI_MALLOC_ERROR -5 - -static const MPI_Comm CQ_MPI_COMM_WORLD = MPI_COMM_WORLD; -static MPI_Comm CQ_MPI_SPLIT_COMM; - -struct cq_mpi_env { - int rank; - int subcomm_rank; - unsigned int verbosity; -}; - -static cq_exec* executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; -static size_t global_exec_id_counter = -1; -static size_t num_active_executors = 0; -static struct cq_mpi_env mpi_env = {.rank = -1, - .subcomm_rank = -1, - .verbosity = 0}; - -struct communicator { - bool comm_busy; - pthread_t device_comm_thread; - pthread_cond_t cond_comm_busy; - pthread_mutex_t comm_lock; -}; - -static struct communicator dev_comm = {0}; - -static char worker_name[64] = {0}; - -static void cq_log(const char* format, ...) { -#ifdef CQ_MPI_IMPL_DEBUG - va_list(args); - va_start(args, format); - vprintf(format, args); -#endif -} - -void init_host_device_mpi(const unsigned int VERBOSITY) { - mpi_env.verbosity = VERBOSITY; - if (VERBOSITY > 0) { - cq_log("Initialising MPI.\n"); - } - - int nprocs; - - MPI_Init(NULL, NULL); - - MPI_Comm_size(CQ_MPI_COMM_WORLD, &nprocs); - MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); - // TODO: for multi-device check that: - // (nproc - 1) == n_device * power of 2 - validate_nproc(nprocs); - -#if CQ_CONF_QUEST_WITH_MPI - const int QUANTUM_WORKER = mpi_env.rank > 0; - MPI_Barrier(CQ_MPI_COMM_WORLD); - MPI_Comm_split(CQ_MPI_COMM_WORLD, QUANTUM_WORKER, mpi_env.rank, - &CQ_MPI_SPLIT_COMM); - MPI_Comm_rank(CQ_MPI_SPLIT_COMM, &mpi_env.subcomm_rank); - cq_log("%s in subcomm I'm rank %d\n", get_comm_source(), - mpi_env.subcomm_rank); - - sprintf(worker_name, "Quantum Worker [%d]:\t\t", mpi_env.subcomm_rank); -#endif - - if (VERBOSITY > 0) { - cq_log("Initialised MPI.\n"); - } - - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - device_init_comms(VERBOSITY); - for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { - executor_handles[i] = NULL; - } - } -} - -void finalise_host_device_mpi(const unsigned int VERBOSITY) { - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - MPI_Barrier(CQ_MPI_SPLIT_COMM); - device_finalise_comms(VERBOSITY); - } - if (VERBOSITY > 0) { - cq_log("%s Finalising MPI.\n", get_comm_source()); - } - MPI_Barrier(CQ_MPI_COMM_WORLD); - MPI_Finalize(); - if (VERBOSITY > 0) { - cq_log("%s Finalised MPI.\n", get_comm_source()); - } -} - -void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params) { - const int device_rank = CQ_MPI_DEVICE_RANK; - cq_log("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), - op_to_str(OP)); - int op_comm_buffer = (int)OP; - MPI_Ssend(&op_comm_buffer, 1, MPI_INT, device_rank, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD); - // send params based on OP - host_comm_params(OP, params); - cq_log("%s [send_ctrl_op]: sent %s\n", get_comm_source(), op_to_str(OP)); -} - -void mpi_host_wait_all_ops(void) { - cq_log("%s [wait_all_ops]: waiting...\n", get_comm_source()); - mpi_host_send_ctrl_op(CQ_CTRL_WAIT, NULL); - size_t num_ops; - const int device_rank = CQ_MPI_DEVICE_RANK; - MPI_Status status; - MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD, &status); - cq_log("%s [wait_all_ops]: all ops completed (num_ops: %zu)\n", - get_comm_source(), num_ops); -} - -void host_device_sync_comms(void) { - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - cq_log("%s [final_sync]: simply waiting\n", get_comm_source()); - device_wait_comms(); - } -} - -// ---------------------------------------------------------------------------- -// Device Comm Ops -// ---------------------------------------------------------------------------- - -// NOTE: aka host_send_ctrl_op from comm.c -// really what I want to use is host_send_ctrl_op implemenetation but it needs -// renaming and wrapping into function called host_send_ctrl_op and depending on -// either MPI or pthread implementation use this in different ctx. -void insert_op(const enum ctrl_code OP, void* ctrl_params) { - pthread_mutex_lock(&dev_ctrl.device_lock); - - while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { - // the control queue is full! - // we'll wait for it to not be full - pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); - } - - dev_ctrl.op_buffer[dev_ctrl.next_op_in] = OP; - dev_ctrl.op_params_buffer[dev_ctrl.next_op_in] = ctrl_params; - ++dev_ctrl.num_ops; - - // It's a ring buffer! - // advance next_op_in then mod out buffer size - ++dev_ctrl.next_op_in; - dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; - - pthread_cond_signal(&dev_ctrl.cond_queue_empty); - pthread_mutex_unlock(&dev_ctrl.device_lock); -} - -void device_init_comms(const unsigned int VERBOSITY) { - if (VERBOSITY > 0) { - cq_log( - "%s [device_init_comms]: setting up on-device communication thread.\n", - get_comm_source()); - } - dev_ctrl.run_device = true; - // cq_log("%s started listening...\n", get_comm_source()); - // device_listen(); - // cq_log("%s closing connection.\n", get_comm_source()); - // finalise_host_device_mpi(VERBOSITY); - dev_comm.comm_busy = true; - pthread_mutex_init(&dev_comm.comm_lock, NULL); - pthread_cond_init(&dev_comm.cond_comm_busy, NULL); - pthread_create(&dev_comm.device_comm_thread, NULL, &device_listen, NULL); - if (VERBOSITY > 0) { - cq_log("%s [device_init_comms]: on-device communication thread set up.\n", - get_comm_source()); - } -} - -void device_finalise_comms(const unsigned int VERBOSITY) { - if (VERBOSITY > 0) { - cq_log( - "%s [device_finalise_comms]: finalising on-device communication " - "thread.\n", - get_comm_source()); - } - - pthread_join(dev_comm.device_comm_thread, NULL); - dev_comm.comm_busy = false; - pthread_cond_destroy(&dev_comm.cond_comm_busy); - pthread_mutex_destroy(&dev_comm.comm_lock); - if (VERBOSITY > 0) { - cq_log( - "%s [device_finalise_comms]: on-device communication thread closed.\n", - get_comm_source()); - } -} - -void* device_listen(void* args) { - // run_device set to FALSE when OP == CQ_CTRL_FINALISE - cq_log("%s started listening...\n", get_comm_source()); - while (dev_ctrl.run_device) { - pthread_mutex_lock(&dev_comm.comm_lock); - dev_comm.comm_busy = true; - pthread_cond_signal(&dev_comm.cond_comm_busy); - pthread_mutex_unlock(&dev_comm.comm_lock); - - enum ctrl_code OP; - int op_comm_buffer; - - cq_log("%s [device_listen]: receiving OP...\n", get_comm_source()); -#if CQ_CONF_QUEST_WITH_MPI - // device master (rank 0) gets from host from different comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { -#endif - - const int host_rank = CQ_MPI_HOST_RANK; - MPI_Status status; - MPI_Recv(&op_comm_buffer, 1, MPI_INT, host_rank, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD, &status); -#if CQ_CONF_QUEST_WITH_MPI - // rank 0 brodcast to rest of q-workers then all do the dispatch - } - cq_log("%s [device_listen]: Starting Bcast\n", get_comm_source()); - // NOTE: doing custom bcast because I have MPI errors on my machine - // when calling MPICH Bcast! - MPI_Barrier(CQ_MPI_SPLIT_COMM); - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { - int subcomm_size = 0; - MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); - - for (size_t i = 1; i < subcomm_size; ++i) { - MPI_Ssend(&op_comm_buffer, 1, MPI_INT, i, CQ_MPI_SUBCOMMS_TAG, - CQ_MPI_SPLIT_COMM); - } - } else { - MPI_Status status; - MPI_Recv(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, - CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); - } - MPI_Barrier(CQ_MPI_SPLIT_COMM); - - // MPI_Bcast(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, - // CQ_MPI_SPLIT_COMM); - cq_log("%s [device_listen]: Finished Bcast\n", get_comm_source()); -#endif - - OP = (enum ctrl_code)op_comm_buffer; - cq_log("%s [device_listen]: received %s\n", get_comm_source(), - op_to_str(OP)); - - device_dispatch_ctrl_op(OP); - - pthread_mutex_lock(&dev_comm.comm_lock); - dev_comm.comm_busy = false; - pthread_cond_signal(&dev_comm.cond_comm_busy); - pthread_mutex_unlock(&dev_comm.comm_lock); - } - cq_log("%s closing connection.\n", get_comm_source()); - - finalise_host_device_mpi(mpi_env.verbosity); - - pthread_join(dev_ctrl.device_thread, NULL); - dev_ctrl.device_busy = false; - pthread_cond_destroy(&dev_ctrl.cond_device_busy); - pthread_cond_destroy(&dev_ctrl.cond_queue_empty); - pthread_cond_destroy(&dev_ctrl.cond_queue_full); - pthread_mutex_destroy(&dev_ctrl.device_lock); - - return NULL; -} - -// optionally get params and run op -// by running op I mean modyfing the internal dev_ctrl fields -// and then the worker thread handles the rest. -void device_dispatch_ctrl_op(const enum ctrl_code OP) { - switch (OP) { - case CQ_CTRL_INIT: { - initialise_device(mpi_env.verbosity); - insert_op(OP, &mpi_env.verbosity); - device_wait_all_ops(); - break; - } - case CQ_CTRL_FINALISE: { - // don't need to insert op into worker. - // we just wait until worker is done and cleanup. - device_wait_all_ops(); - for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { - if (executor_handles[i] != NULL) { - cq_log( - "%s [dispatch][FINALISE]: executor handle with id: %zu is still " - "active. Something went wrong!\n", - get_comm_source(), i); - } - } - - insert_op(OP, &mpi_env.verbosity); - device_wait_all_ops(); - pthread_mutex_lock(&dev_ctrl.device_lock); - dev_ctrl.run_device = false; - pthread_mutex_unlock(&dev_ctrl.device_lock); - - // finalise_device(mpi_env.verbosity); - break; - } - case CQ_CTRL_ALLOC: { - // Alloc is blocking: we get params, run allocation and - // send back the updated params. - // Also, because it's blocking I don't need to worry about - // params lifetime (from the worker perspective) - const int host_rank = CQ_MPI_HOST_RANK; - device_alloc_params params = {0}; - recv_alloc_params(¶ms, host_rank); - insert_op(OP, ¶ms); - cq_log("%s [dispatch][ALLOC]: inserted op\n", get_comm_source()); - device_wait_all_ops(); - send_alloc_params(¶ms, host_rank); - break; - } - case CQ_CTRL_DEALLOC: { - // Same as Alloc - const int host_rank = CQ_MPI_HOST_RANK; - device_alloc_params params = {0}; - recv_alloc_params(¶ms, host_rank); - insert_op(OP, ¶ms); - device_wait_all_ops(); - send_alloc_params(¶ms, host_rank); - break; - } - case CQ_CTRL_RUN_QKERNEL: { - if (num_active_executors >= __CQ_DEVICE_QUEUE_SIZE__) { - cq_log( - "%s [dispatch][RUN_QKERNEL]: You have oversubsribed the executor " - "queue. We allow up to %d " - "concurrent executors per device. Exiting", - get_comm_source(), __CQ_DEVICE_QUEUE_SIZE__); - exit(CQ_MPI_RUNTIME_ERROR); - } - const int host_rank = CQ_MPI_HOST_RANK; - cq_exec* tmp_exec = NULL; - recv_exec_params(&tmp_exec, host_rank); - // NOTE: this can be done here to free old one - // and allocate new one rather than in wait_exec - // device_free_exec(&executor_handles[tmp_exec->id]); - // actually if executor_handles[id] != NULL then it didn't finish and we - // should wait here until we are done. - // - // NOTE: 2 - // Now the question is should we block? - // 1. if we block here and wait for all ops to complete then - // in situation if host submits N_exe > QUEUE_SIZE, we lock here - // indefinitely - // ex: a_qrun(...) x QUEUE_SIZE + 1 (and no wait_qrun...) - // => deadlock - // 2. alternative would be wait to set exec_queue_full flag - // and wait until it is released - // 3. we can just exit and fail! -- most reasonable: - executor_handles[tmp_exec->id] = tmp_exec; - insert_op(OP, executor_handles[tmp_exec->id]); - ++num_active_executors; - // recv_exec_params(&executor_handles[executor_id], host_rank); - // insert_op(OP, executor_handles[executor_id]); - break; - } - case CQ_CTRL_RUN_PQKERNEL: { - // recv params - // insert_op - break; - } - case CQ_CTRL_WAIT_EXEC: { - const int host_rank = CQ_MPI_HOST_RANK; - const size_t executor_id = recv_exec_id(host_rank); - if (executor_handles[executor_id] == NULL) { - cq_log( - "%s [dispatch][WAIT_EXEC]: device ehp is NULL. Returning. " - "Exiting\n", - get_comm_source()); - exit(CQ_MPI_RUNTIME_ERROR); - } - // NOTE: This is commented out as it can cause a deadlock - // comms_exec_wait(executor_handles[executor_id]); - device_wait_all_ops(); - send_exec_params(executor_handles[executor_id], host_rank); - // NOTE: This can be done when allocating new in offload - // i.e. clear old one and allocate new one - device_free_exec(&executor_handles[executor_id]); - --num_active_executors; - if (num_active_executors < 0) { - cq_log( - "%s [dispatch][WAIT_EXEC]: The number of active executors is < 0! " - "Should not happen. Exiting", - get_comm_source()); - exit(CQ_MPI_RUNTIME_ERROR); - } - break; - } - case CQ_CTRL_SYNC_EXEC: { - const int host_rank = CQ_MPI_HOST_RANK; - const size_t executor_id = recv_exec_id(host_rank); - if (executor_handles[executor_id] == NULL) { - cq_log( - "%s [dispatch][SYNC_EXEC]: device ehp is NULL. Returning. " - "Exiting\n", - get_comm_source()); - exit(CQ_MPI_RUNTIME_ERROR); - } - comms_exec_sync(executor_handles[executor_id]); - cq_log("%s [dispatch][SYNC_EXEC]: executor synced\n", get_comm_source()); - send_exec_params(executor_handles[executor_id], host_rank); - cq_log("%s [dispatch][SYNC_EXEC]: sent executor\n", get_comm_source()); - break; - } - case CQ_CTRL_WAIT: { - // wait for worker and send info to the host - // (which is blocked and waiting). - cq_log("%s [dispatch][WAIT]: Started waiting\n", get_comm_source()); - size_t num_ops = device_wait_all_ops(); - cq_log("%s [dispatch][WAIT]: Finished waiting\n", get_comm_source()); -#if CQ_CONF_QUEST_WITH_MPI - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { -#endif - const int host_rank = CQ_MPI_HOST_RANK; - cq_log("%s [dispatch]: sending num ops: %zu...\n", get_comm_source(), - num_ops); - MPI_Ssend(&num_ops, 1, MPI_UINT64_T, host_rank, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD); - cq_log("%s [dispatch]: sent num ops.\n", get_comm_source()); -#if CQ_CONF_QUEST_WITH_MPI - } -#endif - break; - } - case CQ_CTRL_ABORT: { - const int host_rank = CQ_MPI_HOST_RANK; - const size_t executor_id = recv_exec_id(host_rank); - if (executor_handles[executor_id] == NULL) { - cq_log( - "%s [dispatch][ABORT]: device ehp is NULL. Returning. " - "Exiting\n", - get_comm_source()); - exit(CQ_MPI_RUNTIME_ERROR); - } - comms_exec_halt(executor_handles[executor_id]); - cq_log("%s [dispatch][ABORT]: executor halted\n", get_comm_source()); - break; - } - case CQ_CTRL_IDLE: { - break; - } - case CQ_CTRL_TEST: { - cq_log("%s [dispatch][TEST]: called test op\n", get_comm_source()); - break; - } - default: { - break; - } - } -} - -// NOTE: aka host_wait_all_ops from comm.c -// really what I want to use is host_wait_all_ops implementation but it needs -// renaming and wrapping into function called host_send_ctrl_op and depending -// on either MPI or pthread implementation use this in different ctx. -size_t device_wait_all_ops(void) { - pthread_mutex_lock(&dev_ctrl.device_lock); - cq_log("%s [device_wait_all_ops]: Entering loop, num ops: %d\n", - get_comm_source(), dev_ctrl.num_ops); - while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { - pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); - } - cq_log("%s [device_wait_all_ops]: Exited loop, num ops: %d\n", - get_comm_source(), dev_ctrl.num_ops); - - pthread_mutex_unlock(&dev_ctrl.device_lock); - return dev_ctrl.num_ops; -} - -void device_wait_comms(void) { - pthread_mutex_lock(&dev_comm.comm_lock); - while (dev_comm.comm_busy) { - pthread_cond_wait(&dev_comm.cond_comm_busy, &dev_comm.comm_lock); - } - pthread_mutex_unlock(&dev_comm.comm_lock); -} - -// for those below I probably I want comms_exec_sync, comms_exec_wait, -// comms_exec_halt -// NOTE: really what I want to use is host_sync_exec -// implemenetation but it needs renaming and wrapping into function called -// host_send_ctrl_op and depending on either MPI or pthread implementation use -// this in different ctx. -size_t comms_exec_sync(cq_exec* const ehp) { - size_t completed_shots = 0; - pthread_mutex_lock(&(ehp->lock)); - completed_shots = ehp->completed_shots; - pthread_mutex_unlock(&(ehp->lock)); - return completed_shots; -} - -// NOTE: -// really what I want to use is host_wait_exec implemenetation but it needs -// renaming and wrapping into function called host_send_ctrl_op and depending -// on either MPI or pthread implementation use this in different ctx. -size_t comms_exec_wait(cq_exec* const ehp) { - cq_log("%s [comms_exec_wait]: started wait...\n", get_comm_source()); - pthread_mutex_lock(&(ehp->lock)); - cq_log("%s [comms_exec_wait]: locked mutex\n", get_comm_source()); - while (!ehp->complete) { - cq_log("%s [comms_exec_wait]: started loop\n", get_comm_source()); - pthread_cond_wait(&(ehp->cond_exec_complete), &(ehp->lock)); - cq_log("%s [comms_exec_wait]: ending loop\n", get_comm_source()); - } - cq_log("%s [comms_exec_wait]: done while loop\n", get_comm_source()); - pthread_mutex_unlock(&(ehp->lock)); - cq_log("%s [comms_exec_wait]: unlocked mutex\n", get_comm_source()); - cq_log("%s [comms_exec_wait]: done waiting\n", get_comm_source()); - return ehp->completed_shots; -} - -// NOTE: -// really what I want to use is host_request_halt implemenetation but it needs -// renaming and wrapping into function called host_send_ctrl_op and depending -// on either MPI or pthread implementation use this in different ctx. -void comms_exec_halt(cq_exec* const ehp) { - pthread_mutex_lock(&(ehp->lock)); - ehp->halt = true; - pthread_mutex_unlock(&(ehp->lock)); - return; -} - -// ---------------------------------------------------------------------------- -// Device Control Paramaters Comms -// ---------------------------------------------------------------------------- - -void host_comm_params(const enum ctrl_code OP, void* params) { - const int device_rank = CQ_MPI_DEVICE_RANK; - switch (OP) { - case CQ_CTRL_INIT: { - break; - } - case CQ_CTRL_FINALISE: { - break; - } - case CQ_CTRL_ALLOC: { - send_alloc_params(params, device_rank); - recv_alloc_params(params, device_rank); - break; - } - case CQ_CTRL_DEALLOC: { - send_alloc_params(params, device_rank); - recv_alloc_params(params, device_rank); - break; - } - case CQ_CTRL_RUN_QKERNEL: { - send_exec_params(params, device_rank); - break; - } - case CQ_CTRL_WAIT_EXEC: { - send_exec_id(((cq_exec*)params)->id, device_rank); - recv_exec_params(¶ms, device_rank); - break; - } - case CQ_CTRL_SYNC_EXEC: { - send_exec_id(((cq_exec*)params)->id, device_rank); - recv_exec_params(¶ms, device_rank); - break; - } - case CQ_CTRL_ABORT: { - send_exec_id(((cq_exec*)params)->id, device_rank); - break; - } - default: { - break; - } - } -} - -void recv_alloc_params(device_alloc_params* params, int src) { - cq_log("%s [recv_alloc_params]: receiving...\n", get_comm_source()); - - const size_t params_size = sizeof(device_alloc_params); - MPI_Status status; - -#if CQ_CONF_QUEST_WITH_MPI - // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { -#endif - - MPI_Recv(params, params_size, MPI_BYTE, src, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD, &status); - -#if CQ_CONF_QUEST_WITH_MPI - } - // device-rank 0 brodcast params to rest of q-workers - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - // NOTE: doing custom bcast because I have MPI errors on my machine - // when calling MPICH Bcast! - MPI_Barrier(CQ_MPI_SPLIT_COMM); - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { - int subcomm_size = 0; - MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); - - for (size_t i = 1; i < subcomm_size; ++i) { - MPI_Ssend(params, params_size, MPI_BYTE, i, CQ_MPI_SUBCOMMS_TAG, - CQ_MPI_SPLIT_COMM); - } - } else { - MPI_Recv(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, - CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); - } - MPI_Barrier(CQ_MPI_SPLIT_COMM); - // MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, - // CQ_MPI_SPLIT_COMM); - } -#endif - - cq_log("%s [recv_alloc_params]: received.\n", get_comm_source()); - print_alloc_params(params); -} - -void send_alloc_params(const device_alloc_params* params, int dest) { -#if CQ_CONF_QUEST_WITH_MPI - // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { -#endif - - cq_log("%s [send_alloc_params]: sending...\n", get_comm_source()); - const size_t params_size = sizeof(device_alloc_params); - print_alloc_params(params); - MPI_Ssend(params, params_size, MPI_BYTE, dest, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD); - cq_log("%s [send_alloc_params]: sent.\n", get_comm_source()); - -#if CQ_CONF_QUEST_WITH_MPI - } -#endif -} - -size_t recv_exec_id(const int src) { - cq_log("%s [recv_exec_id]: receiving...\n", get_comm_source()); - size_t id = -1; - MPI_Status status; - -#if CQ_CONF_QUEST_WITH_MPI - // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { -#endif - - MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD, &status); - -#if CQ_CONF_QUEST_WITH_MPI - } - // device-rank 0 brodcast params to rest of q-workers - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - // NOTE: doing custom bcast because I have MPI errors on my machine - // when calling MPICH Bcast! - MPI_Barrier(CQ_MPI_SPLIT_COMM); - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { - int subcomm_size = 0; - MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); - - for (size_t i = 1; i < subcomm_size; ++i) { - MPI_Ssend(&id, 1, MPI_UINT64_T, i, CQ_MPI_SUBCOMMS_TAG, - CQ_MPI_SPLIT_COMM); - } - } else { - MPI_Recv(&id, 1, MPI_UINT64_T, CQ_MPI_DEVICE_MASTER_RANK, - CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); - } - MPI_Barrier(CQ_MPI_SPLIT_COMM); - // MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, - // CQ_MPI_SPLIT_COMM); - } -#endif - - cq_log("%s [recv_exec_id]: received id: %zu\n", get_comm_source(), id); - return id; -} - -void send_exec_id(const size_t id, const int dest) { -#if CQ_CONF_QUEST_WITH_MPI - // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { -#endif - - cq_log("%s [send_exec_id]: sending id: %zu...\n", get_comm_source(), id); - MPI_Ssend(&id, 1, MPI_UINT64_T, dest, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD); - cq_log("%s [send_exec_id]: sent\n", get_comm_source()); - -#if CQ_CONF_QUEST_WITH_MPI - } -#endif -} - -void recv_exec_params(cq_exec** ehp, int src) { - // TODO: do validation - cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); - int msg_size; - MPI_Status status; - -#if CQ_CONF_QUEST_WITH_MPI - // device-master (rank 0) gets from host from world comm - // or host gets from device-master - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { -#endif - - MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD, &status); - -#if CQ_CONF_QUEST_WITH_MPI - } - // device-rank 0 brodcast params to rest of q-workers - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - MPI_Barrier(CQ_MPI_SPLIT_COMM); - MPI_Bcast(&msg_size, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, - CQ_MPI_SPLIT_COMM); - } -#endif - - void* recv_buffer = malloc(msg_size); - - // reserve space for all the date + currently unused members - if (*ehp == NULL) { - cq_log("%s [recv_exec_params]: *ehp is NULL. Allocating on device.\n", - get_comm_source()); - - // ehp = (cq_exec*)malloc(msg_size + sizeof(pthread_mutex_t) + - // sizeof(pthread_cond_t) + sizeof(void*)); - *ehp = (cq_exec*)malloc(sizeof(cq_exec)); - pthread_mutex_init(&(*ehp)->lock, NULL); - pthread_cond_init(&(*ehp)->cond_exec_complete, NULL); - } else { - cq_log( - "%s [recv_exec_params]: *ehp is already allocated. So I'm on " - "host.\n", - get_comm_source()); - } - pthread_mutex_lock(&(*ehp)->lock); - - if (recv_buffer == NULL || *ehp == NULL) { - cq_log("%s [recv_exec_params]: malloc failed. Exiting\n", - get_comm_source()); - exit(CQ_MPI_MALLOC_ERROR); - } - -#if CQ_CONF_QUEST_WITH_MPI - // device-master (rank 0) gets from host from world comm - // or host gets from device-master - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { -#endif - - MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD, &status); - -#if CQ_CONF_QUEST_WITH_MPI - } - // device-rank 0 brodcast params to rest of q-workers - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - MPI_Barrier(CQ_MPI_SPLIT_COMM); - MPI_Bcast(recv_buffer, msg_size, MPI_PACKED, CQ_MPI_DEVICE_MASTER_RANK, - CQ_MPI_SPLIT_COMM); - } -#endif - - const size_t bool_size = sizeof(bool); - const size_t cq_status_size = sizeof(cq_status); - // unpack - int position = 0; - MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->id, 1, MPI_UINT64_T, - CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->exec_init, bool_size, - MPI_BYTE, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->complete, bool_size, - MPI_BYTE, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->halt, bool_size, - MPI_BYTE, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->status, cq_status_size, - MPI_BYTE, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->nqubits, 1, - MPI_UINT64_T, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->completed_shots, 1, - MPI_UINT64_T, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->expected_shots, 1, - MPI_UINT64_T, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->nmeasure, 1, - MPI_UINT64_T, CQ_MPI_COMM_WORLD); - - size_t fname_size = 0; - MPI_Unpack(recv_buffer, msg_size, &position, &fname_size, 1, MPI_UINT64_T, - CQ_MPI_COMM_WORLD); - fname_size *= sizeof(char); - - const size_t qreg_size = sizeof(qubit) * (*ehp)->nqubits; - // NOTE: creg_size * expected_shots?? or completed_shots? - // host should get completed, device should get expected - // and similarly when sending - // size_t num_shots = 0; - // if (mpi_env.rank == CQ_MPI_HOST_RANK) { - // num_shots = (*ehp)->completed_shots; - // } else { - // num_shots = (*ehp)->expected_shots; - // } - const size_t num_shots = (*ehp)->expected_shots; - const size_t creg_size = sizeof(cstate) * (*ehp)->nmeasure * num_shots; - - // when on host the resources are already allocated! - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - (*ehp)->fname = (char*)malloc(fname_size); - if ((*ehp)->fname == NULL) { - cq_log("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", - get_comm_source()); - exit(CQ_MPI_MALLOC_ERROR); - } - - (*ehp)->qreg = (qubit*)malloc(qreg_size); - if ((*ehp)->qreg == NULL) { - cq_log("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", - get_comm_source()); - exit(CQ_MPI_MALLOC_ERROR); - } - - (*ehp)->creg = (cstate*)malloc(creg_size); - if ((*ehp)->creg == NULL) { - cq_log("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", - get_comm_source()); - exit(CQ_MPI_MALLOC_ERROR); - } - } - - MPI_Unpack(recv_buffer, msg_size, &position, (*ehp)->fname, fname_size, - MPI_CHAR, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->qreg, qreg_size, - MPI_BYTE, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->creg, creg_size, - MPI_BYTE, CQ_MPI_COMM_WORLD); - - // NOTE: recv ehp->params left for another day... it's for pqkerns - - free(recv_buffer); - - cq_log("%s [recv_exec_params]: received.\n", get_comm_source()); - print_ehp(*ehp); - pthread_mutex_unlock(&(*ehp)->lock); -} - -void send_exec_params(cq_exec* ehp, int dest) { -// if running with MPI QuEST, we don't need to communicate -// with quantum workers because the results from QuEST -// (e.g. measurements) should be already synchronised. -// so only device-master communicates its state to host. -#if CQ_CONF_QUEST_WITH_MPI - // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { -#endif - - pthread_mutex_lock(&ehp->lock); - cq_log("%s [send_exec_params]: sending...\n", get_comm_source()); - print_ehp(ehp); - - const size_t bool_size = sizeof(bool); - const size_t cq_status_size = sizeof(cq_status); - int max_buffer_size = 0; - int member_size = 0; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &max_buffer_size); // id - MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // exec_init - max_buffer_size += member_size; - MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // complete - max_buffer_size += member_size; - MPI_Pack_size(bool_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // halt - max_buffer_size += member_size; - MPI_Pack_size(cq_status_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // status - max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // nqubits - max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // completed_shots - max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // expected_shots - max_buffer_size += member_size; - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // nmeasure - max_buffer_size += member_size; - - // ------------------------------------------------------------------------- - // TODO: sending pthred stuff doesn't sound like a good idea... - // sounds like UB - // const size_t pthread_mutex_size = sizeof(pthread_mutex_t); - // MPI_Pack_size(pthread_mutex_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - // &member_size); // lock - // max_buffer_size += member_size; - // - // const size_t pthread_cond_size = sizeof(pthread_cond_t); - // MPI_Pack_size(pthread_cond_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - // &member_size); // cond_exec_complete - // max_buffer_size += member_size; - // ------------------------------------------------------------------------- - - size_t fname_size = 0; - if (ehp->fname != NULL) { - fname_size = strlen(ehp->fname) + 1; - } - MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, - &member_size); // fname_size - max_buffer_size += member_size; - MPI_Pack_size(fname_size, MPI_CHAR, CQ_MPI_COMM_WORLD, - &member_size); // fname - max_buffer_size += member_size; - - const size_t qreg_size = sizeof(qubit) * ehp->nqubits; - MPI_Pack_size(qreg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // qreg - max_buffer_size += member_size; - - // NOTE: creg_size * expected_shots?? or completed_shots? - // host should send expected, device should send completed - // size_t num_shots = 0; - // if (mpi_env.rank == CQ_MPI_HOST_RANK) { - // num_shots = ehp->expected_shots; - //} else { - // num_shots = ehp->completed_shots; - //} - const size_t num_shots = ehp->expected_shots; - const size_t creg_size = sizeof(cstate) * ehp->nmeasure * num_shots; - - MPI_Pack_size(creg_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - &member_size); // creg - max_buffer_size += member_size; - - // NOTE: sending ehp->params left for another day... it's for pqkerns - - void* send_buffer = malloc(max_buffer_size); - if (send_buffer == NULL) { - cq_log( - "Failed to allocate buffer for sending executor handle. Exiting\n"); - exit(CQ_MPI_MALLOC_ERROR); - } - int position = 0; - MPI_Pack(&ehp->id, 1, MPI_UINT64_T, send_buffer, max_buffer_size, &position, - CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->exec_init, bool_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->complete, bool_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->halt, bool_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->status, cq_status_size, MPI_BYTE, send_buffer, - max_buffer_size, &position, CQ_MPI_COMM_WORLD); - - MPI_Pack(&ehp->nqubits, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->completed_shots, 1, MPI_UINT64_T, send_buffer, - max_buffer_size, &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->expected_shots, 1, MPI_UINT64_T, send_buffer, - max_buffer_size, &position, CQ_MPI_COMM_WORLD); - MPI_Pack(&ehp->nmeasure, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - - // NOTE: skip pthread stuff... - - MPI_Pack(&fname_size, 1, MPI_UINT64_T, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(ehp->fname, fname_size, MPI_CHAR, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - - MPI_Pack(ehp->qreg, qreg_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - MPI_Pack(ehp->creg, creg_size, MPI_BYTE, send_buffer, max_buffer_size, - &position, CQ_MPI_COMM_WORLD); - - // NOTE: ehp->prams left for another day - - MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD); - - MPI_Ssend(send_buffer, position, MPI_PACKED, dest, CQ_MPI_WORLD_COMMS_TAG, - CQ_MPI_COMM_WORLD); - - free(send_buffer); - cq_log("%s [send_exec_params]: sent.\n", get_comm_source()); - pthread_mutex_unlock(&ehp->lock); -#if CQ_CONF_QUEST_WITH_MPI - } -#endif -} - -void device_free_exec(cq_exec** ehp) { - cq_log("%s [device_free_exec]: freeing the executor\n", get_comm_source()); - if (ehp == NULL) { - cq_log("%s [device_free_exec]: ehp is NULL\n", get_comm_source()); - return; - } - - if (*ehp == NULL) { - cq_log("%s [device_free_exec]: *ehp is NULL\n", get_comm_source()); - return; - } - - pthread_mutex_lock(&(*ehp)->lock); - if ((*ehp)->fname != NULL) { - free((*ehp)->fname); - (*ehp)->fname = NULL; - } - - if ((*ehp)->qreg != NULL) { - free((*ehp)->qreg); - (*ehp)->qreg = NULL; - } - - if ((*ehp)->creg != NULL) { - free((*ehp)->creg); - (*ehp)->creg = NULL; - } - - if ((*ehp)->params != NULL) { - free((*ehp)->params); - (*ehp)->params = NULL; - } - - pthread_mutex_unlock(&(*ehp)->lock); - pthread_mutex_destroy(&(*ehp)->lock); - pthread_cond_destroy(&(*ehp)->cond_exec_complete); - if ((*ehp) != NULL) { - free((*ehp)); - (*ehp) = NULL; - } - - cq_log("%s [device_free_exec]: freed the executor\n", get_comm_source()); -} - -// ---------------------------------------------------------------------------- -// Helpers -// ---------------------------------------------------------------------------- - -const char* get_comm_source(void) { - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - return "Host:\t\t\t\t"; - } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { - return "Device:\t\t\t\t"; - } else if (is_quantum_worker()) { - return worker_name; - } else { - return ""; - } -} - -int get_rank(void) { - return mpi_env.rank; -} - -const char* op_to_str(const enum ctrl_code OP) { - switch (OP) { - case CQ_CTRL_IDLE: { - return "CQ_CTRL_IDLE"; - break; - } - case CQ_CTRL_ALLOC: { - return "CQ_CTRL_ALLOC"; - break; - } - case CQ_CTRL_DEALLOC: { - return "CQ_CTRL_DEALLOC"; - break; - } - case CQ_CTRL_INIT: { - return "CQ_CTRL_INIT"; - break; - } - case CQ_CTRL_FINALISE: { - return "CQ_CTRL_FINALISE"; - break; - } - case CQ_CTRL_WAIT: { - return "CQ_CTRL_WAIT"; - break; - } - case CQ_CTRL_RUN_QKERNEL: { - return "CQ_CTRL_RUN_QKERNEL"; - break; - } - case CQ_CTRL_RUN_PQKERNEL: { - return "CQ_CTRL_RUN_PQKERNEL"; - break; - } - case CQ_CTRL_TEST: { - return "CQ_CTRL_TEST"; - break; - } - case CQ_CTRL_ABORT: { - return "CQ_CTRL_ABORT"; - break; - } - case CQ_CTRL_WAIT_EXEC: { - return "CQ_CTRL_WAIT_EXEC"; - break; - } - case CQ_CTRL_SYNC_EXEC: { - return "CQ_CTRL_SYNC_EXEC"; - break; - } - default: { - break; - } - } - return ""; -} - -void print_alloc_params(const device_alloc_params* params) { - cq_log("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", - get_comm_source(), params->NQUBITS, params->qregistry_idx, - params->status); -} - -void print_ehp(const cq_exec* ehp) { - if (ehp == NULL) { - cq_log("ehp is NULL\n"); - } - - cq_log( - "%s ehp details:\nid: %zu, exec_init: %d, complete: %d, halt: %d, " - "STATUS: " - "%d\nNQUBITS: " - "%zu, completed_shots: %zu, expected_shots: %zu, NMEASURE: " - "%zu\nfname: " - "%s\nqreg:\n", - get_comm_source(), ehp->id, ehp->exec_init, ehp->complete, ehp->halt, - ehp->status, ehp->nqubits, ehp->completed_shots, ehp->expected_shots, - ehp->nmeasure, ehp->fname); - - for (size_t i = 0; i < ehp->nqubits; ++i) { - cq_log("qubit[%zu]: reg_idx: %zu, offset: %zu, N: %zu\n", i, - ehp->qreg[i].registry_index, ehp->qreg[i].offset, ehp->qreg[i].N); - } - cq_log("\ncreg:\n"); - - for (size_t i = 0; i < ehp->nmeasure * ehp->expected_shots; ++i) { - cq_log("cstate[%zu]: %d\n", i, ehp->creg[i]); - } - cq_log("%s ehp details END\n\n", get_comm_source()); -} - -size_t assign_exec_id(void) { - // ++executor_id; - // return executor_id; - ++global_exec_id_counter; - global_exec_id_counter %= __CQ_DEVICE_QUEUE_SIZE__; - return global_exec_id_counter; -} - -bool is_device(void) { - return mpi_env.rank != CQ_MPI_HOST_RANK; -} - -int is_quantum_worker(void) { - return mpi_env.rank > 0; -} - -MPI_Comm get_quest_comm(void) { - return CQ_MPI_SPLIT_COMM; -} - -void validate_nproc(int nproc) { - const int device_nproc = nproc - 1; - -#if CQ_CONF_QUEST_WITH_MPI - if (!((device_nproc > 0) && ((device_nproc & (device_nproc - 1)) == 0))) { - cq_log( - "Incorrect number of MPI processes. If QuEST was built with MPI, the " - "(N - 1) should be power of " - "2!\n"); - exit(CQ_MPI_RUNTIME_ERROR); - } -#endif -#ifndef CQ_CONF_QUEST_WITH_MPI - cq_log("NPROC: %d\n", nproc); - if (device_nproc != 1) { - cq_log( - "Incorrect number of MPI processes. If QuEST uses multi-threading " - "only, there should be only 2 MPI processes used for CQ!\n"); - exit(CQ_MPI_RUNTIME_ERROR); - } -#endif -} - -#undef CQ_MPI_IMPL_DEBUG -#undef CQ_MPI_HOST_RANK -#undef CQ_MPI_DEVICE_RANK -#undef CQ_MPI_DEVICE_MASTER_RANK -#undef CQ_MPI_WORLD_COMMS_TAG -#undef CQ_MPI_SUBCOMMS_TAG -#undef CQ_MPI_RUNTIME_ERROR -#undef CQ_MPI_MALLOC_ERROR diff --git a/src/host-device/mpi_comms.c.OLD b/src/host-device/mpi_comms.c.OLD deleted file mode 100644 index b948e76..0000000 --- a/src/host-device/mpi_comms.c.OLD +++ /dev/null @@ -1,801 +0,0 @@ -// I think that the comms.h should stay the same (and ideally -// the rest of the code) and if we compile with MPI build, then -// we link against this file. Otherwise against comms.c - -#include "mpi_comms.h" -#include -#include -#include -#include "datatypes.h" -#include "src/device/control.h" -#include "src/host-device/comms.h" -#include "src/host/opcodes.h" - -#include -#include -#include - -static MPI_Comm cq_mpi_comm = MPI_COMM_WORLD; - -struct cq_mpi_env { - int rank; -}; - -static struct cq_mpi_env mpi_env; - -const char* get_comm_source() { - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - return "Host:\t\t"; - } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { - return "Device:\t\t"; - } else { - return ""; - } -} -void print_params_header(struct ctrl_params_header header) { - // if (mpi_env.rank == CQ_MPI_HOST_RANK) { - // printf("Host [header]: "); - // } else { - // printf("Device [header]: "); - // } - switch (header.type) { - case PARAMS_UINT_T: { - printf("%s [header]: type: UINT, ", get_comm_source()); - break; - } - case PARAMS_ALLOC_T: { - // printf("type: ALLOC, "); - printf("%s [header]: type: ALLOC, ", get_comm_source()); - break; - } - case PARAMS_EXEC_T: { - // printf("type: EXEC, "); - printf("%s [header]: type: EXEC, ", get_comm_source()); - break; - } - default: { - break; - } - } - printf(" %d\n", header.params_msg_size); -} - -void print_alloc_params(void* params) { - device_alloc_params* alloc_params = (device_alloc_params*)params; - - printf("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", - get_comm_source(), alloc_params->NQUBITS, alloc_params->qregistry_idx, - alloc_params->status); -} - -void print_op(const enum ctrl_code OP) { - switch (OP) { - case CQ_CTRL_IDLE: { - printf("CQ_CTRL_IDLE"); - break; - } - case CQ_CTRL_ALLOC: { - printf("CQ_CTRL_ALLOC"); - break; - } - case CQ_CTRL_DEALLOC: { - printf("CQ_CTRL_DEALLOC"); - break; - } - case CQ_CTRL_INIT: { - printf("CQ_CTRL_INIT"); - break; - } - case CQ_CTRL_FINALISE: { - printf("CQ_CTRL_FINALISE"); - break; - } - case CQ_CTRL_RUN_QKERNEL: { - printf("CQ_CTRL_RUN_QKERNEL"); - break; - } - case CQ_CTRL_RUN_PQKERNEL: { - printf("CQ_CTRL_RUN_PQKERNEL"); - break; - } - case CQ_CTRL_TEST: { - printf("CQ_CTRL_TEST"); - break; - } - case CQ_CTRL_ABORT: { - printf("CQ_CTRL_ABORT"); - break; - } - default: { - break; - } - } - printf("\n"); -} - -enum ctrl_params_datatype op_type_to_params_type(enum ctrl_code op_type) - -{ - switch (op_type) { - case CQ_CTRL_ALLOC: - return PARAMS_ALLOC_T; - case CQ_CTRL_DEALLOC: - return PARAMS_ALLOC_T; - case CQ_CTRL_INIT: - return PARAMS_UINT_T; - case CQ_CTRL_FINALISE: - return PARAMS_UINT_T; - case CQ_CTRL_RUN_QKERNEL: - return PARAMS_EXEC_T; - case CQ_CTRL_RUN_PQKERNEL: - return PARAMS_EXEC_T; - default: { - printf("Unhandled op type. Returning PARAMS_UINT_T. Expect segfault.\n"); - return PARAMS_UINT_T; - }; - } -} - -size_t static get_params_size(enum ctrl_params_datatype type) { - switch (type) { - case PARAMS_UINT_T: { - return sizeof(unsigned int); - break; - } - case PARAMS_ALLOC_T: { - return sizeof(device_alloc_params); - break; - } - case PARAMS_EXEC_T: { - return 0; - break; - } - default: { - return 0; - break; - } - } -} - -void params_deep_copy(enum ctrl_params_datatype params_type, - void* src, - void** dest) { - const size_t params_size = get_params_size(params_type); - *dest = malloc(params_size); - memcpy(*dest, src, params_size); -} - -// void send_uint_params(void* params) { -// TODO: do the destination parameter -void send_uint_params(struct device_ctrl_params ctrl_params, int dest) { - MPI_Datatype uint_type = - sizeof(unsigned int) == 4 ? MPI_UINT32_T : MPI_UINT64_T; - - struct ctrl_params_header msg_header = { - .type = ctrl_params.type, .params_msg_size = sizeof(unsigned int)}; - int header_size = sizeof(struct ctrl_params_header); - - MPI_Ssend(&msg_header, header_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm); - MPI_Ssend(ctrl_params.data, 1, uint_type, dest, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm); -} - -void recv_uint_params(void** params, int params_size, int src) { - printf("%s Attempting to recieve verbosity param...\n", get_comm_source()); - if (params == NULL) { - printf("From recv_uint_params: params is NULL.\n"); - exit(-2); - } - - // free previous, now unused resources - if (*params != NULL) { - printf("From recv_uint_params: *params is NULL.\n"); - exit(-2); - - free(params); - *params = NULL; - } - - MPI_Status status; - *params = malloc(params_size); - if (*params == NULL) { - printf("From recv_uint_params: malloc failed.\n"); - exit(-2); - } - - MPI_Recv(*params, params_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm, &status); -} - -// void send_alloc_params(void* params) { -void send_alloc_params(struct device_ctrl_params ctrl_params, int dest) { - // TODO: use memcpy because no pointers in alloc params - // - // pack device_alloc_params - // device_alloc_params* alloc_params = - // (device_alloc_params*)ctrl_params.data; int max_buffer_size; - // MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &max_buffer_size); // NQUBITS - // int member_size; - // MPI_Pack_size(1, MPI_UINT64_T, cq_mpi_comm, &member_size); // - // qregistry_idx max_buffer_size += member_size; MPI_Pack_size(1, MPI_INT, - // cq_mpi_comm, &member_size); // status max_buffer_size += member_size; - // - // char* send_buffer = malloc(max_buffer_size); - // if (send_buffer == NULL) { - // printf("Failed to allocate buffer for sending device allocation - // params.\n"); exit(-1); - // } - // int position; - // MPI_Pack(&alloc_params->NQUBITS, 1, MPI_UINT64_T, send_buffer, - // max_buffer_size, &position, cq_mpi_comm); - // MPI_Pack(&alloc_params->qregistry_idx, 1, MPI_UINT64_T, send_buffer, - // max_buffer_size, &position, cq_mpi_comm); - // MPI_Pack(&alloc_params->status, 1, MPI_INT, send_buffer, max_buffer_size, - // &position, cq_mpi_comm); - - // all nicely packed - // struct ctrl_params_header msg_header = {.type = ctrl_params.type, - // .params_msg_size = position}; - - printf("%s Attempting to send device_alloc_params...\n", get_comm_source()); - size_t buffer_size = sizeof(device_alloc_params); - void* send_buffer = malloc(buffer_size); - memcpy(send_buffer, ctrl_params.data, buffer_size); - struct ctrl_params_header msg_header = {.type = ctrl_params.type, - .params_msg_size = buffer_size}; - - int header_size = sizeof(struct ctrl_params_header); - - MPI_Ssend(&msg_header, header_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm); - MPI_Ssend(send_buffer, buffer_size, MPI_BYTE, dest, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm); - - // MPI_Ssend(send_buffer, position, MPI_PACKED, device_rank, - // CQ_HOST_DEVICE_MPI_TAG, - // cq_mpi_comm); - - free(send_buffer); - printf("%s Sent device_alloc_params...\n", get_comm_source()); -} - -void recv_alloc_params(void** params, int params_size, int src) { - printf("%s Attempting to recieve device_alloc_params...\n", - get_comm_source()); - if (params == NULL) { - printf("From recv_alloc_params: params is NULL.\n"); - exit(-2); - } - - // free previous, now unused resources - if (*params != NULL) { - printf("From recv_alloc_params: *params is NULL.\n"); - exit(-2); - free(params); - *params = NULL; - } - - MPI_Status status; - *params = malloc(params_size); - if (*params == NULL) { - printf("From recv_alloc_params: malloc failed.\n"); - exit(-2); - } - - MPI_Recv(*params, params_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm, &status); - printf("%s Recieved device_alloc_params...\n", get_comm_source()); -} - -void send_exec_params(struct device_ctrl_params ctrl_params, int dest) {} -void recv_exec_params(void** params, int params_size, int src) {} - -// void comm_ctrl_params(struct ctrl_params_header msg_header, -// void* params, -// param_packer_fn packer) { -// if (mpi_env.rank == CQ_MPI_HOST_RANK) { -// send_ctrl_params(msg_header, params, packer); -// } else { -// recv_ctrl_params(); -// } -// } - -// void send_ctrl_params(struct ctrl_params_header msg_header, -// void* params, -// param_packer_fn packer) { -// int device_rank = 1; -// size_t header_size = sizeof(struct ctrl_params_header); -// MPI_Ssend(&msg_header, header_size, MPI_BYTE, device_rank, -// CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); -// print_params_header(msg_header); - -void send_ctrl_params(const struct device_ctrl_params* ctrl_params, int dest) { - switch (ctrl_params->type) { - case PARAMS_UINT_T: { - send_uint_params(*ctrl_params, dest); - break; - } - case PARAMS_ALLOC_T: { - send_alloc_params(*ctrl_params, dest); - break; - } - case PARAMS_EXEC_T: { - send_exec_params(*ctrl_params, dest); - break; - } - default: { - break; - } - } -} - -void recv_ctrl_params(void** params, int src) { - struct ctrl_params_header msg_header; - size_t header_size = sizeof(struct ctrl_params_header); - MPI_Status status; - MPI_Recv(&msg_header, header_size, MPI_BYTE, src, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm, &status); - print_params_header(msg_header); - switch (msg_header.type) { - case PARAMS_UINT_T: { - recv_uint_params(params, msg_header.params_msg_size, src); - break; - } - case PARAMS_ALLOC_T: { - recv_alloc_params(params, msg_header.params_msg_size, src); - break; - } - case PARAMS_EXEC_T: { - recv_exec_params(params, msg_header.params_msg_size, src); - break; - } - default: { - break; - } - } -} - -int mpi_initialise_device(const unsigned int VERBOSITY) { - printf("Initialising MPI.\n"); - MPI_Init(NULL, NULL); - printf("Initialised MPI.\n"); - MPI_Comm_rank(MPI_COMM_WORLD, &mpi_env.rank); - - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - if (VERBOSITY > 0) { - printf("Initialising device.\n"); - } - pthread_mutex_init(&dev_ctrl.device_lock, NULL); - pthread_cond_init(&dev_ctrl.cond_device_busy, NULL); - pthread_cond_init(&dev_ctrl.cond_queue_empty, NULL); - pthread_cond_init(&dev_ctrl.cond_queue_full, NULL); - - dev_ctrl.run_device = true; - dev_ctrl.device_busy = true; - dev_ctrl.num_ops = 0; - dev_ctrl.next_op_in = 0; - dev_ctrl.next_op_out = 0; - - for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { - dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; - dev_ctrl.op_params_buffer[i] = NULL; - } - - pthread_create(&dev_ctrl.device_thread, NULL, &mpi_device_control_thread, - NULL); - } - - unsigned int verbosity = VERBOSITY; - // struct ctrl_params_header msg_header = { - // .type = PARAMS_UINT_T, .params_msg_size = sizeof(unsigned int)}; - // comm_ctrl_params(msg_header, &verbosity, &pack_uint_params); - - struct device_ctrl_params params = {.type = PARAMS_UINT_T, - .data = &verbosity}; - mpi_host_comm_ctrl_op(CQ_CTRL_INIT, ¶ms); - printf("Finished: comm_ctrl_op\n"); - mpi_host_wait_all_ops(); - printf("Finished: wait_all_ops\n"); - - return 0; -} - -void mpi_host_comm_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params* ctrl_params) { - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - mpi_host_send_ctrl_op(OP, ctrl_params); - printf("%s [comm_ctrl_op]: finished sending ctrl op\n", get_comm_source()); - } else { - mpi_host_recv_ctrl_op(); - printf("%s [comm_ctrl_op]: finished recv ctrl op\n", get_comm_source()); - } -} - -// TODO: Need to know the size of ctrl_params -// maybe instead have struct like: -// struct CtrlParams { -// void* data; -// int size; -// } -// and wrap it whenever there is call to host_send_ctrl_op -size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params* ctrl_params) { - const int device_rank = CQ_MPI_DEVICE_RANK; - // TODO: merge sends? - MPI_Ssend(&OP, 1, MPI_INT, device_rank, CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - printf("%s [send_ctrl_op]: Sent ", get_comm_source()); - print_op(OP); - send_ctrl_params(ctrl_params, device_rank); - - size_t num_ops = 0; - MPI_Status status; - MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, CQ_HOST_DEVICE_MPI_TAG, - cq_mpi_comm, &status); - printf("%s recv num ops: %zu\n", get_comm_source(), num_ops); - - // alloc and dealloc should be blocking - if (OP == CQ_CTRL_ALLOC || OP == CQ_CTRL_DEALLOC) { - void* params_buffer = NULL; - recv_ctrl_params(¶ms_buffer, device_rank); - memcpy(ctrl_params->data, params_buffer, sizeof(device_alloc_params)); - printf("Performed deep copy\n"); - print_alloc_params(ctrl_params->data); - } - - // rest handled by executor - - return num_ops; -} - -// TODO: rename -- it is device comms thread that runs this -size_t mpi_host_recv_ctrl_op() { - const int host_rank = CQ_MPI_HOST_RANK; - pthread_mutex_lock(&dev_ctrl.device_lock); - - while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { - // the control queue is full! - // we'll wait for it to not be full - pthread_cond_wait(&dev_ctrl.cond_queue_full, &dev_ctrl.device_lock); - } - - MPI_Status status; - MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_INT, host_rank, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm, &status); - printf("%s [recv_ctrl_op]: Recieved ", get_comm_source()); - print_op(dev_ctrl.op_buffer[dev_ctrl.next_op_in]); - - recv_ctrl_params(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in], host_rank); - if (dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_ALLOC || - dev_ctrl.op_buffer[dev_ctrl.next_op_in] == CQ_CTRL_DEALLOC) { - print_alloc_params(dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); - } else { - printf("%s verbosity params: VERBOSITY: %d\n", get_comm_source(), - *(unsigned int*)dev_ctrl.op_params_buffer[dev_ctrl.next_op_in]); - } - - ++dev_ctrl.num_ops; - - // It's a ring buffer! - // advance next_op_in then mod out buffer size - ++dev_ctrl.next_op_in; - dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; - - MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, - CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - printf("%s [recv_ctrl_op]: sent num ops: %zu\n", get_comm_source(), - dev_ctrl.num_ops); - - pthread_cond_signal(&dev_ctrl.cond_queue_empty); - pthread_mutex_unlock(&dev_ctrl.device_lock); - - return dev_ctrl.num_ops; -} - -size_t mpi_host_wait_all_ops(void) { - if (mpi_env.rank == CQ_MPI_HOST_RANK) { - const int device_rank = CQ_MPI_DEVICE_RANK; - size_t num_ops = 0; - MPI_Status status; - printf("%s [wait_all_ops]: got here\n", get_comm_source()); - // rather than waiting for num ops, wait for the ctrl params and update - // recv_ctrl_params(void** params, int src) - // MPI_Recv(&num_ops, 1, MPI_UINT64_T, device_rank, - // CQ_HOST_DEVICE_MPI_TAG, - // cq_mpi_comm, &status); - return num_ops; - - } else { - const int host_rank = CQ_MPI_HOST_RANK; - pthread_mutex_lock(&dev_ctrl.device_lock); - int foo = 0; - while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { - printf("%s [wait_all_ops][loop]: got here %d\n", get_comm_source(), foo); - ++foo; - pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); - } - printf("%s [wait_all_ops]: got here\n", get_comm_source()); - // rather than sending num ops, device_control_thread sends updated params? - // but also maybe I don't want MPI inside device_control_thread - // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, - // CQ_HOST_DEVICE_MPI_TAG, cq_mpi_comm); - - pthread_mutex_unlock(&dev_ctrl.device_lock); - printf("%s leaving wait_ops now\n", get_comm_source()); - return dev_ctrl.num_ops; - } -} - -void* mpi_device_control_thread(void*) { - enum ctrl_code current_op = CQ_CTRL_IDLE; - void* current_op_params = NULL; - - // run_device set to FALSE at cq_finalise - while (dev_ctrl.run_device) { - pthread_mutex_lock(&dev_ctrl.device_lock); - - while (dev_ctrl.num_ops <= 0) { - // wait for a new op to be posted - dev_ctrl.device_busy = false; - pthread_cond_signal(&dev_ctrl.cond_device_busy); - pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); - } - - dev_ctrl.device_busy = true; - - // take the next op and params out of the dev_ctrl buffer, and then tidy - // up the dev_ctrl buffer - current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; - - // TODO: Can solve NOTE 4 (see below) by doing deep copy here... - // and freeing op_params_buffer earlier - // current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; - - params_deep_copy(op_type_to_params_type(current_op), - dev_ctrl.op_params_buffer[dev_ctrl.next_op_out], - ¤t_op_params); - - if (current_op_params == NULL) { - printf("Params are null!\n"); - } - print_alloc_params(current_op_params); - dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; - // release resources allocated in specialisation of recv_ctrl_params - // Alternativally comment this out and handle freeing in those - // specialisations -- see commented out first few lines in recv_uint_params. - free(dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]); // allocated in - // recv_ctrl_params - dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; - - // NOTE:1 -- 01.05.2026: went with freeing in recievers solution - // NOTE:2 -- 01.05.2026: since we have ring buffer, resources will be freed - // on snd pass - // NOTE:3 -- 05.05.2026: actually reverted and went with freeing resources - // here as sometimes I want update params in the reciever (see end of this - // function) - - // const size_t last_op_idx = dev_ctrl.next_op_out; - - // decrease the number of queued operations and advance next_op_out - --dev_ctrl.num_ops; - ++dev_ctrl.next_op_out; - dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; - - // signal that the queue is no longer full and then relinquish mutex - pthread_cond_signal(&dev_ctrl.cond_queue_full); - pthread_mutex_unlock(&dev_ctrl.device_lock); - - control_registry[current_op](current_op_params); - - // TODO: send updated ctrl_params to host - // also consider if this is the right place for the send... - // NOTE:4 -- 05.05.2026: This is quite nasty because: - // a) I don't wont to block thread for control_registry duration - // b) but I need to free the resources after and lock the dev_ctrl - // c) but meanwhile, other threads can modify dev_ctrl... - if (op_type_to_params_type(current_op) != PARAMS_UINT_T) { - struct device_ctrl_params params_to_sync = { - .type = op_type_to_params_type(current_op), - .data = current_op_params}; - print_alloc_params(params_to_sync.data); - const int host_rank = CQ_MPI_HOST_RANK; - send_ctrl_params(¶ms_to_sync, host_rank); - } - free(current_op_params); // allocated in params_deep_copy(); - } - - pthread_mutex_unlock(&dev_ctrl.device_lock); - - return NULL; -} - -int mpi_finalise_device(const unsigned int VERBOSITY) { - // Politely wait for the device to finish its current business - // otherwise setting dev_ctrl.run_device = false might break - // some stuff, and this should only be called in cq_finalise() - printf("%s [finalise_device]: Entered\n", get_comm_source()); - mpi_host_wait_all_ops(); - printf("%s [finalise_device]: passed the host wait all ops\n", - get_comm_source()); - - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - if (VERBOSITY > 0) { - printf("Finalising device.\n"); - } - - pthread_mutex_lock(&dev_ctrl.device_lock); - dev_ctrl.run_device = false; - pthread_mutex_unlock(&dev_ctrl.device_lock); - } - - unsigned int verbosity = VERBOSITY; - struct device_ctrl_params params = {.type = PARAMS_UINT_T, - .data = &verbosity}; - mpi_host_comm_ctrl_op(CQ_CTRL_FINALISE, ¶ms); - - if (mpi_env.rank != CQ_MPI_HOST_RANK) { - pthread_join(dev_ctrl.device_thread, NULL); - - dev_ctrl.device_busy = false; - - pthread_cond_destroy(&dev_ctrl.cond_device_busy); - pthread_cond_destroy(&dev_ctrl.cond_queue_empty); - pthread_cond_destroy(&dev_ctrl.cond_queue_full); - pthread_mutex_destroy(&dev_ctrl.device_lock); - } - - MPI_Finalize(); - return 0; -} -// struct DeviceLink dev_ctrl; -// -// int mpi_initialise_device(const unsigned int VERBOSITY) { -// if (VERBOSITY > 0) { -// printf("Initialising device.\n"); -// } -// -// int nprocs; -// int world_rank; -// MPI_Comm comm_split; -// -// MPI_Init(NULL, NULL); -// -// MPI_Comm_size(MPI_COMM_WORLD, &nprocs); -// MPI_Comm_rank(MPI_COMM_WORLD, &world_rank); -// // int in_quest_comm = world_rank != 0; -// // MPI_Comm_split(MPI_COMM_WORLD, I_AM_QUANTUM, world_rank, &comm_split); -// -// dev_ctrl.run_device = true; -// dev_ctrl.device_busy = true; -// dev_ctrl.num_ops = 0; -// dev_ctrl.next_op_in = 0; -// dev_ctrl.next_op_out = 0; -// -// for (size_t i = 0; i < __CQ_DEVICE_QUEUE_SIZE__; ++i) { -// dev_ctrl.op_buffer[i] = CQ_CTRL_IDLE; -// dev_ctrl.op_params_buffer[i] = NULL; -// } -// -// unsigned int verbosity = VERBOSITY; -// mpi_host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); -// mpi_host_wait_all_ops(); -// -// return 0; -// } -// -// size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, void* ctrl_params) { -// // 1. need to request from device queue size... -// // 2. send OP for device op_buffer -// int rank; -// MPI_Comm_rank(MPI_COMM_WORLD, &rank); -// if (rank == 0) { -// MPI_Status status; -// int device_rank = 1; -// -// while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { -// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD, &status); -// } -// MPI_Ssend(&OP, 1, MPI_INT, device_rank, 0, MPI_COMM_WORLD); -// // TODO: this needs solution -// // MPI_Ssend(ctrl_params, ctrl_params_extent, MPI_CHAR, device_rank, 0, -// // MPI_COMM_WORLD); -// -// ++dev_ctrl.num_ops; -// ++dev_ctrl.next_op_in; -// dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; -// -// // NOTE: matched by Recv on lines 120 and 122 -// MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD); -// MPI_Ssend(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD); -// -// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD, -// &status); -// MPI_Recv(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, device_rank, 0, -// MPI_COMM_WORLD, &status); -// -// } else { -// // NOTE: okay, I think it's simply wrong place for this code -// // actually we leave host rank as it is and this code goes to the device -// // code or something -// // int host_rank = 0; -// // MPI_Status status; -// // -// // // NOTE: if the device queue is full this will deadlock -// // // instead we have to call host_send_ctrl_op with a modification -// // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, -// // MPI_COMM_WORLD); -// // -// // MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_UINT64_T, -// // host_rank, 0, MPI_COMM_WORLD, &status); -// // -// // // TODO: this is tricky and needs solving -// // // MPI_Recv(&dev_ctrl.op_params_buffer[dev_ctrl.next_op_in], -// // // ctrl_params_extent, MPI_CHAR, -// // // host_rank, 0, MPI_COMM_WORLD, &status); -// // -// // ++dev_ctrl.num_ops; -// // ++dev_ctrl.next_op_in; -// // dev_ctrl.next_op_in %= __CQ_DEVICE_QUEUE_SIZE__; -// // MPI_Ssend(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, -// // MPI_COMM_WORLD); MPI_Ssend(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, -// // host_rank, 0, -// // MPI_COMM_WORLD); -// // } -// } -// return dev_ctrl.num_ops; -// } -// -// void* mpi_device_control_thread(void* par) { -// enum ctrl_code current_op = CQ_CTRL_IDLE; -// void* current_op_params = NULL; -// int host_rank = 0; -// MPI_Status status; -// -// // run_device set to FALSE at cq_finalise -// while (dev_ctrl.run_device) { -// // pthread_mutex_lock(&dev_ctrl.device_lock); -// -// while (dev_ctrl.num_ops <= 0) { -// // wait for a new op to be posted -// dev_ctrl.device_busy = false; -// MPI_Recv(&dev_ctrl.num_ops, 1, MPI_UINT64_T, host_rank, 0, -// MPI_COMM_WORLD, -// &status); -// MPI_Recv(&dev_ctrl.next_op_in, 1, MPI_UINT64_T, host_rank, 0, -// MPI_COMM_WORLD, &status); -// -// pthread_cond_signal(&dev_ctrl.cond_device_busy); -// pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); -// } -// MPI_Recv(&dev_ctrl.op_buffer[dev_ctrl.next_op_in], 1, MPI_UINT64_T, -// host_rank, 0, MPI_COMM_WORLD, &status); -// -// dev_ctrl.device_busy = true; -// -// // take the next op and params out of the dev_ctrl buffer, and then tidy -// // up the dev_ctrl buffer -// current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; -// current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; -// dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; -// dev_ctrl.op_params_buffer[dev_ctrl.next_op_out] = NULL; -// -// // decrease the number of queued operations and advance next_op_out -// --dev_ctrl.num_ops; -// ++dev_ctrl.next_op_out; -// dev_ctrl.next_op_out %= __CQ_DEVICE_QUEUE_SIZE__; -// -// // signal that the queue is no longer full and then relinquish mutex -// pthread_cond_signal(&dev_ctrl.cond_queue_full); -// pthread_mutex_unlock(&dev_ctrl.device_lock); -// -// control_registry[current_op](current_op_params); -// } -// -// pthread_mutex_unlock(&dev_ctrl.device_lock); -// -// return NULL; -// } diff --git a/src/host-device/mpi_comms.h b/src/host-device/mpi_comms.h deleted file mode 100644 index 01e40d6..0000000 --- a/src/host-device/mpi_comms.h +++ /dev/null @@ -1,208 +0,0 @@ -#ifndef CQ_HOST_DEVICE_MPI_COMMS_H -#define CQ_HOST_DEVICE_MPI_COMMS_H - -#include "datatypes.h" -#include "src/host-device/comms.h" -#include "src/host/opcodes.h" - -#include - -#include - -// device_wait_comms(); -// if (is_quantum_worker()) { -// return CQ_SUCCESS; -// } - -#define RUN_HOST_ONLY() \ - { \ - int rank = get_rank(); \ - if (rank == -1) { \ - return CQ_ERROR; \ - } \ - if (is_device()) { \ - return CQ_SUCCESS; \ - } \ - } - -/// -/// initialises MPI environment both on host and on the device. -/// @param VERBOSITY unsigned integer controlling diagnostic output. -void init_host_device_mpi(const unsigned int VERBOSITY); - -/// -/// finalises MPI environment both on host and on the device. -/// @param VERBOSITY unsigned integer controlling diagnostic output. -void finalise_host_device_mpi(const unsigned int VERBOSITY); - -// ---------------------------------------------------------------------------- -// Host Comm Ops -// ---------------------------------------------------------------------------- - -/// -/// sends the control operation to the device. -/// @param OP an enum argument specifying CQ_CTRL_OP -/// @param[in,out] params void pointer to arbitrary control parameters -void mpi_host_send_ctrl_op(const enum ctrl_code OP, void* params); - -/// -/// blocks and waits for the operations to complete on the device. -void mpi_host_wait_all_ops(void); - -/// -/// wrapper around device_wait_comms, intended to be called from the host. -/// The intended caller of this function is cq_finalise and register_(p)qkern! -void host_device_sync_comms(void); - -// ---------------------------------------------------------------------------- -// Device Comm Ops -// ---------------------------------------------------------------------------- - -/// -/// initialises on-device communication thread responsible for MPI messaging -/// with host. -/// @param VERBOSITY unsigned integer controlling diagnostic output. -void device_init_comms(const unsigned int VERBOSITY); - -/// -/// finalises on-device communication thread responsible for MPI messaging -/// with host. -/// @param VERBOSITY unsigned integer controlling diagnostic output. -void device_finalise_comms(const unsigned int VERBOSITY); - -/// -/// starts listening for the incoming messages from the host. -/// @param args arbitrary arguments to function to satisfy pthread function -/// signature -/// @return arbitrary return data to satisfy pthread function signature. -/// Currently always returns NULL. -void* device_listen(void* args); - -/// -/// dispatches recieved control operation to the worker thread. -/// @param OP an enum argument specifying CQ_CTRL_OP -void device_dispatch_ctrl_op(const enum ctrl_code OP); - -/// -/// blocks device main thread and awaits for the worker to complete. -/// @return number of remaining operations which should be 0 -size_t device_wait_all_ops(void); - -/// -/// blocks device master thread and awaits for the comms to complete. -/// it needs to be called only once at the end to ensure that the main thread -/// does not clean-up and close MPI before work is done on the worker. -void device_wait_comms(void); -// TODO: above can be static and in mpi_comms.c - -// ---------------------------------------------------------------------------- -// Device Control Paramaters Comms -// ---------------------------------------------------------------------------- - -/// -/// communicates control parameters between host and device. -/// @param OP an enum argument specifying CQ_CTRL_OP -/// @param[in,out] params void pointer to arbitrary params -void host_comm_params(const enum ctrl_code OP, void* params); - -/// -/// receives allocation parameters from the source. -/// @param[out] params reference to parameters to store the results of -/// communication -/// @param src source rank of incoming message -void recv_alloc_params(device_alloc_params* params, int src); - -/// -/// sends allocation parameters to the destination. -/// @param[in] params reference to parameters to communicate -/// @param dest destination rank of outgoing message -void send_alloc_params(const device_alloc_params* params, int dest); - -size_t recv_exec_id(const int src); -void send_exec_id(const size_t id, const int dest); - -/// -/// receives executor handle from the source. Also, if called on device, -/// allocates executor in on-device memory, which then needs to be freed using -/// device_free_exec. -/// @param[out] ehp executor handle used for host-device offloading -/// @param src source rank of incoming message -void recv_exec_params(cq_exec** ehp, int src); - -/// sends updated executor handle to the destination. -/// @param[in] ehp executor handle used for host-device offloading -/// @param dest destination rank of outgoing message -void send_exec_params(cq_exec* ehp, int dest); - -/// -/// frees on-device memory pointed to by executor handle. -/// @param[in,out] ehp executor handle used for host-device offloading -void device_free_exec(cq_exec** ehp); - -// ---------------------------------------------------------------------------- -// Helpers -// ---------------------------------------------------------------------------- - -/// -/// returns the name of either host or the device. Used for printing -/// diagnostics. -/// @return name of the device (or host). -const char* get_comm_source(void); - -/// -/// returns the current MPI rank. -/// @return MPI rank of a given process. -int get_rank(void); - -/// -/// converts CQ control code to string. -/// @param OP an enum argument specifying CQ_CTRL_OP -/// @return string represntation of OP. -const char* op_to_str(const enum ctrl_code OP); - -/// -/// prints values of the alloc parameters. -/// @param[in] params reference to parameters -void print_alloc_params(const device_alloc_params* params); - -/// -/// prints members of the executor. -/// @param[in] ehp reference to executor -void print_ehp(const cq_exec* ehp); - -/// -/// generates and assigns id to the executor that is used for host-device -/// communication. -/// @return new executor id -size_t assign_exec_id(void); - -/// -/// check if MPI process is assigned to the device. -/// @return -int is_device(void); - -/// -/// check if MPI process is a quantum worker (not resposible for host-device -/// communication). -/// @return -int is_quantum_worker(void); - -/// -/// accessor to the subcommunicator intended for QuEST simulation. -/// @return MPI Subcommunicator used to initialise QuEST environment -MPI_Comm get_quest_comm(void); - -/// validates the number of MPI processes to meet QuEST constraints. -/// Exits program if check failed. -/// @param nproc number of MPI processes -void validate_nproc(int nproc); - -// ---------------------------------------------------------------------------- -// TODO: Need to go to comms.h and comms.c and use existing stuff to wrap around -// ---------------------------------------------------------------------------- - -size_t comms_exec_sync(cq_exec* const ehp); -size_t comms_exec_wait(cq_exec* const ehp); -void comms_exec_halt(cq_exec* const ehp); - -#endif diff --git a/src/host-device/mpi_comms.h.OLD b/src/host-device/mpi_comms.h.OLD deleted file mode 100644 index 306bb0d..0000000 --- a/src/host-device/mpi_comms.h.OLD +++ /dev/null @@ -1,112 +0,0 @@ -#ifndef HOST_DEVICE_MPI_COMMS_H -#define HOST_DEVICE_MPI_COMMS_H - -#include "datatypes.h" -#include "src/host/opcodes.h" - -#include - -#include -#include - -#define __CQ_DEVICE_QUEUE_SIZE__ 16 -#define __CQ_HOST_DEVICE_MPI_PROC__ 0 -#define CQ_MPI_HOST_RANK 0 -#define CQ_MPI_DEVICE_RANK 1 -#define CQ_HOST_DEVICE_MPI_TAG 0 - -enum ctrl_params_datatype { PARAMS_UINT_T, PARAMS_ALLOC_T, PARAMS_EXEC_T }; - -struct ctrl_params_header { - enum ctrl_params_datatype type; - int params_msg_size; -}; - -struct device_ctrl_params { - enum ctrl_params_datatype type; - void* data; -}; - -const char* get_comm_source(void); -void print_params_header(struct ctrl_params_header header); -void print_alloc_params(void* params); -void print_op(const enum ctrl_code OP); - -typedef void (*param_packer_fn)(void* src, void* dest); - -enum ctrl_params_datatype op_type_to_params_type(enum ctrl_code op_type); - -void params_deep_copy(enum ctrl_params_datatype params_type, - void* src, - void** dest); - -#define HOST_ONLY \ - { \ - int rank; \ - MPI_Comm_rank(MPI_COMM_WORLD, &rank); \ - if (rank != CQ_MPI_HOST_RANK) \ - return CQ_SUCCESS; \ - } - -// void pack_uint_params(void* src, void* dest); -// void pack_alloc_params(void* src, void* dest); -// void pack_exec_params(void* src, void* dest); -// or send_xxx_params and recv_xxx_params -// void send_uint_params(void* params); -// void send_alloc_params(void* params); -// void send_exec_params(void* params); - -// TODO: const what should be const - -// NOTE: currently sending 2 messages, header than the payload -// could do 1 message and first probe the size on the recv side -// but probe might be actually slower -- benchmark? -void send_ctrl_params(const struct device_ctrl_params* ctrl_params, int dest); - -void send_uint_params(struct device_ctrl_params ctrl_params, int dest); - -// TODO: Need to send the updated params back from device to host. -void send_alloc_params(struct device_ctrl_params ctrl_params, int dest); -void send_exec_params(struct device_ctrl_params ctrl_params, int dest); - -void recv_ctrl_params(void** params, int src); -void recv_uint_params(void** params, int params_size, int src); -void recv_alloc_params(void** params, int params_size, int src); -void recv_exec_params(void** params, int params_size, int src); - -// void comm_ctrl_params(struct ctrl_params_header msg_header, -// void* params, -// param_packer_fn packer); -// void send_ctrl_params(struct ctrl_params_header msg_header, -// void* params, -// param_packer_fn packer); - -int mpi_initialise_device(const unsigned int VERBOSITY); - -void mpi_host_comm_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params* ctrl_params); - -size_t mpi_host_send_ctrl_op(const enum ctrl_code OP, - struct device_ctrl_params* ctrl_params); - -// TODO: rename -- it is device comms thread that runs this -size_t mpi_host_recv_ctrl_op(void); - -size_t mpi_host_sync_exec(cq_exec* const ehp); - -size_t mpi_host_wait_exec(cq_exec* const ehp); - -void mpi_host_request_halt(cq_exec* const ehp); - -size_t mpi_host_wait_all_ops(void); - -size_t mpi_device_sync_exec(const cq_status STATUS, - const size_t SHOT, - cstate const* const RESULT, - cq_exec* ehp); - -void* mpi_device_control_thread(void*); - -int mpi_finalise_device(const unsigned int VERBOSITY); - -#endif From 646e0f03f39d15f4bebc6fa9d5b72937929d017a Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 19 May 2026 13:19:38 +0100 Subject: [PATCH 22/38] Removed unused file. --- src/host-device/comms/env.c.CHANGES | 70 ----------------------------- 1 file changed, 70 deletions(-) delete mode 100644 src/host-device/comms/env.c.CHANGES diff --git a/src/host-device/comms/env.c.CHANGES b/src/host-device/comms/env.c.CHANGES deleted file mode 100644 index 6f6210e..0000000 --- a/src/host-device/comms/env.c.CHANGES +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include -#include "datatypes.h" -#include "src/host-device/comms.h" -#include "src/host-device/mpi_comms.h" -#include "opcodes.h" -#include "env.h" - -struct cq_environment cq_env = { - .initialised = false, - .finalised = false -}; - -struct dev_link dev_ctrl; - -cq_status cq_init(const unsigned int VERBOSITY) { - cq_status status = CQ_SUCCESS; - //init_host_device_mpi(VERBOSITY); - //RUN_HOST_ONLY(); - - if (!cq_env.finalised) { - if (!cq_env.initialised) { - if (VERBOSITY > 0) { - printf("Initialising CQ Simulated Backend library. QuEST environment report to follow.\n\n"); - } - - initialise_device(VERBOSITY); - RUN_HOST_ONLY(); - mpi_host_send_ctrl_op(CQ_CTRL_INIT, &VERBOSITY); - mpi_host_wait_all_ops(); - - cq_env.initialised = true; - } else { - if (VERBOSITY > 0) { - printf("CQ-SimBE is already initialised. No need to do it again.\n"); - } - status = CQ_WARNING; - } - } else { - printf("CQ-SimBE cannot be reinitialised once finalised! This would break QuEST.\n"); - status = CQ_ERROR; - } - - return status; -} - -cq_status cq_finalise(const unsigned int VERBOSITY) { - host_device_sync_comms(); - RUN_HOST_ONLY(); - cq_status status = CQ_SUCCESS; - - if (!cq_env.finalised) { - if (VERBOSITY > 0) { - printf("Host finalising\n"); - } - - mpi_host_wait_all_ops(); - mpi_host_send_ctrl_op(CQ_CTRL_FINALISE, &VERBOSITY); - - cq_env.finalised = true; - } else { - if (VERBOSITY > 0) { - printf("CQ-SimBE is already finalised. No need to do it again.\n"); - } - status = CQ_WARNING; - } - finalise_host_device_mpi(VERBOSITY); - return status; -} From b4f7ec9525988cc82b544002ecde7e4ca896561a Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Fri, 22 May 2026 12:29:47 +0100 Subject: [PATCH 23/38] Removed some comments, modified functions signature to use const more. Fixed kernel registration. --- src/host-device/comms.h | 5 - src/host-device/comms/comms_core.c | 51 ++++------ src/host-device/comms/comms_core.h | 6 +- src/host-device/comms/comms_mpi.c | 149 +++++++++++------------------ src/host-device/comms/comms_mpi.h | 10 +- src/host-device/kernel_utils.c | 4 +- 6 files changed, 88 insertions(+), 137 deletions(-) diff --git a/src/host-device/comms.h b/src/host-device/comms.h index f768901..c4a4468 100644 --- a/src/host-device/comms.h +++ b/src/host-device/comms.h @@ -41,13 +41,10 @@ typedef struct device_alloc_params { extern struct dev_link dev_ctrl; -// TODO: pth as in original comms.c int initialise_device(const unsigned int VERBOSITY); -// TODO: pth wrapper around insert_op size_t host_send_ctrl_op(const enum ctrl_code OP, void* ctrl_params); -// TODO: pth wrapper around device_wait_all_ops size_t host_wait_all_ops(void); size_t device_sync_exec(const cq_status STATUS, @@ -55,10 +52,8 @@ size_t device_sync_exec(const cq_status STATUS, cstate const* const RESULT, cq_exec* ehp); -// TODO: Implement for pthreads void host_device_sync_comms(void); -// TODO: pth as in original comms.c int finalise_device(const unsigned int VERBOSITY); /// generates and assigns id to the executor that is used for host-device diff --git a/src/host-device/comms/comms_core.c b/src/host-device/comms/comms_core.c index 79960ee..5803d13 100644 --- a/src/host-device/comms/comms_core.c +++ b/src/host-device/comms/comms_core.c @@ -30,12 +30,7 @@ int init_device_controls(const unsigned int VERBOSITY) { pthread_create(&dev_ctrl.device_thread, NULL, &device_control_thread, NULL); - //unsigned int verbosity = VERBOSITY; - //host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); - //host_wait_all_ops(); - return 0; - } void stop_device(void) { @@ -57,10 +52,7 @@ int finalise_device_controls(const unsigned int VERBOSITY) { return 0; } -// NOTE: aka host_send_ctrl_op from comm.c -// really what I want to use is host_send_ctrl_op implemenetation but it needs -// renaming and wrapping into function called host_send_ctrl_op and depending on -// either MPI or pthread implementation use this in different ctx. +// NOTE: aka host_send_ctrl_op from OG comm.c size_t insert_op(const enum ctrl_code OP, void* ctrl_params) { pthread_mutex_lock(&dev_ctrl.device_lock); @@ -109,10 +101,7 @@ void comms_exec_halt(cq_exec* const ehp) { return; } -// NOTE: aka host_wait_all_ops from comm.c -// really what I want to use is host_wait_all_ops implementation but it needs -// renaming and wrapping into function called host_send_ctrl_op and depending -// on either MPI or pthread implementation use this in different ctx. +// NOTE: aka host_wait_all_ops from OG comm.c size_t device_wait_all_ops(void) { pthread_mutex_lock(&dev_ctrl.device_lock); while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { @@ -123,24 +112,25 @@ size_t device_wait_all_ops(void) { return dev_ctrl.num_ops; } -void * device_control_thread(void * par) { +void* device_control_thread(void* par) { enum ctrl_code current_op = CQ_CTRL_IDLE; - void * current_op_params = NULL; + void* current_op_params = NULL; // run_device set to FALSE at cq_finalise while (dev_ctrl.run_device) { pthread_mutex_lock(&dev_ctrl.device_lock); - while(dev_ctrl.num_ops <= 0) { + while (dev_ctrl.num_ops <= 0) { // wait for a new op to be posted dev_ctrl.device_busy = false; pthread_cond_signal(&dev_ctrl.cond_device_busy); pthread_cond_wait(&dev_ctrl.cond_queue_empty, &dev_ctrl.device_lock); } - + dev_ctrl.device_busy = true; - // take the next op and params out of the dev_ctrl buffer, and then tidy up the dev_ctrl buffer + // take the next op and params out of the dev_ctrl buffer, and then tidy up + // the dev_ctrl buffer current_op = dev_ctrl.op_buffer[dev_ctrl.next_op_out]; current_op_params = dev_ctrl.op_params_buffer[dev_ctrl.next_op_out]; dev_ctrl.op_buffer[dev_ctrl.next_op_out] = CQ_CTRL_IDLE; @@ -156,17 +146,19 @@ void * device_control_thread(void * par) { pthread_mutex_unlock(&dev_ctrl.device_lock); control_registry[current_op](current_op_params); - } - + } + pthread_mutex_unlock(&dev_ctrl.device_lock); return NULL; } -size_t device_sync_exec(const cq_status STATUS, const size_t SHOT, -cstate const * const RESULT, cq_exec * ehp) { +size_t device_sync_exec(const cq_status STATUS, + const size_t SHOT, + cstate const* const RESULT, + cq_exec* ehp) { pthread_mutex_lock(&ehp->lock); - + if (STATUS == CQ_EARLY_SUCCESS) { // generally speaking we should respect the kernel-provided // status code, but CQ_EARLY_SUCCESS really means CQ_SUCCESS @@ -175,19 +167,16 @@ cstate const * const RESULT, cq_exec * ehp) { } else { ehp->status = STATUS; } - + ehp->completed_shots += 1; // copy local result register to exec - cstate * dest_creg = ehp->creg + SHOT * ehp->nmeasure; + cstate* dest_creg = ehp->creg + SHOT * ehp->nmeasure; memcpy(dest_creg, RESULT, ehp->nmeasure * sizeof(cstate)); - + // check if the whole execution is done - if ( - ehp->completed_shots == ehp->expected_shots - || STATUS != CQ_SUCCESS - || ehp->halt - ) { + if (ehp->completed_shots == ehp->expected_shots || STATUS != CQ_SUCCESS || + ehp->halt) { ehp->complete = true; pthread_cond_signal(&(ehp->cond_exec_complete)); } diff --git a/src/host-device/comms/comms_core.h b/src/host-device/comms/comms_core.h index 8eaaf77..a0f7f3e 100644 --- a/src/host-device/comms/comms_core.h +++ b/src/host-device/comms/comms_core.h @@ -12,7 +12,7 @@ void stop_device(void); int finalise_device_controls(const unsigned int VERBOSITY); -size_t insert_op(const enum ctrl_code OP, void* ctrl_params); +size_t insert_op(const enum ctrl_code OP, void* ctrl_params); size_t comms_exec_sync(cq_exec* const ehp); @@ -22,6 +22,6 @@ void comms_exec_halt(cq_exec* const ehp); size_t device_wait_all_ops(void); -void * device_control_thread(void *); - +void* device_control_thread(void*); + #endif diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index f86066e..eda7694 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -1,4 +1,5 @@ #include "comms_mpi.h" +#include #include "comms_core.h" #include "src/host-device/comms.h" @@ -272,24 +273,24 @@ void* device_listen(void* args) { cq_log("%s [device_listen]: Starting Bcast\n", get_comm_source()); // NOTE: doing custom bcast because I have MPI errors on my machine // when calling MPICH Bcast! - MPI_Barrier(CQ_MPI_SPLIT_COMM); - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { - int subcomm_size = 0; - MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); - - for (size_t i = 1; i < subcomm_size; ++i) { - MPI_Ssend(&op_comm_buffer, 1, MPI_INT, i, CQ_MPI_SUBCOMMS_TAG, - CQ_MPI_SPLIT_COMM); - } - } else { - MPI_Status status; - MPI_Recv(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, - CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); - } - MPI_Barrier(CQ_MPI_SPLIT_COMM); + // MPI_Barrier(CQ_MPI_SPLIT_COMM); + // if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + // int subcomm_size = 0; + // MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); + + // for (size_t i = 1; i < subcomm_size; ++i) { + // MPI_Ssend(&op_comm_buffer, 1, MPI_INT, i, CQ_MPI_SUBCOMMS_TAG, + // CQ_MPI_SPLIT_COMM); + // } + //} else { + // MPI_Status status; + // MPI_Recv(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, + // CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); + //} + // MPI_Barrier(CQ_MPI_SPLIT_COMM); - // MPI_Bcast(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, - // CQ_MPI_SPLIT_COMM); + MPI_Bcast(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SPLIT_COMM); cq_log("%s [device_listen]: Finished Bcast\n", get_comm_source()); #endif @@ -312,9 +313,6 @@ void* device_listen(void* args) { return NULL; } -// optionally get params and run op -// by running op I mean modyfing the internal dev_ctrl fields -// and then the worker thread handles the rest. void device_dispatch_ctrl_op(const enum ctrl_code OP) { switch (OP) { case CQ_CTRL_INIT: { @@ -414,8 +412,8 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { exit(CQ_MPI_RUNTIME_ERROR); } // NOTE: This is commented out as it can cause a deadlock - // comms_exec_wait(executor_handles[executor_id]); device_wait_all_ops(); + comms_exec_wait(executor_handles[executor_id]); send_exec_params(executor_handles[executor_id], host_rank); // NOTE: This can be done when allocating new in offload // i.e. clear old one and allocate new one @@ -550,7 +548,7 @@ void host_comm_params(const enum ctrl_code OP, void* params) { } } -void recv_alloc_params(device_alloc_params* params, int src) { +void recv_alloc_params(device_alloc_params* params, const int src) { cq_log("%s [recv_alloc_params]: receiving...\n", get_comm_source()); const size_t params_size = sizeof(device_alloc_params); @@ -571,22 +569,22 @@ void recv_alloc_params(device_alloc_params* params, int src) { if (mpi_env.rank != CQ_MPI_HOST_RANK) { // NOTE: doing custom bcast because I have MPI errors on my machine // when calling MPICH Bcast! - MPI_Barrier(CQ_MPI_SPLIT_COMM); - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { - int subcomm_size = 0; - MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); - - for (size_t i = 1; i < subcomm_size; ++i) { - MPI_Ssend(params, params_size, MPI_BYTE, i, CQ_MPI_SUBCOMMS_TAG, - CQ_MPI_SPLIT_COMM); - } - } else { - MPI_Recv(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, - CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); - } - MPI_Barrier(CQ_MPI_SPLIT_COMM); - // MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, - // CQ_MPI_SPLIT_COMM); + // MPI_Barrier(CQ_MPI_SPLIT_COMM); + // if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + // int subcomm_size = 0; + // MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); + + // for (size_t i = 1; i < subcomm_size; ++i) { + // MPI_Ssend(params, params_size, MPI_BYTE, i, CQ_MPI_SUBCOMMS_TAG, + // CQ_MPI_SPLIT_COMM); + // } + //} else { + // MPI_Recv(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, + // CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); + //} + // MPI_Barrier(CQ_MPI_SPLIT_COMM); + MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SPLIT_COMM); } #endif @@ -594,7 +592,7 @@ void recv_alloc_params(device_alloc_params* params, int src) { print_alloc_params(params); } -void send_alloc_params(const device_alloc_params* params, int dest) { +void send_alloc_params(const device_alloc_params* params, const int dest) { #if CQ_CONF_QUEST_WITH_MPI // device master (rank 0) gets from host from world comm if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || @@ -633,22 +631,22 @@ size_t recv_exec_id(const int src) { if (mpi_env.rank != CQ_MPI_HOST_RANK) { // NOTE: doing custom bcast because I have MPI errors on my machine // when calling MPICH Bcast! - MPI_Barrier(CQ_MPI_SPLIT_COMM); - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { - int subcomm_size = 0; - MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); - - for (size_t i = 1; i < subcomm_size; ++i) { - MPI_Ssend(&id, 1, MPI_UINT64_T, i, CQ_MPI_SUBCOMMS_TAG, - CQ_MPI_SPLIT_COMM); - } - } else { - MPI_Recv(&id, 1, MPI_UINT64_T, CQ_MPI_DEVICE_MASTER_RANK, - CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); - } - MPI_Barrier(CQ_MPI_SPLIT_COMM); - // MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, - // CQ_MPI_SPLIT_COMM); + // MPI_Barrier(CQ_MPI_SPLIT_COMM); + // if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { + // int subcomm_size = 0; + // MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); + + // for (size_t i = 1; i < subcomm_size; ++i) { + // MPI_Ssend(&id, 1, MPI_UINT64_T, i, CQ_MPI_SUBCOMMS_TAG, + // CQ_MPI_SPLIT_COMM); + // } + //} else { + // MPI_Recv(&id, 1, MPI_UINT64_T, CQ_MPI_DEVICE_MASTER_RANK, + // CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); + //} + // MPI_Barrier(CQ_MPI_SPLIT_COMM); + MPI_Bcast(&id, 1, MPI_UINT64_T, CQ_MPI_DEVICE_MASTER_RANK, + CQ_MPI_SPLIT_COMM); } #endif @@ -673,7 +671,7 @@ void send_exec_id(const size_t id, const int dest) { #endif } -void recv_exec_params(cq_exec** ehp, int src) { +void recv_exec_params(cq_exec** ehp, const int src) { // TODO: do validation cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); int msg_size; @@ -701,13 +699,11 @@ void recv_exec_params(cq_exec** ehp, int src) { void* recv_buffer = malloc(msg_size); - // reserve space for all the date + currently unused members + // reserve space for all the data + currently unused members if (*ehp == NULL) { cq_log("%s [recv_exec_params]: *ehp is NULL. Allocating on device.\n", get_comm_source()); - // ehp = (cq_exec*)malloc(msg_size + sizeof(pthread_mutex_t) + - // sizeof(pthread_cond_t) + sizeof(void*)); *ehp = (cq_exec*)malloc(sizeof(cq_exec)); pthread_mutex_init(&(*ehp)->lock, NULL); pthread_cond_init(&(*ehp)->cond_exec_complete, NULL); @@ -774,15 +770,6 @@ void recv_exec_params(cq_exec** ehp, int src) { fname_size *= sizeof(char); const size_t qreg_size = sizeof(qubit) * (*ehp)->nqubits; - // NOTE: creg_size * expected_shots?? or completed_shots? - // host should get completed, device should get expected - // and similarly when sending - // size_t num_shots = 0; - // if (mpi_env.rank == CQ_MPI_HOST_RANK) { - // num_shots = (*ehp)->completed_shots; - // } else { - // num_shots = (*ehp)->expected_shots; - // } const size_t num_shots = (*ehp)->expected_shots; const size_t creg_size = sizeof(cstate) * (*ehp)->nmeasure * num_shots; @@ -826,7 +813,7 @@ void recv_exec_params(cq_exec** ehp, int src) { pthread_mutex_unlock(&(*ehp)->lock); } -void send_exec_params(cq_exec* ehp, int dest) { +void send_exec_params(cq_exec* ehp, const int dest) { // if running with MPI QuEST, we don't need to communicate // with quantum workers because the results from QuEST // (e.g. measurements) should be already synchronised. @@ -872,20 +859,6 @@ void send_exec_params(cq_exec* ehp, int dest) { &member_size); // nmeasure max_buffer_size += member_size; - // ------------------------------------------------------------------------- - // TODO: sending pthred stuff doesn't sound like a good idea... - // sounds like UB - // const size_t pthread_mutex_size = sizeof(pthread_mutex_t); - // MPI_Pack_size(pthread_mutex_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - // &member_size); // lock - // max_buffer_size += member_size; - // - // const size_t pthread_cond_size = sizeof(pthread_cond_t); - // MPI_Pack_size(pthread_cond_size, MPI_BYTE, CQ_MPI_COMM_WORLD, - // &member_size); // cond_exec_complete - // max_buffer_size += member_size; - // ------------------------------------------------------------------------- - size_t fname_size = 0; if (ehp->fname != NULL) { fname_size = strlen(ehp->fname) + 1; @@ -902,14 +875,6 @@ void send_exec_params(cq_exec* ehp, int dest) { &member_size); // qreg max_buffer_size += member_size; - // NOTE: creg_size * expected_shots?? or completed_shots? - // host should send expected, device should send completed - // size_t num_shots = 0; - // if (mpi_env.rank == CQ_MPI_HOST_RANK) { - // num_shots = ehp->expected_shots; - //} else { - // num_shots = ehp->completed_shots; - //} const size_t num_shots = ehp->expected_shots; const size_t creg_size = sizeof(cstate) * ehp->nmeasure * num_shots; @@ -1133,7 +1098,7 @@ MPI_Comm get_quest_comm(void) { return CQ_MPI_SPLIT_COMM; } -void validate_nproc(int nproc) { +void validate_nproc(const int nproc) { const int device_nproc = nproc - 1; #if CQ_CONF_QUEST_WITH_MPI if (!((device_nproc > 0) && ((device_nproc & (device_nproc - 1)) == 0))) { diff --git a/src/host-device/comms/comms_mpi.h b/src/host-device/comms/comms_mpi.h index 456f891..ec412ff 100644 --- a/src/host-device/comms/comms_mpi.h +++ b/src/host-device/comms/comms_mpi.h @@ -70,13 +70,13 @@ void host_comm_params(const enum ctrl_code OP, void* params); /// @param[out] params reference to parameters to store the results of /// communication /// @param src source rank of incoming message -void recv_alloc_params(device_alloc_params* params, int src); +void recv_alloc_params(device_alloc_params* params, const int src); /// /// sends allocation parameters to the destination. /// @param[in] params reference to parameters to communicate /// @param dest destination rank of outgoing message -void send_alloc_params(const device_alloc_params* params, int dest); +void send_alloc_params(const device_alloc_params* params, const int dest); /// /// receives executor id used for matching results with correct executor. @@ -96,12 +96,12 @@ void send_exec_id(const size_t id, const int dest); /// device_free_exec. /// @param[out] ehp executor handle used for host-device offloading /// @param src source rank of incoming message -void recv_exec_params(cq_exec** ehp, int src); +void recv_exec_params(cq_exec** ehp, const int src); /// sends updated executor handle to the destination. /// @param[in] ehp executor handle used for host-device offloading /// @param dest destination rank of outgoing message -void send_exec_params(cq_exec* ehp, int dest); +void send_exec_params(cq_exec* ehp, const int dest); /// /// frees on-device memory pointed to by executor handle. @@ -154,6 +154,6 @@ MPI_Comm get_quest_comm(void); /// validates the number of MPI processes to meet QuEST constraints. /// Exits program if check failed. /// @param nproc number of MPI processes -void validate_nproc(int nproc); +void validate_nproc(const int nproc); #endif diff --git a/src/host-device/kernel_utils.c b/src/host-device/kernel_utils.c index c2b9562..a244315 100644 --- a/src/host-device/kernel_utils.c +++ b/src/host-device/kernel_utils.c @@ -33,7 +33,9 @@ cq_status register_qkern(qkern kernel) { } } - host_device_sync_comms(); + //host_device_sync_comms(); + RUN_HOST_ONLY(); + host_wait_all_ops(); return status; } From 34b635b0ea82d4de6373f24a166c9930fedb12c5 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Fri, 22 May 2026 12:47:21 +0100 Subject: [PATCH 24/38] Added support for sending params with executor. --- include/datatypes.h | 1 + src/host-device/comms/comms_mpi.c | 30 +++++++++++++++++++++++------- src/host-device/kernel_utils.c | 1 + 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/include/datatypes.h b/include/datatypes.h index f44ed30..d678143 100644 --- a/include/datatypes.h +++ b/include/datatypes.h @@ -36,6 +36,7 @@ typedef struct exec { size_t completed_shots; size_t expected_shots; size_t nmeasure; + size_t params_size; pthread_mutex_t lock; pthread_cond_t cond_exec_complete; char * fname; diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index eda7694..bd4ed40 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -763,6 +763,8 @@ void recv_exec_params(cq_exec** ehp, const int src) { MPI_UINT64_T, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->nmeasure, 1, MPI_UINT64_T, CQ_MPI_COMM_WORLD); + MPI_Unpack(recv_buffer, msg_size, &position, &(*ehp)->params_size, 1, + MPI_UINT64_T, CQ_MPI_COMM_WORLD); size_t fname_size = 0; MPI_Unpack(recv_buffer, msg_size, &position, &fname_size, 1, MPI_UINT64_T, @@ -772,6 +774,7 @@ void recv_exec_params(cq_exec** ehp, const int src) { const size_t qreg_size = sizeof(qubit) * (*ehp)->nqubits; const size_t num_shots = (*ehp)->expected_shots; const size_t creg_size = sizeof(cstate) * (*ehp)->nmeasure * num_shots; + const size_t params_size = (*ehp)->params_size; // when on host the resources are already allocated! if (mpi_env.rank != CQ_MPI_HOST_RANK) { @@ -795,6 +798,13 @@ void recv_exec_params(cq_exec** ehp, const int src) { get_comm_source()); exit(CQ_MPI_MALLOC_ERROR); } + + (*ehp)->params = malloc(params_size); + if ((*ehp)->params == NULL) { + cq_log("%s [recv_exec_params]: malloc *ehp->params failed. Exiting\n", + get_comm_source()); + exit(CQ_MPI_MALLOC_ERROR); + } } MPI_Unpack(recv_buffer, msg_size, &position, (*ehp)->fname, fname_size, @@ -803,8 +813,8 @@ void recv_exec_params(cq_exec** ehp, const int src) { MPI_BYTE, CQ_MPI_COMM_WORLD); MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->creg, creg_size, MPI_BYTE, CQ_MPI_COMM_WORLD); - - // NOTE: recv ehp->params left for another day... it's for pqkerns + MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->params, + params_size, MPI_BYTE, CQ_MPI_COMM_WORLD); free(recv_buffer); @@ -858,6 +868,8 @@ void send_exec_params(cq_exec* ehp, const int dest) { MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, &member_size); // nmeasure max_buffer_size += member_size; + MPI_Pack_size(1, MPI_UINT64_T, CQ_MPI_COMM_WORLD, &member_size); + max_buffer_size += member_size; // params_size size_t fname_size = 0; if (ehp->fname != NULL) { @@ -882,7 +894,9 @@ void send_exec_params(cq_exec* ehp, const int dest) { &member_size); // creg max_buffer_size += member_size; - // NOTE: sending ehp->params left for another day... it's for pqkerns + const size_t params_size = ehp->params_size; + MPI_Pack_size(params_size, MPI_BYTE, CQ_MPI_COMM_WORLD, &member_size); + max_buffer_size += member_size; // params void* send_buffer = malloc(max_buffer_size); if (send_buffer == NULL) { @@ -910,6 +924,8 @@ void send_exec_params(cq_exec* ehp, const int dest) { max_buffer_size, &position, CQ_MPI_COMM_WORLD); MPI_Pack(&ehp->nmeasure, 1, MPI_UINT64_T, send_buffer, max_buffer_size, &position, CQ_MPI_COMM_WORLD); + MPI_Pack(&ehp->params_size, 1, MPI_UINT64_T, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); // NOTE: skip pthread stuff... @@ -922,8 +938,8 @@ void send_exec_params(cq_exec* ehp, const int dest) { &position, CQ_MPI_COMM_WORLD); MPI_Pack(ehp->creg, creg_size, MPI_BYTE, send_buffer, max_buffer_size, &position, CQ_MPI_COMM_WORLD); - - // NOTE: ehp->prams left for another day + MPI_Pack(ehp->params, params_size, MPI_BYTE, send_buffer, max_buffer_size, + &position, CQ_MPI_COMM_WORLD); MPI_Ssend(&position, 1, MPI_INT, dest, CQ_MPI_WORLD_COMMS_TAG, CQ_MPI_COMM_WORLD); @@ -1072,11 +1088,11 @@ void print_ehp(const cq_exec* ehp) { "STATUS: " "%d\nNQUBITS: " "%zu, completed_shots: %zu, expected_shots: %zu, NMEASURE: " - "%zu\nfname: " + "%zu, params_size: %zu\nfname: " "%s\nqreg:\n", get_comm_source(), ehp->id, ehp->exec_init, ehp->complete, ehp->halt, ehp->status, ehp->nqubits, ehp->completed_shots, ehp->expected_shots, - ehp->nmeasure, ehp->fname); + ehp->nmeasure, ehp->params_size, ehp->fname); for (size_t i = 0; i < ehp->nqubits; ++i) { cq_log("qubit[%zu]: reg_idx: %zu, offset: %zu, N: %zu\n", i, diff --git a/src/host-device/kernel_utils.c b/src/host-device/kernel_utils.c index a244315..739bee9 100644 --- a/src/host-device/kernel_utils.c +++ b/src/host-device/kernel_utils.c @@ -121,6 +121,7 @@ void init_exec_handle(const size_t NQUBITS, const size_t NSHOTS, const size_t NM ehp->completed_shots = 0; ehp->expected_shots = NSHOTS; ehp->nmeasure = NMEASURE; + ehp->params_size = 0; ehp->fname = NULL; ehp->qreg = NULL; ehp->creg = NULL; From e135f29f6b01366fd09b9597e738f4b6f0017e7f Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Wed, 17 Jun 2026 14:44:32 +0100 Subject: [PATCH 25/38] Added extra input validation in comms. --- src/host-device/comms/comms_core.c | 11 +++++ src/host-device/comms/comms_mpi.c | 74 ++++++++++++++++++++++++++++-- 2 files changed, 80 insertions(+), 5 deletions(-) diff --git a/src/host-device/comms/comms_core.c b/src/host-device/comms/comms_core.c index 5803d13..8fd21da 100644 --- a/src/host-device/comms/comms_core.c +++ b/src/host-device/comms/comms_core.c @@ -78,6 +78,9 @@ size_t insert_op(const enum ctrl_code OP, void* ctrl_params) { } size_t comms_exec_sync(cq_exec* const ehp) { + if (ehp == NULL) { + return 0; + } size_t completed_shots = 0; pthread_mutex_lock(&(ehp->lock)); completed_shots = ehp->completed_shots; @@ -86,6 +89,10 @@ size_t comms_exec_sync(cq_exec* const ehp) { } size_t comms_exec_wait(cq_exec* const ehp) { + if (ehp == NULL) { + return 0; + } + pthread_mutex_lock(&(ehp->lock)); while (!ehp->complete) { pthread_cond_wait(&(ehp->cond_exec_complete), &(ehp->lock)); @@ -95,6 +102,10 @@ size_t comms_exec_wait(cq_exec* const ehp) { } void comms_exec_halt(cq_exec* const ehp) { + if (ehp == NULL) { + return; + } + pthread_mutex_lock(&(ehp->lock)); ehp->halt = true; pthread_mutex_unlock(&(ehp->lock)); diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index bd4ed40..bf3beae 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -42,13 +42,15 @@ struct cq_mpi_env { int rank; int subcomm_rank; unsigned int verbosity; + int world_size; }; static cq_exec* executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; static size_t num_active_executors = 0; static struct cq_mpi_env mpi_env = {.rank = -1, .subcomm_rank = -1, - .verbosity = 0}; + .verbosity = 0, + .world_size = 0}; struct communicator { bool comm_busy; @@ -69,6 +71,15 @@ static void cq_log(const char* format, ...) { #endif } +static bool cq_validate_mpi_rank(const int rank) { + if (rank < 0 || rank >= mpi_env.world_size) { + cq_log( + "passed incorrect mpi rank with value either < 0 or >= world_size\n"); + return false; + } + return true; +} + int initialise_device(const unsigned int VERBOSITY) { init_host_device_mpi(VERBOSITY); RUN_HOST_ONLY(); @@ -161,16 +172,14 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { cq_log("Initialising MPI.\n"); } - int nprocs; - MPI_Init(NULL, NULL); - MPI_Comm_size(CQ_MPI_COMM_WORLD, &nprocs); + MPI_Comm_size(CQ_MPI_COMM_WORLD, &mpi_env.world_size); MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); // TODO: for multi-device check that: // (nproc - 1) == n_device * power of 2 - validate_nproc(nprocs); + validate_nproc(mpi_env.world_size); #if CQ_CONF_QUEST_WITH_MPI const int QUANTUM_WORKER = mpi_env.rank > 0; @@ -551,6 +560,12 @@ void host_comm_params(const enum ctrl_code OP, void* params) { void recv_alloc_params(device_alloc_params* params, const int src) { cq_log("%s [recv_alloc_params]: receiving...\n", get_comm_source()); + if (!cq_validate_mpi_rank(src)) { + cq_log("%s [recv_exec_params]: encountered input error\n", + get_comm_source()); + return; + } + const size_t params_size = sizeof(device_alloc_params); MPI_Status status; @@ -593,6 +608,17 @@ void recv_alloc_params(device_alloc_params* params, const int src) { } void send_alloc_params(const device_alloc_params* params, const int dest) { + if (params == NULL) { + cq_log("%s [recv_exec_params]: passed null params\n", get_comm_source()); + return; + } + + if (!cq_validate_mpi_rank(dest)) { + cq_log("%s [recv_exec_params]: encountered input error\n", + get_comm_source()); + return; + } + #if CQ_CONF_QUEST_WITH_MPI // device master (rank 0) gets from host from world comm if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || @@ -613,6 +639,12 @@ void send_alloc_params(const device_alloc_params* params, const int dest) { size_t recv_exec_id(const int src) { cq_log("%s [recv_exec_id]: receiving...\n", get_comm_source()); + if (!cq_validate_mpi_rank(src)) { + cq_log("%s [recv_exec_params]: encountered input error\n", + get_comm_source()); + return -1; + } + size_t id = -1; MPI_Status status; @@ -655,6 +687,12 @@ size_t recv_exec_id(const int src) { } void send_exec_id(const size_t id, const int dest) { + if (!cq_validate_mpi_rank(dest)) { + cq_log("%s [recv_exec_params]: encountered input error\n", + get_comm_source()); + return; + } + #if CQ_CONF_QUEST_WITH_MPI // device master (rank 0) gets from host from world comm if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || @@ -674,6 +712,18 @@ void send_exec_id(const size_t id, const int dest) { void recv_exec_params(cq_exec** ehp, const int src) { // TODO: do validation cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); + + if (ehp == NULL) { + cq_log("%s [recv_exec_params]: passed null ehp\n", get_comm_source()); + return; + } + + if (!cq_validate_mpi_rank(src)) { + cq_log("%s [recv_exec_params]: encountered input error\n", + get_comm_source()); + return; + } + int msg_size; MPI_Status status; @@ -824,6 +874,17 @@ void recv_exec_params(cq_exec** ehp, const int src) { } void send_exec_params(cq_exec* ehp, const int dest) { + if (ehp == NULL) { + cq_log("%s [recv_exec_params]: passed null ehp\n", get_comm_source()); + return; + } + + if (!cq_validate_mpi_rank(dest)) { + cq_log("%s [recv_exec_params]: encountered input error\n", + get_comm_source()); + return; + } + // if running with MPI QuEST, we don't need to communicate // with quantum workers because the results from QuEST // (e.g. measurements) should be already synchronised. @@ -1073,6 +1134,9 @@ const char* op_to_str(const enum ctrl_code OP) { } void print_alloc_params(const device_alloc_params* params) { + if (params == NULL) { + return; + } cq_log("%s alloc params: NQUBITS: %zu, qreg_idx: %zu, STATUS: %d\n", get_comm_source(), params->NQUBITS, params->qregistry_idx, params->status); From 5b7484fd722ea6145f0c2d2c0e040131728ba825 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 30 Jun 2026 12:56:14 +0100 Subject: [PATCH 26/38] Removed commented out code. Added logic for handling parameterised quantum kernels. --- src/host-device/comms/comms_mpi.c | 70 +++++++------------------------ 1 file changed, 16 insertions(+), 54 deletions(-) diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index bf3beae..0fb9933 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -280,24 +280,6 @@ void* device_listen(void* args) { // rank 0 brodcast to rest of q-workers then all do the dispatch } cq_log("%s [device_listen]: Starting Bcast\n", get_comm_source()); - // NOTE: doing custom bcast because I have MPI errors on my machine - // when calling MPICH Bcast! - // MPI_Barrier(CQ_MPI_SPLIT_COMM); - // if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { - // int subcomm_size = 0; - // MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); - - // for (size_t i = 1; i < subcomm_size; ++i) { - // MPI_Ssend(&op_comm_buffer, 1, MPI_INT, i, CQ_MPI_SUBCOMMS_TAG, - // CQ_MPI_SPLIT_COMM); - // } - //} else { - // MPI_Status status; - // MPI_Recv(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, - // CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); - //} - // MPI_Barrier(CQ_MPI_SPLIT_COMM); - MPI_Bcast(&op_comm_buffer, 1, MPI_INT, CQ_MPI_DEVICE_MASTER_RANK, CQ_MPI_SPLIT_COMM); cq_log("%s [device_listen]: Finished Bcast\n", get_comm_source()); @@ -406,8 +388,21 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { break; } case CQ_CTRL_RUN_PQKERNEL: { - // recv params - // insert_op + // Same logic as in CQ_CTRL_RUN_QKERNEL + if (num_active_executors >= __CQ_DEVICE_QUEUE_SIZE__) { + cq_log( + "%s [dispatch][RUN_QKERNEL]: You have oversubsribed the executor " + "queue. We allow up to %d " + "concurrent executors per device. Exiting", + get_comm_source(), __CQ_DEVICE_QUEUE_SIZE__); + exit(CQ_MPI_RUNTIME_ERROR); + } + const int host_rank = CQ_MPI_HOST_RANK; + cq_exec* tmp_exec = NULL; + recv_exec_params(&tmp_exec, host_rank); + executor_handles[tmp_exec->id] = tmp_exec; + insert_op(OP, executor_handles[tmp_exec->id]); + ++num_active_executors; break; } case CQ_CTRL_WAIT_EXEC: { @@ -420,7 +415,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { get_comm_source()); exit(CQ_MPI_RUNTIME_ERROR); } - // NOTE: This is commented out as it can cause a deadlock + // NOTE: This part needs further tests to ensure no deadlock device_wait_all_ops(); comms_exec_wait(executor_handles[executor_id]); send_exec_params(executor_handles[executor_id], host_rank); @@ -582,22 +577,6 @@ void recv_alloc_params(device_alloc_params* params, const int src) { } // device-rank 0 brodcast params to rest of q-workers if (mpi_env.rank != CQ_MPI_HOST_RANK) { - // NOTE: doing custom bcast because I have MPI errors on my machine - // when calling MPICH Bcast! - // MPI_Barrier(CQ_MPI_SPLIT_COMM); - // if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { - // int subcomm_size = 0; - // MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); - - // for (size_t i = 1; i < subcomm_size; ++i) { - // MPI_Ssend(params, params_size, MPI_BYTE, i, CQ_MPI_SUBCOMMS_TAG, - // CQ_MPI_SPLIT_COMM); - // } - //} else { - // MPI_Recv(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, - // CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); - //} - // MPI_Barrier(CQ_MPI_SPLIT_COMM); MPI_Bcast(params, params_size, MPI_BYTE, CQ_MPI_DEVICE_MASTER_RANK, CQ_MPI_SPLIT_COMM); } @@ -661,22 +640,6 @@ size_t recv_exec_id(const int src) { } // device-rank 0 brodcast params to rest of q-workers if (mpi_env.rank != CQ_MPI_HOST_RANK) { - // NOTE: doing custom bcast because I have MPI errors on my machine - // when calling MPICH Bcast! - // MPI_Barrier(CQ_MPI_SPLIT_COMM); - // if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK) { - // int subcomm_size = 0; - // MPI_Comm_size(CQ_MPI_SPLIT_COMM, &subcomm_size); - - // for (size_t i = 1; i < subcomm_size; ++i) { - // MPI_Ssend(&id, 1, MPI_UINT64_T, i, CQ_MPI_SUBCOMMS_TAG, - // CQ_MPI_SPLIT_COMM); - // } - //} else { - // MPI_Recv(&id, 1, MPI_UINT64_T, CQ_MPI_DEVICE_MASTER_RANK, - // CQ_MPI_SUBCOMMS_TAG, CQ_MPI_SPLIT_COMM, &status); - //} - // MPI_Barrier(CQ_MPI_SPLIT_COMM); MPI_Bcast(&id, 1, MPI_UINT64_T, CQ_MPI_DEVICE_MASTER_RANK, CQ_MPI_SPLIT_COMM); } @@ -710,7 +673,6 @@ void send_exec_id(const size_t id, const int dest) { } void recv_exec_params(cq_exec** ehp, const int src) { - // TODO: do validation cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); if (ehp == NULL) { From 7e9ccd5577b323dce6db043474bb268c33eadbc7 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 13 Jul 2026 15:33:25 +0100 Subject: [PATCH 27/38] Added macros for wrapping up code when running MPI built. Updated examples. [WIP]: Added fallback mechanism when running with 1 proc (requires test on cluster). --- examples/qft/aqft.c | 27 ++-- examples/qft/sqft.c | 18 +-- examples/vqe/vqe.c | 47 +++---- examples/vqe/vqe.h | 19 ++- examples/vqe/vqe_utils.c | 37 +++--- include/comms_utils.h | 11 ++ include/cq.h | 5 +- include/env.h | 9 +- include/host_ops.h | 192 +++++++++++++++++++-------- include/utils.h | 27 ++-- src/host-device/comms.h | 20 ++- src/host-device/comms/comms.c | 50 ++----- src/host-device/comms/comms_core.c | 77 +++++++++-- src/host-device/comms/comms_core.h | 20 ++- src/host-device/comms/comms_mpi.c | 204 ++++++++++++++++++++--------- src/host-device/kernel_utils.c | 30 ++++- src/host/host_ops.c | 197 ++++++++++++++++++++++++---- 17 files changed, 681 insertions(+), 309 deletions(-) create mode 100644 include/comms_utils.h diff --git a/examples/qft/aqft.c b/examples/qft/aqft.c index 863ab6f..732006d 100644 --- a/examples/qft/aqft.c +++ b/examples/qft/aqft.c @@ -1,11 +1,12 @@ -#include -#include -#include #include "qft.h" + #include "cq.h" -int main (void) -{ +#include +#include +#include + +int main(void) { const size_t NQUBITS = 10; const size_t NSHOTS = 10; const size_t NMEASURE = NQUBITS; @@ -14,6 +15,11 @@ int main (void) cq_init(0); + register_qkern(zero_init_full_qft); + register_qkern(plus_init_full_qft); + + CQ_PROG_BEGIN() + // We will reuse the quantum buffer as the quantum // kernels cannot run simultaneously (for now...) qubit * qr = NULL; @@ -25,29 +31,28 @@ int main (void) init_creg(NMEASURE * NSHOTS, -1, cr_zero); init_creg(NMEASURE * NSHOTS, -1, cr_plus); - register_qkern(zero_init_full_qft); - register_qkern(plus_init_full_qft); - printf("Offloading both QFT circuits to the quantum device.\n"); am_qrun(zero_init_full_qft, qr, NQUBITS, cr_zero, NMEASURE, NSHOTS, &eh_zero); am_qrun(plus_init_full_qft, qr, NQUBITS, cr_plus, NMEASURE, NSHOTS, &eh_plus); printf("Hello from the host, pretend I'm doing something useful!\n"); sleep(2); - printf("Hello again, I'm done being 'useful' and will now wait for "); + printf("Hello again, I'm done being 'useful' and will now wait for "); printf("the quantum device to return!\n"); wait_qrun(&eh_zero); printf("Results from zero-initialised QFT:\n"); report_results(cr_zero, NMEASURE, NSHOTS); - wait_qrun(&eh_plus); + wait_qrun(&eh_plus); printf("Results from plus-initialised QFT:\n"); report_results(cr_plus, NMEASURE, NSHOTS); free_qureg(&qr); + CQ_PROG_END() + cq_finalise(0); return 0; -} \ No newline at end of file +} diff --git a/examples/qft/sqft.c b/examples/qft/sqft.c index c5db0bd..7e49a2b 100644 --- a/examples/qft/sqft.c +++ b/examples/qft/sqft.c @@ -1,16 +1,19 @@ -#include #include -#include "qft.h" +#include #include "cq.h" +#include "qft.h" -int main (void) -{ +int main(void) { const size_t NQUBITS = 10; const size_t NSHOTS = 10; const size_t NMEASURE = NQUBITS; cq_init(0); + register_qkern(zero_init_full_qft); + register_qkern(plus_init_full_qft); + CQ_PROG_BEGIN(); + qubit * qr = NULL; alloc_qureg(&qr, NQUBITS); @@ -18,9 +21,6 @@ int main (void) cr = malloc(NMEASURE * NSHOTS * sizeof(cstate)); init_creg(NMEASURE * NSHOTS, -1, cr); - register_qkern(zero_init_full_qft); - register_qkern(plus_init_full_qft); - printf("Running first QFT circuit on quantum device.\n"); sm_qrun(zero_init_full_qft, qr, NQUBITS, cr, NMEASURE, NSHOTS); report_results(cr, NMEASURE, NSHOTS); @@ -32,7 +32,9 @@ int main (void) free_qureg(&qr); free(cr); + CQ_PROG_END(); + cq_finalise(0); return 0; -} \ No newline at end of file +} diff --git a/examples/vqe/vqe.c b/examples/vqe/vqe.c index 778dc06..ba55c06 100644 --- a/examples/vqe/vqe.c +++ b/examples/vqe/vqe.c @@ -1,32 +1,26 @@ -#include "cq.h" #include "vqe.h" +#include "cq.h" #include #include -hamiltonian h2_hamil = { - .paulis = { - 'I', 'I', - 'I', 'Z', - 'Z', 'I', - 'Z', 'Z', - 'X', 'X' }, - .coeffs = { - -1.052373245772859, - 0.39793742484318045, - -0.39793742484318045, - -0.01128010425623538, - 0.18093119978423156}, - .term_start_idx = 0 -}; - -int main (void) { +hamiltonian h2_hamil + = { .paulis = { 'I', 'I', 'I', 'Z', 'Z', 'I', 'Z', 'Z', 'X', 'X' }, + .coeffs + = { -1.052373245772859, 0.39793742484318045, -0.39793742484318045, + -0.01128010425623538, 0.18093119978423156 }, + .term_start_idx = 0 }; + +int main(void) { const double TRUE_ENERGY = -1.85728; const size_t NQUBITS = 2; const size_t NSHOTS = 2000; const size_t NMEASURE = NQUBITS; cq_init(0); + register_pqkern(ansatz); + + CQ_PROG_BEGIN(); qubit * qr = NULL; alloc_qureg(&qr, NQUBITS); @@ -35,19 +29,18 @@ int main (void) { cr = malloc(NMEASURE * NSHOTS * sizeof(cstate)); init_creg(NMEASURE * NSHOTS, -1, cr); - register_qkern(ansatz); - double energy = vqe_optimize(qr, cr, NQUBITS, NMEASURE, NSHOTS); - printf("FCI Energy: %f\n" - "VQE Energy: %f\n" - "VQE Error: %f\n", - TRUE_ENERGY, - energy, - TRUE_ENERGY - energy); - + printf( + "FCI Energy: %f\n" + "VQE Energy: %f\n" + "VQE Error: %f\n", + TRUE_ENERGY, energy, TRUE_ENERGY - energy); + free_qureg(&qr); free(cr); + CQ_PROG_END(); + cq_finalise(0); return 0; diff --git a/examples/vqe/vqe.h b/examples/vqe/vqe.h index 5be006c..868248b 100644 --- a/examples/vqe/vqe.h +++ b/examples/vqe/vqe.h @@ -9,25 +9,22 @@ #define M_PI 3.141592653589793238462643383 #endif -typedef struct vqe_settings { - qubit * qr; - cstate * cr; - size_t NQUBITS; - size_t NSHOTS; -} vqe_settings; - #define NLAYERS 1 #define MAX_NQUBITS 2 #define NPARAMS NLAYERS * MAX_NQUBITS #define PARAM_MIN 0.0 #define PARAM_MAX 2.0 * M_PI -typedef struct vqe_context { +typedef struct vqe_settings { + qubit * qr; + cstate * cr; + size_t NQUBITS; + size_t NSHOTS; double prev_energy; double params[NPARAMS]; double prev_params[NPARAMS]; ptrdiff_t iter; -} vqe_context; +} vqe_settings; #define NTERMS 5 #define NPAULIS MAX_NQUBITS * NTERMS @@ -40,7 +37,7 @@ typedef struct hamiltonian { extern hamiltonian h2_hamil; void init_hf_state(qubit * qr, int num_spin_orbitals); -cq_status ansatz(const size_t NQUBITS, qubit * qr, const size_t NMEASURE, cstate * cr, qkern_map * reg); +cq_status ansatz(const size_t NQUBITS, qubit * qr, const size_t NMEASURE, cstate * cr, void * kernpar, pqkern_map * reg); static double get_term_expectation(int *histogram, int num_bins, @@ -49,7 +46,7 @@ static double get_term_expectation(int *histogram, int num_bins, void init_vqe_settings(vqe_settings *settings, qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHOTS); void init_vqe_params(double *params, ptrdiff_t num_params); -double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHOTS); +double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHOTS, const double * x); double vqe_iter_nlopt(unsigned int n, const double *x, double *grad, void *f_data); double vqe_optimize(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NMEASURE, const size_t NSHOTS); diff --git a/examples/vqe/vqe_utils.c b/examples/vqe/vqe_utils.c index 8682c38..8408a4f 100644 --- a/examples/vqe/vqe_utils.c +++ b/examples/vqe/vqe_utils.c @@ -3,17 +3,17 @@ #include "nlopt.h" -vqe_context context = {0}; - void init_hf_state(qubit * qr, int num_spin_orbitals) { for (ptrdiff_t i = 0; i < num_spin_orbitals; ++i) { paulix(&qr[i]); } } -cq_status ansatz(const size_t NQUBITS, qubit * qr, const size_t NMEASURE, cstate * cr, qkern_map * reg) { +cq_status ansatz(const size_t NQUBITS, qubit * qr, const size_t NMEASURE, cstate * cr, void * kernpar, pqkern_map * reg) { CQ_REGISTER_KERNEL(reg) + double * params = (double *)kernpar; + HANDLE_CQ_ERROR(set_qureg(qr, 0, NQUBITS)); // HF init state. For H2 we set NQUBITS/2 to 1 init_hf_state(qr, NQUBITS / 2); @@ -22,7 +22,7 @@ cq_status ansatz(const size_t NQUBITS, qubit * qr, const size_t NMEASURE, cstate for (ptrdiff_t i = 0; i < NLAYERS; ++i) { for (ptrdiff_t j = 0; j < NQUBITS; ++j) { ptrdiff_t param_idx = j + i * (NQUBITS * 2); - roty(&qr[j], context.params[param_idx]); + roty(&qr[j], params[param_idx]); if (j < NQUBITS - 1) { int control = j; @@ -116,7 +116,7 @@ static int result_to_int(cstate *cr, int num_qubits) { #define MAX_BINS 1 << MAX_NQUBITS -double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHOTS) { +double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHOTS, const double * x) { int histogram[MAX_BINS] = {0}; const ptrdiff_t num_bins = (ptrdiff_t)1 << NQUBITS; double expectation = 0.0; @@ -126,7 +126,7 @@ double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHO for (ptrdiff_t i = 0; i < NTERMS; ++i) { ptrdiff_t paulis_start = i * NQUBITS; - sm_qrun(ansatz, qr, NQUBITS, cr, NMEASURE, NSHOTS); + smp_qrun(ansatz, x, NPARAMS * sizeof(double), qr, NQUBITS, cr, NMEASURE, NSHOTS); for (ptrdiff_t j = 0; j < NSHOTS; ++j) { ptrdiff_t cr_start = j * NQUBITS; @@ -137,8 +137,6 @@ double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHO expectation += get_term_expectation(histogram, num_bins, NMEASURE, NSHOTS, coeff); h2_hamil.term_start_idx += NQUBITS; } - printf("Iter: %td -- Expectation: %f\n", context.iter, expectation); - ++context.iter; return expectation; } @@ -147,22 +145,30 @@ double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHO double vqe_iter_nlopt(unsigned int n, const double *x, double *grad, void *f_data) { vqe_settings *settings = (vqe_settings *)(f_data); - double energy = vqe_iter(settings->qr, settings->cr, settings->NQUBITS, settings->NSHOTS); + double energy = vqe_iter(settings->qr, settings->cr, settings->NQUBITS, settings->NSHOTS, x); + + printf("Iter: %td -- Expectation: %f\n", settings->iter, energy); + ++settings->iter; + if (!grad) return energy; // estimate gradient if using gradient-based optimizer for (ptrdiff_t i = 0; i < NPARAMS; ++i) { - grad[i] = (energy - context.prev_energy) / (x[i] - context.prev_params[i]); - context.prev_params[i] = x[i]; + grad[i] = (energy - settings->prev_energy) / (x[i] - settings->prev_params[i]); + settings->prev_params[i] = x[i]; } - context.prev_energy = energy; + settings->prev_energy = energy; return energy; } double vqe_optimize(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NMEASURE, const size_t NSHOTS) { - init_vqe_params(context.params, NPARAMS); + vqe_settings settings = {0}; + init_vqe_settings(&settings, qr, cr, NQUBITS, NSHOTS); + + init_vqe_params(settings.params, NPARAMS); + // gradient-free optimizers nlopt_opt opt = nlopt_create(NLOPT_LN_COBYLA, NPARAMS); //nlopt_opt opt = nlopt_create(NLOPT_LN_SBPLX, NPARAMS); @@ -175,15 +181,12 @@ double vqe_optimize(qubit * qr, cstate * cr, const size_t NQUBITS, double ftol = 0.0001; nlopt_set_ftol_rel(opt, ftol); - vqe_settings settings; - init_vqe_settings(&settings, qr, cr, NQUBITS, NSHOTS); - nlopt_result res = nlopt_set_min_objective(opt, vqe_iter_nlopt, (void *)&settings); nlopt_set_lower_bounds1(opt, PARAM_MIN); nlopt_set_upper_bounds1(opt, PARAM_MAX); double best_expectation = 0.0; - res = nlopt_optimize(opt, context.params, &best_expectation); + res = nlopt_optimize(opt, settings.params, &best_expectation); printf("The final expectation: %f\n", best_expectation); diff --git a/include/comms_utils.h b/include/comms_utils.h new file mode 100644 index 0000000..dc7e8ba --- /dev/null +++ b/include/comms_utils.h @@ -0,0 +1,11 @@ +#ifndef CQ_COMMS_UTILS_H +#define CQ_COMMS_UTILS_H + +#include + +bool is_device(); + +#define CQ_PROG_BEGIN() if (!is_device()) { +#define CQ_PROG_END() } + +#endif diff --git a/include/cq.h b/include/cq.h index f4e002a..6d4fae0 100644 --- a/include/cq.h +++ b/include/cq.h @@ -2,12 +2,13 @@ #define CQ_H #include "analog.h" +#include "comms_utils.h" #include "datatypes.h" -#include "env.h" #include "device_ops.h" +#include "env.h" #include "host_ops.h" -#include "qasm_gates.h" #include "kernel_utils.h" +#include "qasm_gates.h" #include "utils.h" #endif diff --git a/include/env.h b/include/env.h index b886172..dcaf669 100644 --- a/include/env.h +++ b/include/env.h @@ -1,10 +1,11 @@ #ifndef CQ_ENV_H #define CQ_ENV_H -#include -#include #include "datatypes.h" +#include +#include + struct cq_environment { bool initialised; bool finalised; @@ -14,6 +15,8 @@ extern struct cq_environment cq_env; cq_status cq_init(const unsigned int VERBOSITY); +// cq_status cq_init_custom_mpi_comm(int foo, const unsigned int VERBOSITY); + cq_status cq_finalise(const unsigned int VERBOSITY); -#endif \ No newline at end of file +#endif diff --git a/include/host_ops.h b/include/host_ops.h index 8be1e8d..f3bd70e 100644 --- a/include/host_ops.h +++ b/include/host_ops.h @@ -16,61 +16,141 @@ cq_status free_qureg(qubit ** qrp); // Executors -cq_status s_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, cstate * const crp, - const size_t NMEASURE); - -cq_status a_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, cstate * const crp, - const size_t NMEASURE, cq_exec * const ehp); - -cq_status sm_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const size_t NSHOTS); - -cq_status am_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const size_t NSHOTS, - cq_exec * const ehp); - -cq_status sb_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const backend_id BE); - -cq_status ab_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const backend_id BE, - cq_exec * const ehp); - -cq_status smb_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const size_t NSHOTS, - const backend_id BE); - -cq_status amb_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const size_t NSHOTS, - const backend_id BE, cq_exec * const ehp); - -cq_status sp_qrun(pqkern kernel, void * kernpar, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE); - -cq_status ap_qrun(pqkern kernel, void * kernpar, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, cq_exec * const ehp); - -cq_status smp_qrun(pqkern kernel, void * kernpar, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const size_t NSHOTS); - -cq_status amp_qrun(pqkern kernel, void * kernpar, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const size_t NSHOTS, - cq_exec * const ehp); - -cq_status sbp_qrun(pqkern kernel, void * kernpar, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const backend_id BE); - -cq_status abp_qrun(pqkern kernel, void * kernpar, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, - const backend_id BE, cq_exec * const ehp); - -cq_status smbp_qrun(pqkern kernel, void * kernpar, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const size_t NSHOTS, - const backend_id BE); - -cq_status ambp_qrun(pqkern kernel, void * kernpar, qubit * qrp, const size_t NQUBITS, - cstate * const crp, const size_t NMEASURE, const size_t NSHOTS, - const backend_id BE, cq_exec * const ehp); +cq_status s_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE); + +cq_status a_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + cq_exec * const ehp); + +cq_status sm_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS); + +cq_status am_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS, + cq_exec * const ehp); + +cq_status sb_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const backend_id BE); + +cq_status ab_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const backend_id BE, + cq_exec * const ehp); + +cq_status smb_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS, + const backend_id BE); + +cq_status amb_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS, + const backend_id BE, + cq_exec * const ehp); + +// NOTE: requires modification of CQ spec +cq_status sp_qrun(pqkern kernel, + void * kernpar, + const size_t KERNPAR_SIZE, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE); + +// NOTE: requires modification of CQ spec +cq_status ap_qrun(pqkern kernel, + void * kernpar, + const size_t KERNPAR_SIZE, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + cq_exec * const ehp); + +// NOTE: requires modification of CQ spec +cq_status smp_qrun(pqkern kernel, + void * kernpar, + const size_t KERNPAR_SIZE, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS); + +// NOTE: requires modification of CQ spec +cq_status amp_qrun(pqkern kernel, + void * kernpar, + const size_t KERNPAR_SIZE, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS, + cq_exec * const ehp); + +cq_status sbp_qrun(pqkern kernel, + void * kernpar, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const backend_id BE); + +cq_status abp_qrun(pqkern kernel, + void * kernpar, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const backend_id BE, + cq_exec * const ehp); + +cq_status smbp_qrun(pqkern kernel, + void * kernpar, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS, + const backend_id BE); + +cq_status ambp_qrun(pqkern kernel, + void * kernpar, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS, + const backend_id BE, + cq_exec * const ehp); // Synchronisation @@ -80,4 +160,4 @@ cq_status wait_qrun(cq_exec * const ehp); cq_status halt_qrun(cq_exec * const ehp); -#endif \ No newline at end of file +#endif diff --git a/include/utils.h b/include/utils.h index f7cb9d2..a8ad938 100644 --- a/include/utils.h +++ b/include/utils.h @@ -5,18 +5,19 @@ void init_creg(const size_t LENGTH, const cstate INIT_VAL, cstate * cr); -void report_results( - cstate const * const CR, - const size_t NMEASURE, - const size_t NSHOTS); +void report_results(cstate const * const CR, + const size_t NMEASURE, + const size_t NSHOTS); -#define HANDLE_CQ_ERROR(x) \ - { \ - if (x == CQ_ERROR) \ - { \ - printf(" From: %s\n", __func__); \ - return CQ_ERROR; \ - } \ - }; +#define HANDLE_CQ_ERROR(x) \ + { \ + if (x == CQ_ERROR) { \ + printf(" From: %s\n", __func__); \ + return CQ_ERROR; \ + } \ + }; -#endif \ No newline at end of file +#define CQ_PROG_BEGIN() if (!is_device()) { +#define CQ_PROG_END() } + +#endif diff --git a/src/host-device/comms.h b/src/host-device/comms.h index c4a4468..c829f41 100644 --- a/src/host-device/comms.h +++ b/src/host-device/comms.h @@ -1,11 +1,13 @@ #ifndef CQ_HOST_DEVICE_COMMS_H #define CQ_HOST_DEVICE_COMMS_H -#include -#include +#include "comms_utils.h" #include "datatypes.h" #include "src/host/opcodes.h" +#include +#include + #define __CQ_DEVICE_QUEUE_SIZE__ 16 #define RUN_HOST_ONLY() \ @@ -30,7 +32,7 @@ struct dev_link { size_t next_op_in; size_t next_op_out; enum ctrl_code op_buffer[__CQ_DEVICE_QUEUE_SIZE__]; - void* op_params_buffer[__CQ_DEVICE_QUEUE_SIZE__]; + void * op_params_buffer[__CQ_DEVICE_QUEUE_SIZE__]; }; typedef struct device_alloc_params { @@ -43,14 +45,14 @@ extern struct dev_link dev_ctrl; int initialise_device(const unsigned int VERBOSITY); -size_t host_send_ctrl_op(const enum ctrl_code OP, void* ctrl_params); +size_t host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params); size_t host_wait_all_ops(void); size_t device_sync_exec(const cq_status STATUS, const size_t SHOT, - cstate const* const RESULT, - cq_exec* ehp); + cstate const * const RESULT, + cq_exec * ehp); void host_device_sync_comms(void); @@ -61,12 +63,6 @@ int finalise_device(const unsigned int VERBOSITY); /// @return new executor id size_t assign_exec_id(void); -/// -/// check if MPI process is assigned to the device. -/// @return true if the MPI process is device process. false by default (when CQ -/// built without MPI) -bool is_device(void); - /// /// initialises correct QuEST environment depending on the build opotions. void init_quest_env(void); diff --git a/src/host-device/comms/comms.c b/src/host-device/comms/comms.c index 724e60c..2b1090f 100644 --- a/src/host-device/comms/comms.c +++ b/src/host-device/comms/comms.c @@ -7,57 +7,23 @@ #include int initialise_device(const unsigned int VERBOSITY) { - init_device_controls(VERBOSITY); - unsigned int verbosity = VERBOSITY; - host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); - host_wait_all_ops(); - return 0; + return serial_initialise_device(VERBOSITY); } -size_t host_send_ctrl_op(const enum ctrl_code OP, void* ctrl_params) { - switch (OP) { - case CQ_CTRL_SYNC_EXEC: { - return comms_exec_sync(ctrl_params); - break; - } - case CQ_CTRL_WAIT_EXEC: { - return comms_exec_wait(ctrl_params); - break; - } - case CQ_CTRL_ABORT: { - comms_exec_halt(ctrl_params); - return 0; - break; - } - default: { - break; - } - } - return insert_op(OP, ctrl_params); +size_t host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params) { + return serial_host_send_ctrl_op(OP, ctrl_params); } size_t host_wait_all_ops(void) { - return device_wait_all_ops(); + return serial_host_wait_all_ops(); } -void host_device_sync_comms(void) {} +void host_device_sync_comms(void) { + serial_host_device_sync_comms(); +} int finalise_device(const unsigned int VERBOSITY) { - // Politely wait for the device to finish its current business - // otherwise setting dev_ctrl.run_device = false might break - // some stuff, and this should only be called in cq_finalise() - host_wait_all_ops(); - - if (VERBOSITY > 0) { - printf("Finalising device.\n"); - } - - stop_device(); - unsigned int verbosity = VERBOSITY; - host_send_ctrl_op(CQ_CTRL_FINALISE, &verbosity); - finalise_device_controls(VERBOSITY); - - return 0; + return serial_finalise_device(VERBOSITY); } bool is_device(void) { diff --git a/src/host-device/comms/comms_core.c b/src/host-device/comms/comms_core.c index 8fd21da..73980a6 100644 --- a/src/host-device/comms/comms_core.c +++ b/src/host-device/comms/comms_core.c @@ -2,6 +2,7 @@ #include "../comms.h" #include "src/device/control.h" +#include "src/host/opcodes.h" #include #include @@ -53,7 +54,7 @@ int finalise_device_controls(const unsigned int VERBOSITY) { } // NOTE: aka host_send_ctrl_op from OG comm.c -size_t insert_op(const enum ctrl_code OP, void* ctrl_params) { +size_t insert_op(const enum ctrl_code OP, void * ctrl_params) { pthread_mutex_lock(&dev_ctrl.device_lock); while (dev_ctrl.num_ops >= __CQ_DEVICE_QUEUE_SIZE__) { @@ -77,7 +78,7 @@ size_t insert_op(const enum ctrl_code OP, void* ctrl_params) { return dev_ctrl.num_ops; } -size_t comms_exec_sync(cq_exec* const ehp) { +size_t comms_exec_sync(cq_exec * const ehp) { if (ehp == NULL) { return 0; } @@ -88,7 +89,7 @@ size_t comms_exec_sync(cq_exec* const ehp) { return completed_shots; } -size_t comms_exec_wait(cq_exec* const ehp) { +size_t comms_exec_wait(cq_exec * const ehp) { if (ehp == NULL) { return 0; } @@ -101,7 +102,7 @@ size_t comms_exec_wait(cq_exec* const ehp) { return ehp->completed_shots; } -void comms_exec_halt(cq_exec* const ehp) { +void comms_exec_halt(cq_exec * const ehp) { if (ehp == NULL) { return; } @@ -123,9 +124,9 @@ size_t device_wait_all_ops(void) { return dev_ctrl.num_ops; } -void* device_control_thread(void* par) { +void * device_control_thread(void * par) { enum ctrl_code current_op = CQ_CTRL_IDLE; - void* current_op_params = NULL; + void * current_op_params = NULL; // run_device set to FALSE at cq_finalise while (dev_ctrl.run_device) { @@ -166,8 +167,8 @@ void* device_control_thread(void* par) { size_t device_sync_exec(const cq_status STATUS, const size_t SHOT, - cstate const* const RESULT, - cq_exec* ehp) { + cstate const * const RESULT, + cq_exec * ehp) { pthread_mutex_lock(&ehp->lock); if (STATUS == CQ_EARLY_SUCCESS) { @@ -182,12 +183,12 @@ size_t device_sync_exec(const cq_status STATUS, ehp->completed_shots += 1; // copy local result register to exec - cstate* dest_creg = ehp->creg + SHOT * ehp->nmeasure; + cstate * dest_creg = ehp->creg + SHOT * ehp->nmeasure; memcpy(dest_creg, RESULT, ehp->nmeasure * sizeof(cstate)); // check if the whole execution is done - if (ehp->completed_shots == ehp->expected_shots || STATUS != CQ_SUCCESS || - ehp->halt) { + if (ehp->completed_shots == ehp->expected_shots || STATUS != CQ_SUCCESS + || ehp->halt) { ehp->complete = true; pthread_cond_signal(&(ehp->cond_exec_complete)); } @@ -202,3 +203,57 @@ size_t assign_exec_id(void) { global_exec_id_counter %= __CQ_DEVICE_QUEUE_SIZE__; return global_exec_id_counter; } + +size_t serial_host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params) { + switch (OP) { + case CQ_CTRL_SYNC_EXEC: { + return comms_exec_sync(ctrl_params); + break; + } + case CQ_CTRL_WAIT_EXEC: { + return comms_exec_wait(ctrl_params); + break; + } + case CQ_CTRL_ABORT: { + comms_exec_halt(ctrl_params); + return 0; + break; + } + default: { + break; + } + } + return insert_op(OP, ctrl_params); +} + +int serial_initialise_device(const unsigned int VERBOSITY) { + init_device_controls(VERBOSITY); + unsigned int verbosity = VERBOSITY; + host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); + host_wait_all_ops(); + return 0; +} + +size_t serial_host_wait_all_ops(void) { + return device_wait_all_ops(); +} + +void serial_host_device_sync_comms(void) {} + +int serial_finalise_device(const unsigned int VERBOSITY) { + // Politely wait for the device to finish its current business + // otherwise setting dev_ctrl.run_device = false might break + // some stuff, and this should only be called in cq_finalise() + serial_host_wait_all_ops(); + + if (VERBOSITY > 0) { + printf("Finalising device.\n"); + } + + stop_device(); + unsigned int verbosity = VERBOSITY; + serial_host_send_ctrl_op(CQ_CTRL_FINALISE, &verbosity); + finalise_device_controls(VERBOSITY); + + return 0; +} diff --git a/src/host-device/comms/comms_core.h b/src/host-device/comms/comms_core.h index a0f7f3e..3b1745f 100644 --- a/src/host-device/comms/comms_core.h +++ b/src/host-device/comms/comms_core.h @@ -12,16 +12,26 @@ void stop_device(void); int finalise_device_controls(const unsigned int VERBOSITY); -size_t insert_op(const enum ctrl_code OP, void* ctrl_params); +size_t insert_op(const enum ctrl_code OP, void * ctrl_params); -size_t comms_exec_sync(cq_exec* const ehp); +size_t comms_exec_sync(cq_exec * const ehp); -size_t comms_exec_wait(cq_exec* const ehp); +size_t comms_exec_wait(cq_exec * const ehp); -void comms_exec_halt(cq_exec* const ehp); +void comms_exec_halt(cq_exec * const ehp); size_t device_wait_all_ops(void); -void* device_control_thread(void*); +void * device_control_thread(void *); + +size_t serial_host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params); + +int serial_initialise_device(const unsigned int VERBOSITY); + +size_t serial_host_wait_all_ops(void); + +void serial_host_device_sync_comms(void); + +int serial_finalise_device(const unsigned int VERBOSITY); #endif diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index 0fb9933..b4f7517 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -1,14 +1,14 @@ #include "comms_mpi.h" -#include +#include #include "comms_core.h" #include "src/host-device/comms.h" #include "datatypes.h" #include "src/host/opcodes.h" -#ifndef CQ_CONF_QUEST_WITH_MPI -#include "quest/include/environment.h" -#endif +// #ifndef CQ_CONF_QUEST_WITH_MPI +// #include "quest/include/environment.h" +// #endif #if CQ_CONF_QUEST_WITH_MPI #include "quest/include/subcommunicator.h" @@ -26,8 +26,15 @@ // ---------------------------------------------------------------------------- // Macros // ---------------------------------------------------------------------------- -#define CQ_MPI_HOST_RANK 0 -#define CQ_MPI_DEVICE_RANK 1 +// TODO: change these to be variables rather than macros +// i.e. CQ_MPI_HOST_RANK is the one specified by user +// CQ_MPI_DEVICE RANK is also specified by user or CQ_MPI_HOST_RANK + 1 or +// CQ_MPI_HOST_RANK if running with only 1 proc +// +// so default values for host_rank = 0, device_rank = host_rank + 1 +// but user can pass as args +// #define CQ_MPI_HOST_RANK 0 +// #define CQ_MPI_DEVICE_RANK 1 #define CQ_MPI_DEVICE_MASTER_RANK 0 #define CQ_MPI_WORLD_COMMS_TAG 0 #define CQ_MPI_SUBCOMMS_TAG 0 @@ -35,8 +42,11 @@ #define CQ_MPI_RUNTIME_ERROR -4 #define CQ_MPI_MALLOC_ERROR -5 -static const MPI_Comm CQ_MPI_COMM_WORLD = MPI_COMM_WORLD; -static MPI_Comm CQ_MPI_SPLIT_COMM; +static MPI_Comm CQ_MPI_COMM_WORLD = MPI_COMM_NULL; +static MPI_Comm CQ_MPI_SPLIT_COMM = MPI_COMM_NULL; + +static int CQ_MPI_HOST_RANK = 0; +static int CQ_MPI_DEVICE_RANK = 0; struct cq_mpi_env { int rank; @@ -45,12 +55,14 @@ struct cq_mpi_env { int world_size; }; -static cq_exec* executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; +static cq_exec * executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; static size_t num_active_executors = 0; -static struct cq_mpi_env mpi_env = {.rank = -1, - .subcomm_rank = -1, - .verbosity = 0, - .world_size = 0}; +static struct cq_mpi_env mpi_env = { + .rank = -1, + .subcomm_rank = -1, + .verbosity = 0, + .world_size = 0, +}; struct communicator { bool comm_busy; @@ -59,11 +71,11 @@ struct communicator { pthread_mutex_t comm_lock; }; -static struct communicator dev_comm = {0}; +static struct communicator dev_comm = { 0 }; -static char debug_worker_name[64] = {0}; +static char debug_worker_name[64] = { 0 }; -static void cq_log(const char* format, ...) { +static void cq_log(const char * format, ...) { #ifdef DEBUG_MODE va_list(args); va_start(args, format); @@ -82,6 +94,10 @@ static bool cq_validate_mpi_rank(const int rank) { int initialise_device(const unsigned int VERBOSITY) { init_host_device_mpi(VERBOSITY); + if (mpi_env.world_size == 1) { + return serial_initialise_device(VERBOSITY); + } + RUN_HOST_ONLY(); unsigned int verbosity = VERBOSITY; host_send_ctrl_op(CQ_CTRL_INIT, &verbosity); @@ -89,7 +105,11 @@ int initialise_device(const unsigned int VERBOSITY) { return 0; } -size_t host_send_ctrl_op(const enum ctrl_code OP, void* params) { +size_t host_send_ctrl_op(const enum ctrl_code OP, void * params) { + if (mpi_env.world_size == 1) { + return serial_host_send_ctrl_op(OP, params); + } + const int device_rank = CQ_MPI_DEVICE_RANK; cq_log("%s [send_ctrl_op]: sending %s...\n", get_comm_source(), op_to_str(OP)); @@ -103,6 +123,10 @@ size_t host_send_ctrl_op(const enum ctrl_code OP, void* params) { } size_t host_wait_all_ops(void) { + if (mpi_env.world_size == 1) { + return serial_host_wait_all_ops(); + } + cq_log("%s [wait_all_ops]: waiting...\n", get_comm_source()); host_send_ctrl_op(CQ_CTRL_WAIT, NULL); size_t num_ops; @@ -116,6 +140,10 @@ size_t host_wait_all_ops(void) { } void host_device_sync_comms(void) { + if (mpi_env.world_size == 1) { + return serial_host_device_sync_comms(); + } + if (mpi_env.rank != CQ_MPI_HOST_RANK) { cq_log("%s [final_sync]: simply waiting\n", get_comm_source()); device_wait_comms(); @@ -123,6 +151,12 @@ void host_device_sync_comms(void) { } int finalise_device(const unsigned int VERBOSITY) { + if (mpi_env.world_size == 1) { + int res = serial_finalise_device(VERBOSITY); + finalise_host_device_mpi(VERBOSITY); + return res; + } + host_device_sync_comms(); if (mpi_env.rank == CQ_MPI_HOST_RANK) { @@ -152,6 +186,10 @@ bool is_device(void) { } void init_quest_env(void) { +// if (mpi_env.world_size == 1) { +// initQuESTEnv(); +// return; +// } #if CQ_WITH_MPI_COMMS && CQ_CONF_QUEST_WITH_MPI if (is_quantum_worker()) { initCustomMpiCommQuESTEnv(get_quest_comm(), 0, 0); @@ -172,7 +210,20 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { cq_log("Initialising MPI.\n"); } - MPI_Init(NULL, NULL); + int provided; + MPI_Init_thread(NULL, NULL, MPI_THREAD_MULTIPLE, &provided); + + if (provided != MPI_THREAD_MULTIPLE) { + cq_log( + "Failed setting up multithreading with MPI. Requested " + "MPI_THREAD_MULTIPLE, given: %d\n", + provided); + exit(CQ_MPI_RUNTIME_ERROR); + } + + if (CQ_MPI_COMM_WORLD == MPI_COMM_NULL) { + MPI_Comm_dup(MPI_COMM_WORLD, &CQ_MPI_COMM_WORLD); + } MPI_Comm_size(CQ_MPI_COMM_WORLD, &mpi_env.world_size); MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); @@ -181,8 +232,14 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { // (nproc - 1) == n_device * power of 2 validate_nproc(mpi_env.world_size); + if (mpi_env.world_size == 1) { + MPI_Barrier(CQ_MPI_COMM_WORLD); + return; + } + #if CQ_CONF_QUEST_WITH_MPI - const int QUANTUM_WORKER = mpi_env.rank > 0; + // const int QUANTUM_WORKER = mpi_env.rank > 0; + const int QUANTUM_WORKER = mpi_env.rank >= CQ_MPI_DEVICE_RANK; MPI_Barrier(CQ_MPI_COMM_WORLD); MPI_Comm_split(CQ_MPI_COMM_WORLD, QUANTUM_WORKER, mpi_env.rank, &CQ_MPI_SPLIT_COMM); @@ -254,7 +311,7 @@ void device_finalise_comms(const unsigned int VERBOSITY) { } } -void* device_listen(void* args) { +void * device_listen(void * args) { // run_device set to FALSE when OP == CQ_CTRL_FINALISE cq_log("%s started listening...\n", get_comm_source()); while (dev_ctrl.run_device) { @@ -336,7 +393,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // Also, because it's blocking I don't need to worry about // params lifetime (from the worker perspective) const int host_rank = CQ_MPI_HOST_RANK; - device_alloc_params params = {0}; + device_alloc_params params = { 0 }; recv_alloc_params(¶ms, host_rank); insert_op(OP, ¶ms); cq_log("%s [dispatch][ALLOC]: inserted op\n", get_comm_source()); @@ -347,7 +404,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { case CQ_CTRL_DEALLOC: { // Same as Alloc const int host_rank = CQ_MPI_HOST_RANK; - device_alloc_params params = {0}; + device_alloc_params params = { 0 }; recv_alloc_params(¶ms, host_rank); insert_op(OP, ¶ms); device_wait_all_ops(); @@ -364,7 +421,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { exit(CQ_MPI_RUNTIME_ERROR); } const int host_rank = CQ_MPI_HOST_RANK; - cq_exec* tmp_exec = NULL; + cq_exec * tmp_exec = NULL; recv_exec_params(&tmp_exec, host_rank); // NOTE: this can be done here to free old one // and allocate new one rather than in wait_exec @@ -391,14 +448,14 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { // Same logic as in CQ_CTRL_RUN_QKERNEL if (num_active_executors >= __CQ_DEVICE_QUEUE_SIZE__) { cq_log( - "%s [dispatch][RUN_QKERNEL]: You have oversubsribed the executor " + "%s [dispatch][RUN_PQKERNEL]: You have oversubsribed the executor " "queue. We allow up to %d " "concurrent executors per device. Exiting", get_comm_source(), __CQ_DEVICE_QUEUE_SIZE__); exit(CQ_MPI_RUNTIME_ERROR); } const int host_rank = CQ_MPI_HOST_RANK; - cq_exec* tmp_exec = NULL; + cq_exec * tmp_exec = NULL; recv_exec_params(&tmp_exec, host_rank); executor_handles[tmp_exec->id] = tmp_exec; insert_op(OP, executor_handles[tmp_exec->id]); @@ -507,7 +564,7 @@ void device_wait_comms(void) { // Device Control Paramaters Comms // ---------------------------------------------------------------------------- -void host_comm_params(const enum ctrl_code OP, void* params) { +void host_comm_params(const enum ctrl_code OP, void * params) { const int device_rank = CQ_MPI_DEVICE_RANK; switch (OP) { case CQ_CTRL_INIT: { @@ -530,20 +587,24 @@ void host_comm_params(const enum ctrl_code OP, void* params) { send_exec_params(params, device_rank); break; } + case CQ_CTRL_RUN_PQKERNEL: { + send_exec_params(params, device_rank); + break; + } case CQ_CTRL_WAIT_EXEC: { - cq_exec* exec_params = (cq_exec*)params; - send_exec_id(((cq_exec*)params)->id, device_rank); + cq_exec * exec_params = (cq_exec *)params; + send_exec_id(((cq_exec *)params)->id, device_rank); recv_exec_params(&exec_params, device_rank); break; } case CQ_CTRL_SYNC_EXEC: { - cq_exec* exec_params = (cq_exec*)params; - send_exec_id(((cq_exec*)params)->id, device_rank); + cq_exec * exec_params = (cq_exec *)params; + send_exec_id(((cq_exec *)params)->id, device_rank); recv_exec_params(&exec_params, device_rank); break; } case CQ_CTRL_ABORT: { - send_exec_id(((cq_exec*)params)->id, device_rank); + send_exec_id(((cq_exec *)params)->id, device_rank); break; } default: { @@ -552,7 +613,7 @@ void host_comm_params(const enum ctrl_code OP, void* params) { } } -void recv_alloc_params(device_alloc_params* params, const int src) { +void recv_alloc_params(device_alloc_params * params, const int src) { cq_log("%s [recv_alloc_params]: receiving...\n", get_comm_source()); if (!cq_validate_mpi_rank(src)) { @@ -566,8 +627,8 @@ void recv_alloc_params(device_alloc_params* params, const int src) { #if CQ_CONF_QUEST_WITH_MPI // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK + || mpi_env.rank == CQ_MPI_HOST_RANK) { #endif MPI_Recv(params, params_size, MPI_BYTE, src, CQ_MPI_WORLD_COMMS_TAG, @@ -586,7 +647,7 @@ void recv_alloc_params(device_alloc_params* params, const int src) { print_alloc_params(params); } -void send_alloc_params(const device_alloc_params* params, const int dest) { +void send_alloc_params(const device_alloc_params * params, const int dest) { if (params == NULL) { cq_log("%s [recv_exec_params]: passed null params\n", get_comm_source()); return; @@ -600,8 +661,8 @@ void send_alloc_params(const device_alloc_params* params, const int dest) { #if CQ_CONF_QUEST_WITH_MPI // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK + || mpi_env.rank == CQ_MPI_HOST_RANK) { #endif cq_log("%s [send_alloc_params]: sending...\n", get_comm_source()); @@ -629,8 +690,8 @@ size_t recv_exec_id(const int src) { #if CQ_CONF_QUEST_WITH_MPI // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK + || mpi_env.rank == CQ_MPI_HOST_RANK) { #endif MPI_Recv(&id, 1, MPI_UINT64_T, src, CQ_MPI_WORLD_COMMS_TAG, @@ -658,8 +719,8 @@ void send_exec_id(const size_t id, const int dest) { #if CQ_CONF_QUEST_WITH_MPI // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK + || mpi_env.rank == CQ_MPI_HOST_RANK) { #endif cq_log("%s [send_exec_id]: sending id: %zu...\n", get_comm_source(), id); @@ -672,7 +733,7 @@ void send_exec_id(const size_t id, const int dest) { #endif } -void recv_exec_params(cq_exec** ehp, const int src) { +void recv_exec_params(cq_exec ** ehp, const int src) { cq_log("%s [recv_exec_params]: receiving...\n", get_comm_source()); if (ehp == NULL) { @@ -692,8 +753,8 @@ void recv_exec_params(cq_exec** ehp, const int src) { #if CQ_CONF_QUEST_WITH_MPI // device-master (rank 0) gets from host from world comm // or host gets from device-master - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK + || mpi_env.rank == CQ_MPI_HOST_RANK) { #endif MPI_Recv(&msg_size, 1, MPI_INT, src, CQ_MPI_WORLD_COMMS_TAG, @@ -709,14 +770,14 @@ void recv_exec_params(cq_exec** ehp, const int src) { } #endif - void* recv_buffer = malloc(msg_size); + void * recv_buffer = malloc(msg_size); // reserve space for all the data + currently unused members if (*ehp == NULL) { cq_log("%s [recv_exec_params]: *ehp is NULL. Allocating on device.\n", get_comm_source()); - *ehp = (cq_exec*)malloc(sizeof(cq_exec)); + *ehp = (cq_exec *)malloc(sizeof(cq_exec)); pthread_mutex_init(&(*ehp)->lock, NULL); pthread_cond_init(&(*ehp)->cond_exec_complete, NULL); } else { @@ -736,8 +797,8 @@ void recv_exec_params(cq_exec** ehp, const int src) { #if CQ_CONF_QUEST_WITH_MPI // device-master (rank 0) gets from host from world comm // or host gets from device-master - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK + || mpi_env.rank == CQ_MPI_HOST_RANK) { #endif MPI_Recv(recv_buffer, msg_size, MPI_PACKED, src, CQ_MPI_WORLD_COMMS_TAG, @@ -790,21 +851,21 @@ void recv_exec_params(cq_exec** ehp, const int src) { // when on host the resources are already allocated! if (mpi_env.rank != CQ_MPI_HOST_RANK) { - (*ehp)->fname = (char*)malloc(fname_size); + (*ehp)->fname = (char *)malloc(fname_size); if ((*ehp)->fname == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", get_comm_source()); exit(CQ_MPI_MALLOC_ERROR); } - (*ehp)->qreg = (qubit*)malloc(qreg_size); + (*ehp)->qreg = (qubit *)malloc(qreg_size); if ((*ehp)->qreg == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", get_comm_source()); exit(CQ_MPI_MALLOC_ERROR); } - (*ehp)->creg = (cstate*)malloc(creg_size); + (*ehp)->creg = (cstate *)malloc(creg_size); if ((*ehp)->creg == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", get_comm_source()); @@ -821,11 +882,11 @@ void recv_exec_params(cq_exec** ehp, const int src) { MPI_Unpack(recv_buffer, msg_size, &position, (*ehp)->fname, fname_size, MPI_CHAR, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->qreg, qreg_size, + MPI_Unpack(recv_buffer, msg_size, &position, (char *)(*ehp)->qreg, qreg_size, MPI_BYTE, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->creg, creg_size, + MPI_Unpack(recv_buffer, msg_size, &position, (char *)(*ehp)->creg, creg_size, MPI_BYTE, CQ_MPI_COMM_WORLD); - MPI_Unpack(recv_buffer, msg_size, &position, (char*)(*ehp)->params, + MPI_Unpack(recv_buffer, msg_size, &position, (char *)(*ehp)->params, params_size, MPI_BYTE, CQ_MPI_COMM_WORLD); free(recv_buffer); @@ -835,7 +896,7 @@ void recv_exec_params(cq_exec** ehp, const int src) { pthread_mutex_unlock(&(*ehp)->lock); } -void send_exec_params(cq_exec* ehp, const int dest) { +void send_exec_params(cq_exec * ehp, const int dest) { if (ehp == NULL) { cq_log("%s [recv_exec_params]: passed null ehp\n", get_comm_source()); return; @@ -853,8 +914,8 @@ void send_exec_params(cq_exec* ehp, const int dest) { // so only device-master communicates its state to host. #if CQ_CONF_QUEST_WITH_MPI // device master (rank 0) gets from host from world comm - if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK || - mpi_env.rank == CQ_MPI_HOST_RANK) { + if (mpi_env.subcomm_rank == CQ_MPI_DEVICE_MASTER_RANK + || mpi_env.rank == CQ_MPI_HOST_RANK) { #endif pthread_mutex_lock(&ehp->lock); @@ -921,7 +982,7 @@ void send_exec_params(cq_exec* ehp, const int dest) { MPI_Pack_size(params_size, MPI_BYTE, CQ_MPI_COMM_WORLD, &member_size); max_buffer_size += member_size; // params - void* send_buffer = malloc(max_buffer_size); + void * send_buffer = malloc(max_buffer_size); if (send_buffer == NULL) { cq_log( "Failed to allocate buffer for sending executor handle. Exiting\n"); @@ -978,7 +1039,7 @@ void send_exec_params(cq_exec* ehp, const int dest) { #endif } -void device_free_exec(cq_exec** ehp) { +void device_free_exec(cq_exec ** ehp) { cq_log("%s [device_free_exec]: freeing the executor\n", get_comm_source()); if (ehp == NULL) { cq_log("%s [device_free_exec]: ehp is NULL\n", get_comm_source()); @@ -1026,7 +1087,7 @@ void device_free_exec(cq_exec** ehp) { // Helpers // ---------------------------------------------------------------------------- -const char* get_comm_source(void) { +const char * get_comm_source(void) { if (mpi_env.rank == CQ_MPI_HOST_RANK) { return "Host:\t\t\t\t"; } else if (mpi_env.rank == CQ_MPI_DEVICE_RANK) { @@ -1038,7 +1099,7 @@ const char* get_comm_source(void) { } } -const char* op_to_str(const enum ctrl_code OP) { +const char * op_to_str(const enum ctrl_code OP) { switch (OP) { case CQ_CTRL_IDLE: { return "CQ_CTRL_IDLE"; @@ -1095,7 +1156,7 @@ const char* op_to_str(const enum ctrl_code OP) { return ""; } -void print_alloc_params(const device_alloc_params* params) { +void print_alloc_params(const device_alloc_params * params) { if (params == NULL) { return; } @@ -1104,7 +1165,7 @@ void print_alloc_params(const device_alloc_params* params) { params->status); } -void print_ehp(const cq_exec* ehp) { +void print_ehp(const cq_exec * ehp) { if (ehp == NULL) { cq_log("ehp is NULL\n"); } @@ -1126,8 +1187,14 @@ void print_ehp(const cq_exec* ehp) { } cq_log("\ncreg:\n"); - for (size_t i = 0; i < ehp->nmeasure * ehp->expected_shots; ++i) { - cq_log("cstate[%zu]: %d\n", i, ehp->creg[i]); + // for (size_t i = 0; i < ehp->nmeasure * ehp->expected_shots; ++i) { + // cq_log("cstate[%zu]: %d\n", i, ehp->creg[i]); + // } + + cq_log("\nparams:\n"); + double * dparams = (double *)ehp->params; + for (size_t i = 0; i < ehp->params_size / sizeof(double); ++i) { + cq_log("params[%zu]: %f\n", i, dparams[i]); } cq_log("%s ehp details END\n\n", get_comm_source()); } @@ -1142,6 +1209,11 @@ MPI_Comm get_quest_comm(void) { void validate_nproc(const int nproc) { const int device_nproc = nproc - 1; + if (nproc == 1) { + cq_log("Initialising CQ MPI with 1 process. Running serial job.\n"); + CQ_MPI_DEVICE_RANK = CQ_MPI_HOST_RANK; + return; + } #if CQ_CONF_QUEST_WITH_MPI if (!((device_nproc > 0) && ((device_nproc & (device_nproc - 1)) == 0))) { if (mpi_env.rank == CQ_MPI_HOST_RANK) { @@ -1162,6 +1234,8 @@ void validate_nproc(const int nproc) { exit(CQ_MPI_RUNTIME_ERROR); } #endif + // default setup + CQ_MPI_DEVICE_RANK = CQ_MPI_HOST_RANK + 1; } #undef CQ_MPI_HOST_RANK diff --git a/src/host-device/kernel_utils.c b/src/host-device/kernel_utils.c index 739bee9..19b0c5a 100644 --- a/src/host-device/kernel_utils.c +++ b/src/host-device/kernel_utils.c @@ -40,6 +40,32 @@ cq_status register_qkern(qkern kernel) { } cq_status register_pqkern(pqkern kernel) { + cq_status status = CQ_ERROR; + char * fname; + + if (kernel != NULL && pqk_reg.next_available_slot < __CQ_MAX_NUM_QKERN__) { + status = find_pqkern_name(kernel, &fname); + if (status == CQ_SUCCESS) { + // This kernel has already been registered! + status = CQ_WARNING; + } else { + pqkern_map * pqkmap = &pqk_reg.pqkernels[pqk_reg.next_available_slot]; + kernel(0, NULL, 0, NULL, NULL, pqkmap); + if (pqkmap->fname[0] != '\0') { + pqkmap->fn = kernel; + ++pqk_reg.next_available_slot; + status = CQ_SUCCESS; + } else { + status = CQ_ERROR; + } + } + } + + //host_device_sync_comms(); + RUN_HOST_ONLY(); + host_wait_all_ops(); + return status; + return CQ_ERROR; } @@ -81,7 +107,7 @@ cq_status find_pqkern_pointer(char const * const FNAME, pqkern * pqk) { *pqk = NULL; int status = CQ_SUCCESS; - for (size_t i = 0; i < qk_reg.next_available_slot; ++i) { + for (size_t i = 0; i < pqk_reg.next_available_slot; ++i) { if (!strcmp(FNAME, pqk_reg.pqkernels[i].fname)) { // We found it! *pqk = pqk_reg.pqkernels[i].fn; @@ -98,7 +124,7 @@ cq_status find_pqkern_name(pqkern const PQK, char ** fname) { *fname = NULL; int status = CQ_SUCCESS; - for (size_t i = 0; i < qk_reg.next_available_slot; ++i) { + for (size_t i = 0; i < pqk_reg.next_available_slot; ++i) { if (PQK == pqk_reg.pqkernels[i].fn) { // We found it! *fname = pqk_reg.pqkernels[i].fname; diff --git a/src/host/host_ops.c b/src/host/host_ops.c index 39a9e0f..2e34a65 100644 --- a/src/host/host_ops.c +++ b/src/host/host_ops.c @@ -1,10 +1,12 @@ -#include -#include "datatypes.h" #include "host_ops.h" + +#include "datatypes.h" #include "kernel_utils.h" #include "src/host-device/comms.h" -//#include "src/host-device/mpi_comms.h" +#include + +// #include "src/host-device/mpi_comms.h" #include // Resource Management @@ -15,20 +17,20 @@ cq_status alloc_qubit(qubit ** qhp) { cq_status alloc_qureg(qubit ** qrp, size_t N) { RUN_HOST_ONLY(); cq_status status = CQ_SUCCESS; - + // check qr is NULL // If it's not, we still allocate, but issue a CQ_WARNING. - if (*qrp != NULL) { + if (*qrp != NULL) { status = CQ_WARNING; } - + device_alloc_params alloc_params = { .NQUBITS = N, .qregistry_idx = 0, - .status = CQ_ERROR + .status = CQ_ERROR, }; - *qrp = (qubit *) malloc(sizeof(qubit) * N); + *qrp = (qubit *)malloc(sizeof(qubit) * N); if (*qrp == NULL) { /* whoops, the malloc failed originally we only malloc'd on the host after receiving @@ -38,7 +40,7 @@ cq_status alloc_qureg(qubit ** qrp, size_t N) { */ return CQ_ERROR; } - + host_send_ctrl_op(CQ_CTRL_ALLOC, &alloc_params); host_wait_all_ops(); @@ -70,7 +72,7 @@ cq_status free_qureg(qubit ** qrp) { device_alloc_params dealloc_params = { .NQUBITS = 0, .qregistry_idx = (*qrp)[0].registry_index, - .status = CQ_ERROR + .status = CQ_ERROR, }; host_send_ctrl_op(CQ_CTRL_DEALLOC, &dealloc_params); @@ -86,14 +88,17 @@ cq_status free_qureg(qubit ** qrp) { // Executors -cq_status s_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, -cstate * crp, const size_t NMEASURE) { +cq_status s_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * crp, + const size_t NMEASURE) { RUN_HOST_ONLY(); cq_status status = CQ_ERROR; cq_exec exec_handle; status = a_qrun(kernel, qrp, NQUBITS, crp, NMEASURE, &exec_handle); - + if (status == CQ_SUCCESS) { status = wait_qrun(&exec_handle); @@ -105,8 +110,12 @@ cstate * crp, const size_t NMEASURE) { return status; } -cq_status a_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, -cstate * crp, const size_t NMEASURE, cq_exec * const ehp) { +cq_status a_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * crp, + const size_t NMEASURE, + cq_exec * const ehp) { RUN_HOST_ONLY(); cq_status status = CQ_ERROR; char * fname = NULL; @@ -133,8 +142,12 @@ cstate * crp, const size_t NMEASURE, cq_exec * const ehp) { return status; } -cq_status sm_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, -cstate * const crp, const size_t NMEASURE, const size_t NSHOTS) { +cq_status sm_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS) { cq_exec exec_handle; cq_status status = CQ_ERROR; @@ -151,9 +164,13 @@ cstate * const crp, const size_t NMEASURE, const size_t NSHOTS) { return status; } -cq_status am_qrun(qkern kernel, qubit * qrp, const size_t NQUBITS, -cstate * const crp, const size_t NMEASURE, const size_t NSHOTS, -cq_exec * const ehp) { +cq_status am_qrun(qkern kernel, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS, + cq_exec * const ehp) { RUN_HOST_ONLY(); cq_status status = CQ_ERROR; char * fname = NULL; @@ -184,23 +201,154 @@ cq_exec * const ehp) { return status; } +cq_status sp_qrun(pqkern kernel, + void * kernpar, + const size_t KERNPAR_SIZE, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE) { + RUN_HOST_ONLY(); + cq_status status = CQ_ERROR; + cq_exec exec_handle; + + status = ap_qrun(kernel, kernpar, KERNPAR_SIZE, qrp, NQUBITS, crp, NMEASURE, + &exec_handle); + + if (status == CQ_SUCCESS) { + status = wait_qrun(&exec_handle); + + if (status == CQ_SUCCESS) { + status = exec_handle.status; + } + } + + return status; +} + +cq_status ap_qrun(pqkern kernel, + void * kernpar, + const size_t KERNPAR_SIZE, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + cq_exec * const ehp) { + RUN_HOST_ONLY(); + cq_status status = CQ_ERROR; + char * fname = NULL; + + if (ehp == NULL) return status; + init_exec_handle(NQUBITS, 1, NMEASURE, ehp); + + if (KERNPAR_SIZE < 0) return status; + ehp->params = kernpar; + ehp->params_size = KERNPAR_SIZE; + + if (qrp != NULL && (NMEASURE == 0 || crp != NULL)) { + status = find_pqkern_name(kernel, &fname); + + if (status == CQ_SUCCESS) { + ehp->fname = fname; + ehp->qreg = qrp; + ehp->creg = crp; + + host_send_ctrl_op(CQ_CTRL_RUN_PQKERNEL, ehp); + } else { + finalise_exec_handle(ehp); + } + } else { + finalise_exec_handle(ehp); + } + + return status; +} + +cq_status smp_qrun(pqkern kernel, + void * kernpar, + const size_t KERNPAR_SIZE, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS) { + cq_exec exec_handle; + cq_status status = CQ_ERROR; + + status = amp_qrun(kernel, kernpar, KERNPAR_SIZE, qrp, NQUBITS, crp, NMEASURE, + NSHOTS, &exec_handle); + + if (status == CQ_SUCCESS) { + status = wait_qrun(&exec_handle); + + if (status == CQ_SUCCESS) { + status = exec_handle.status; + } + } + + return status; +} + +cq_status amp_qrun(pqkern kernel, + void * kernpar, + const size_t KERNPAR_SIZE, + qubit * qrp, + const size_t NQUBITS, + cstate * const crp, + const size_t NMEASURE, + const size_t NSHOTS, + cq_exec * const ehp) { + RUN_HOST_ONLY(); + cq_status status = CQ_ERROR; + char * fname = NULL; + + if (ehp == NULL) return status; + init_exec_handle(NQUBITS, NSHOTS, NMEASURE, ehp); + + if (KERNPAR_SIZE < 0) return status; + ehp->params = kernpar; + ehp->params_size = KERNPAR_SIZE; + + if (NSHOTS == 0) { + status = CQ_SUCCESS; + finalise_exec_handle(ehp); + } else if (qrp != NULL && (NMEASURE == 0 || crp != NULL)) { + status = find_pqkern_name(kernel, &fname); + + if (status == CQ_SUCCESS) { + ehp->fname = fname; + ehp->qreg = qrp; + ehp->creg = crp; + host_send_ctrl_op(CQ_CTRL_RUN_PQKERNEL, ehp); + } else { + finalise_exec_handle(ehp); + } + + } else { + finalise_exec_handle(ehp); + } + + return status; +} + // Synchronisation cq_status sync_qrun(cq_exec * const ehp) { + RUN_HOST_ONLY(); cq_status status = CQ_ERROR; if (ehp != NULL && ehp->exec_init) { - //host_sync_exec(ehp); + // host_sync_exec(ehp); host_send_ctrl_op(CQ_CTRL_SYNC_EXEC, ehp); status = CQ_SUCCESS; } - return status; + return status; } cq_status wait_qrun(cq_exec * const ehp) { RUN_HOST_ONLY(); cq_status status = CQ_ERROR; if (ehp != NULL && ehp->exec_init) { - //host_wait_exec(ehp); + // host_wait_exec(ehp); host_send_ctrl_op(CQ_CTRL_WAIT_EXEC, ehp); finalise_exec_handle(ehp); status = CQ_SUCCESS; @@ -212,9 +360,10 @@ cq_status wait_qrun(cq_exec * const ehp) { } cq_status halt_qrun(cq_exec * const ehp) { + RUN_HOST_ONLY(); cq_status status = CQ_ERROR; if (ehp != NULL && ehp->exec_init) { - //host_request_halt(ehp); + // host_request_halt(ehp); host_send_ctrl_op(CQ_CTRL_ABORT, ehp); status = wait_qrun(ehp); } From 63e0309b8d8a3d69d6d60cc380dc9abdbd18d198 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 13 Jul 2026 15:40:15 +0100 Subject: [PATCH 28/38] Removed unused example. --- CMakeLists.txt | 4 ---- mpi-test.c | 51 -------------------------------------------------- 2 files changed, 55 deletions(-) delete mode 100644 mpi-test.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 037be9e..15fcb1f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,10 +124,6 @@ endif() add_subdirectory(src) -add_executable(cq-mpi-test mpi-test.c) -target_link_libraries(cq-mpi-test PRIVATE CQ-SIMBE::cq-simbe) -#target_link_libraries(cq-mpi-test PRIVATE ${MPI_C_LIBRARIES}) - if (ENABLE_TESTING) find_package(unity REQUIRED) diff --git a/mpi-test.c b/mpi-test.c deleted file mode 100644 index fc38080..0000000 --- a/mpi-test.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include "cq.h" - -cq_status test_qkern(const size_t NQUBITS, - qubit* qr, - const size_t NMEASURE, - cstate* cr, - qkern_map* reg) { - CQ_REGISTER_KERNEL(reg) - set_qureg(qr, 0, NQUBITS); - for (size_t i = 0; i < NQUBITS; ++i) { - hadamard(&qr[i]); - } - measure_qureg(qr, NQUBITS, cr); - return CQ_SUCCESS; -} - -int main() { - const size_t NQUBITS = 10; - const size_t NSHOTS = 10; - const size_t NMEASURE = NQUBITS; - - cq_init(1); - qubit* qr; - alloc_qureg(&qr, NQUBITS); - // if (get_rank() == 0) { - // printf( - // "[MAIN]: created qreg: with NQUBITS: %zu, qregistry_idx: %zu, - // offset: " - // "%zu\n", - // qr->N, qr->registry_index, qr->offset); - // printf("\n\nresult of free qureg: %d\n\n", free_qureg(&qr)); - // } - - cstate cr[NMEASURE * NSHOTS]; - init_creg(NMEASURE * NSHOTS, -1, cr); - register_qkern(test_qkern); - - cq_exec eh; - // a_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, &eh); - // wait_qrun(&eh); - // s_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE); - sm_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS); - // am_qrun(test_qkern, qr, NQUBITS, cr, NMEASURE, NSHOTS, &eh); - // halt_qrun(&eh); - // wait_qrun(&eh); - - free_qureg(&qr); - cq_finalise(1); - return 0; -} From c6abbca758b4856b074e88cff47649c241766f35 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 13 Jul 2026 17:14:37 +0100 Subject: [PATCH 29/38] Added new function to initialise CQ with user owned MPI. Updated MPI based example showcasing use of new functionality. --- CMakeLists.txt | 2 +- examples/qft/CMakeLists.txt | 18 +++++- examples/qft/mpi_aqft.c | 86 ++++++++++++++++++++++++++ include/env.h | 6 +- src/host-device/comms.h | 6 ++ src/host-device/comms/comms_core.c | 6 +- src/host-device/comms/comms_mpi.c | 97 ++++++++++++++++++------------ src/host/env.c | 62 ++++++++++++++----- 8 files changed, 226 insertions(+), 57 deletions(-) create mode 100644 examples/qft/mpi_aqft.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 15fcb1f..e956f8f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,7 @@ add_library(CQ-SIMBE::cq-simbe ALIAS cq-simbe) if (BUILD_WITH_MPI) find_package(MPI REQUIRED) include_directories(SYSTEM ${MPI_INCLUDE_PATH}) - target_compile_definitions(cq-simbe PRIVATE + target_compile_definitions(cq-simbe PUBLIC CQ_WITH_MPI_COMMS=1 $<$:DEBUG_MODE> ) diff --git a/examples/qft/CMakeLists.txt b/examples/qft/CMakeLists.txt index 64150b6..ff07a54 100644 --- a/examples/qft/CMakeLists.txt +++ b/examples/qft/CMakeLists.txt @@ -22,4 +22,20 @@ target_link_libraries(aqft PRIVATE CQ-SIMBE::cq-simbe ${MATHS_LIBRARY} -) \ No newline at end of file +) + +if (BUILD_WITH_MPI) + add_executable(mpi_aqft + mpi_aqft.c + qft-utils.c + ) + + find_library(MATHS_LIBRARY m REQUIRED) + + target_link_libraries(mpi_aqft + PRIVATE + CQ-SIMBE::cq-simbe + ${MATHS_LIBRARY} + ${MPI_C_LIBRARIES} + ) +endif() diff --git a/examples/qft/mpi_aqft.c b/examples/qft/mpi_aqft.c new file mode 100644 index 0000000..8f8fb5c --- /dev/null +++ b/examples/qft/mpi_aqft.c @@ -0,0 +1,86 @@ +#include "env.h" +#include "qft.h" + +#include "cq.h" + +#include +#include +#include + +#include + +int main(void) { + int provided; + + MPI_Comm cq_comm; + int nproc; + int rank; + MPI_Init_thread(NULL, NULL, MPI_THREAD_MULTIPLE, &provided); + MPI_Comm_size(MPI_COMM_WORLD, &nproc); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + const int split_cond = rank >= 3; + MPI_Comm_split(MPI_COMM_WORLD, split_cond, rank, &cq_comm); + const size_t NQUBITS = 10; + const size_t NSHOTS = 10; + const size_t NMEASURE = NQUBITS; + + cstate cr_zero[NMEASURE * NSHOTS]; + cstate cr_plus[NMEASURE * NSHOTS]; + cq_exec eh_zero, eh_plus; + + qubit * qr = NULL; + + if (split_cond) { + cq_init_custom_mpi_comm(cq_comm, 1); + + register_qkern(zero_init_full_qft); + register_qkern(plus_init_full_qft); + + CQ_PROG_BEGIN() + + // We will reuse the quantum buffer as the quantum + // kernels cannot run simultaneously (for now...) + alloc_qureg(&qr, NQUBITS); + + init_creg(NMEASURE * NSHOTS, -1, cr_zero); + init_creg(NMEASURE * NSHOTS, -1, cr_plus); + + printf("Offloading both QFT circuits to the quantum device.\n"); + am_qrun(zero_init_full_qft, qr, NQUBITS, cr_zero, NMEASURE, NSHOTS, + &eh_zero); + am_qrun(plus_init_full_qft, qr, NQUBITS, cr_plus, NMEASURE, NSHOTS, + &eh_plus); + + CQ_PROG_END() + } + + if (rank < 3) { + printf("Hello from the host, pretend I'm doing something useful!\n"); + sleep(2); + printf("Hello again, I'm done being 'useful' and will now wait for "); + printf("the quantum device to return!\n"); + } + + if (split_cond) { + CQ_PROG_BEGIN() + + wait_qrun(&eh_zero); + wait_qrun(&eh_plus); + + free_qureg(&qr); + + printf("Results from zero-initialised QFT:\n"); + report_results(cr_zero, NMEASURE, NSHOTS); + + printf("Results from plus-initialised QFT:\n"); + report_results(cr_plus, NMEASURE, NSHOTS); + + CQ_PROG_END() + cq_finalise(0); + } + + MPI_Finalize(); + + return 0; +} diff --git a/include/env.h b/include/env.h index dcaf669..9fdce72 100644 --- a/include/env.h +++ b/include/env.h @@ -15,7 +15,11 @@ extern struct cq_environment cq_env; cq_status cq_init(const unsigned int VERBOSITY); -// cq_status cq_init_custom_mpi_comm(int foo, const unsigned int VERBOSITY); +#if CQ_WITH_MPI_COMMS +#include +cq_status cq_init_custom_mpi_comm(MPI_Comm cq_comm, + const unsigned int VERBOSITY); +#endif cq_status cq_finalise(const unsigned int VERBOSITY); diff --git a/src/host-device/comms.h b/src/host-device/comms.h index c829f41..19b96b4 100644 --- a/src/host-device/comms.h +++ b/src/host-device/comms.h @@ -45,6 +45,12 @@ extern struct dev_link dev_ctrl; int initialise_device(const unsigned int VERBOSITY); +#if CQ_WITH_MPI_COMMS +#include +int initialise_device_with_custom_mpi_comm(MPI_Comm cq_comm, + const unsigned int VERBOSITY); +#endif + size_t host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params); size_t host_wait_all_ops(void); diff --git a/src/host-device/comms/comms_core.c b/src/host-device/comms/comms_core.c index 73980a6..3928e26 100644 --- a/src/host-device/comms/comms_core.c +++ b/src/host-device/comms/comms_core.c @@ -116,6 +116,7 @@ void comms_exec_halt(cq_exec * const ehp) { // NOTE: aka host_wait_all_ops from OG comm.c size_t device_wait_all_ops(void) { pthread_mutex_lock(&dev_ctrl.device_lock); + printf("dev ctrl %d\n", dev_ctrl.num_ops); while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); } @@ -223,7 +224,10 @@ size_t serial_host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params) { break; } } - return insert_op(OP, ctrl_params); + //return insert_op(OP, ctrl_params); + size_t num_ops = insert_op(OP, ctrl_params); + printf("NUM_OPS After insert: %d\n", num_ops); + return num_ops; } int serial_initialise_device(const unsigned int VERBOSITY) { diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index b4f7517..0b1ae72 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -44,6 +44,7 @@ static MPI_Comm CQ_MPI_COMM_WORLD = MPI_COMM_NULL; static MPI_Comm CQ_MPI_SPLIT_COMM = MPI_COMM_NULL; +static bool owns_mpi_comm = false; static int CQ_MPI_HOST_RANK = 0; static int CQ_MPI_DEVICE_RANK = 0; @@ -92,6 +93,12 @@ static bool cq_validate_mpi_rank(const int rank) { return true; } +int initialise_device_with_custom_mpi_comm(MPI_Comm cq_comm, + const unsigned int VERBOSITY) { + MPI_Comm_dup(cq_comm, &CQ_MPI_COMM_WORLD); + return initialise_device(VERBOSITY); +} + int initialise_device(const unsigned int VERBOSITY) { init_host_device_mpi(VERBOSITY); if (mpi_env.world_size == 1) { @@ -153,7 +160,7 @@ void host_device_sync_comms(void) { int finalise_device(const unsigned int VERBOSITY) { if (mpi_env.world_size == 1) { int res = serial_finalise_device(VERBOSITY); - finalise_host_device_mpi(VERBOSITY); + // finalise_host_device_mpi(VERBOSITY); return res; } @@ -179,7 +186,7 @@ bool is_device(void) { if (mpi_env.rank < 0) { cq_log("%s [is_device]: rank is < 0 => MPI not initialised. Exiting!\n", get_comm_source()); - exit(CQ_MPI_RUNTIME_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); } return mpi_env.rank != CQ_MPI_HOST_RANK; @@ -192,7 +199,7 @@ void init_quest_env(void) { // } #if CQ_WITH_MPI_COMMS && CQ_CONF_QUEST_WITH_MPI if (is_quantum_worker()) { - initCustomMpiCommQuESTEnv(get_quest_comm(), 0, 0); + initCustomMpiCommQuESTEnv(CQ_MPI_SPLIT_COMM, 0, 0); } #endif #ifndef CQ_CONF_QUEST_WITH_MPI @@ -210,24 +217,29 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { cq_log("Initialising MPI.\n"); } - int provided; - MPI_Init_thread(NULL, NULL, MPI_THREAD_MULTIPLE, &provided); + if (CQ_MPI_COMM_WORLD == MPI_COMM_NULL) { + owns_mpi_comm = true; - if (provided != MPI_THREAD_MULTIPLE) { - cq_log( - "Failed setting up multithreading with MPI. Requested " - "MPI_THREAD_MULTIPLE, given: %d\n", - provided); - exit(CQ_MPI_RUNTIME_ERROR); - } + int provided; + MPI_Init_thread(NULL, NULL, MPI_THREAD_MULTIPLE, &provided); + + if (provided != MPI_THREAD_MULTIPLE) { + cq_log( + "Failed setting up multithreading with MPI. Requested " + "MPI_THREAD_MULTIPLE, given: %d\n", + provided); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); + } - if (CQ_MPI_COMM_WORLD == MPI_COMM_NULL) { MPI_Comm_dup(MPI_COMM_WORLD, &CQ_MPI_COMM_WORLD); } MPI_Comm_size(CQ_MPI_COMM_WORLD, &mpi_env.world_size); MPI_Comm_rank(CQ_MPI_COMM_WORLD, &mpi_env.rank); + cq_log("CQ comm size: %d\n", mpi_env.world_size); + cq_log("%s in CQ subcomm I'm rank %d\n", get_comm_source(), mpi_env.rank); + // TODO: for multi-device check that: // (nproc - 1) == n_device * power of 2 validate_nproc(mpi_env.world_size); @@ -244,6 +256,11 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { MPI_Comm_split(CQ_MPI_COMM_WORLD, QUANTUM_WORKER, mpi_env.rank, &CQ_MPI_SPLIT_COMM); MPI_Comm_rank(CQ_MPI_SPLIT_COMM, &mpi_env.subcomm_rank); + + int quest_size; + MPI_Comm_size(CQ_MPI_SPLIT_COMM, &quest_size); + cq_log("QuEST comm size: %d\n", quest_size); + cq_log("%s in subcomm I'm rank %d\n", get_comm_source(), mpi_env.subcomm_rank); @@ -266,8 +283,10 @@ void finalise_host_device_mpi(const unsigned int VERBOSITY) { if (VERBOSITY > 0) { cq_log("%s Finalising MPI.\n", get_comm_source()); } - MPI_Barrier(CQ_MPI_COMM_WORLD); - MPI_Finalize(); + if (owns_mpi_comm) { + MPI_Barrier(CQ_MPI_COMM_WORLD); + MPI_Finalize(); + } if (VERBOSITY > 0) { cq_log("%s Finalised MPI.\n", get_comm_source()); } @@ -418,7 +437,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { "queue. We allow up to %d " "concurrent executors per device. Exiting", get_comm_source(), __CQ_DEVICE_QUEUE_SIZE__); - exit(CQ_MPI_RUNTIME_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); } const int host_rank = CQ_MPI_HOST_RANK; cq_exec * tmp_exec = NULL; @@ -452,7 +471,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { "queue. We allow up to %d " "concurrent executors per device. Exiting", get_comm_source(), __CQ_DEVICE_QUEUE_SIZE__); - exit(CQ_MPI_RUNTIME_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); } const int host_rank = CQ_MPI_HOST_RANK; cq_exec * tmp_exec = NULL; @@ -470,7 +489,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { "%s [dispatch][WAIT_EXEC]: device ehp is NULL. Returning. " "Exiting\n", get_comm_source()); - exit(CQ_MPI_RUNTIME_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); } // NOTE: This part needs further tests to ensure no deadlock device_wait_all_ops(); @@ -485,7 +504,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { "%s [dispatch][WAIT_EXEC]: The number of active executors is < 0! " "Should not happen. Exiting", get_comm_source()); - exit(CQ_MPI_RUNTIME_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); } break; } @@ -497,7 +516,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { "%s [dispatch][SYNC_EXEC]: device ehp is NULL. Returning. " "Exiting\n", get_comm_source()); - exit(CQ_MPI_RUNTIME_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); } comms_exec_sync(executor_handles[executor_id]); cq_log("%s [dispatch][SYNC_EXEC]: executor synced\n", get_comm_source()); @@ -533,7 +552,7 @@ void device_dispatch_ctrl_op(const enum ctrl_code OP) { "%s [dispatch][ABORT]: device ehp is NULL. Returning. " "Exiting\n", get_comm_source()); - exit(CQ_MPI_RUNTIME_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); } comms_exec_halt(executor_handles[executor_id]); cq_log("%s [dispatch][ABORT]: executor halted\n", get_comm_source()); @@ -791,7 +810,7 @@ void recv_exec_params(cq_exec ** ehp, const int src) { if (recv_buffer == NULL || *ehp == NULL) { cq_log("%s [recv_exec_params]: malloc failed. Exiting\n", get_comm_source()); - exit(CQ_MPI_MALLOC_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_MALLOC_ERROR); } #if CQ_CONF_QUEST_WITH_MPI @@ -855,28 +874,28 @@ void recv_exec_params(cq_exec ** ehp, const int src) { if ((*ehp)->fname == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->fname failed. Exiting\n", get_comm_source()); - exit(CQ_MPI_MALLOC_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_MALLOC_ERROR); } (*ehp)->qreg = (qubit *)malloc(qreg_size); if ((*ehp)->qreg == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->qreg failed. Exiting\n", get_comm_source()); - exit(CQ_MPI_MALLOC_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_MALLOC_ERROR); } (*ehp)->creg = (cstate *)malloc(creg_size); if ((*ehp)->creg == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->creg failed. Exiting\n", get_comm_source()); - exit(CQ_MPI_MALLOC_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_MALLOC_ERROR); } (*ehp)->params = malloc(params_size); if ((*ehp)->params == NULL) { cq_log("%s [recv_exec_params]: malloc *ehp->params failed. Exiting\n", get_comm_source()); - exit(CQ_MPI_MALLOC_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_MALLOC_ERROR); } } @@ -986,7 +1005,7 @@ void send_exec_params(cq_exec * ehp, const int dest) { if (send_buffer == NULL) { cq_log( "Failed to allocate buffer for sending executor handle. Exiting\n"); - exit(CQ_MPI_MALLOC_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_MALLOC_ERROR); } int position = 0; MPI_Pack(&ehp->id, 1, MPI_UINT64_T, send_buffer, max_buffer_size, &position, @@ -1203,9 +1222,9 @@ bool is_quantum_worker(void) { return mpi_env.rank > 0; } -MPI_Comm get_quest_comm(void) { - return CQ_MPI_SPLIT_COMM; -} +// MPI_Comm get_quest_comm(void) { +// return CQ_MPI_SPLIT_COMM; +// } void validate_nproc(const int nproc) { const int device_nproc = nproc - 1; @@ -1221,7 +1240,7 @@ void validate_nproc(const int nproc) { "Incorrect number of MPI processes. The (N - 1) should be power of " "2!\n"); } - exit(CQ_MPI_RUNTIME_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); } #endif #ifndef CQ_CONF_QUEST_WITH_MPI @@ -1231,17 +1250,17 @@ void validate_nproc(const int nproc) { "Incorrect number of MPI processes. If QuEST uses multi-threading " "only, there should be only 2 MPI processes used for CQ!\n"); } - exit(CQ_MPI_RUNTIME_ERROR); + MPI_Abort(CQ_MPI_COMM_WORLD, CQ_MPI_RUNTIME_ERROR); } #endif // default setup CQ_MPI_DEVICE_RANK = CQ_MPI_HOST_RANK + 1; } -#undef CQ_MPI_HOST_RANK -#undef CQ_MPI_DEVICE_RANK -#undef CQ_MPI_DEVICE_MASTER_RANK -#undef CQ_MPI_WORLD_COMMS_TAG -#undef CQ_MPI_SUBCOMMS_TAG -#undef CQ_MPI_RUNTIME_ERROR -#undef CQ_MPI_MALLOC_ERROR +// #undef CQ_MPI_HOST_RANK +// #undef CQ_MPI_DEVICE_RANK +// #undef CQ_MPI_DEVICE_MASTER_RANK +// #undef CQ_MPI_WORLD_COMMS_TAG +// #undef CQ_MPI_SUBCOMMS_TAG +// #undef CQ_MPI_RUNTIME_ERROR +// #undef CQ_MPI_MALLOC_ERROR diff --git a/src/host/env.c b/src/host/env.c index 187c6ab..4f2c85f 100644 --- a/src/host/env.c +++ b/src/host/env.c @@ -1,25 +1,24 @@ -#include -#include +#include "env.h" #include +#include +#include #include "datatypes.h" -#include "src/host-device/comms.h" #include "opcodes.h" -#include "env.h" +#include "src/host-device/comms.h" -struct cq_environment cq_env = { - .initialised = false, - .finalised = false -}; +struct cq_environment cq_env = { .initialised = false, .finalised = false }; struct dev_link dev_ctrl; cq_status cq_init(const unsigned int VERBOSITY) { - cq_status status = CQ_SUCCESS; + cq_status status = CQ_SUCCESS; if (!cq_env.finalised) { - if (!cq_env.initialised) { + if (!cq_env.initialised) { if (VERBOSITY > 0) { - printf("Initialising CQ Simulated Backend library. QuEST environment report to follow.\n\n"); + printf( + "Initialising CQ Simulated Backend library. QuEST environment " + "report to follow.\n\n"); } initialise_device(VERBOSITY); @@ -32,19 +31,54 @@ cq_status cq_init(const unsigned int VERBOSITY) { status = CQ_WARNING; } } else { - printf("CQ-SimBE cannot be reinitialised once finalised! This would break QuEST.\n"); + printf( + "CQ-SimBE cannot be reinitialised once finalised! This would break " + "QuEST.\n"); status = CQ_ERROR; } return status; } +#if CQ_WITH_MPI_COMMS +cq_status cq_init_custom_mpi_comm(MPI_Comm cq_comm, + const unsigned int VERBOSITY) { + cq_status status = CQ_SUCCESS; + + if (!cq_env.finalised) { + if (!cq_env.initialised) { + if (VERBOSITY > 0) { + printf( + "Initialising CQ Simulated Backend library. QuEST environment " + "report to follow.\n\n"); + } + + initialise_device_with_custom_mpi_comm(cq_comm, VERBOSITY); + + cq_env.initialised = true; + } else { + if (VERBOSITY > 0) { + printf("CQ-SimBE is already initialised. No need to do it again.\n"); + } + status = CQ_WARNING; + } + } else { + printf( + "CQ-SimBE cannot be reinitialised once finalised! This would break " + "QuEST.\n"); + status = CQ_ERROR; + } + + return status; +} + +#endif + cq_status cq_finalise(const unsigned int VERBOSITY) { cq_status status = CQ_SUCCESS; if (!cq_env.finalised) { - if (VERBOSITY > 0) - printf("Host finalising\n"); + if (VERBOSITY > 0) printf("Host finalising\n"); finalise_device(VERBOSITY); cq_env.finalised = true; From 610ec59e8b5f3b786c2bbc8aa9aaa91bcc324c91 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 13 Jul 2026 20:09:54 +0100 Subject: [PATCH 30/38] Cleaned up code. --- src/host-device/comms/comms_core.c | 6 +----- src/host-device/comms/comms_mpi.c | 23 +++++++++-------------- src/host-device/comms/comms_mpi.h | 27 +++++++++++---------------- 3 files changed, 21 insertions(+), 35 deletions(-) diff --git a/src/host-device/comms/comms_core.c b/src/host-device/comms/comms_core.c index 3928e26..73980a6 100644 --- a/src/host-device/comms/comms_core.c +++ b/src/host-device/comms/comms_core.c @@ -116,7 +116,6 @@ void comms_exec_halt(cq_exec * const ehp) { // NOTE: aka host_wait_all_ops from OG comm.c size_t device_wait_all_ops(void) { pthread_mutex_lock(&dev_ctrl.device_lock); - printf("dev ctrl %d\n", dev_ctrl.num_ops); while (dev_ctrl.num_ops > 0 || dev_ctrl.device_busy) { pthread_cond_wait(&dev_ctrl.cond_device_busy, &dev_ctrl.device_lock); } @@ -224,10 +223,7 @@ size_t serial_host_send_ctrl_op(const enum ctrl_code OP, void * ctrl_params) { break; } } - //return insert_op(OP, ctrl_params); - size_t num_ops = insert_op(OP, ctrl_params); - printf("NUM_OPS After insert: %d\n", num_ops); - return num_ops; + return insert_op(OP, ctrl_params); } int serial_initialise_device(const unsigned int VERBOSITY) { diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index 0b1ae72..87a0b6c 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -6,9 +6,9 @@ #include "datatypes.h" #include "src/host/opcodes.h" -// #ifndef CQ_CONF_QUEST_WITH_MPI -// #include "quest/include/environment.h" -// #endif +#ifndef CQ_CONF_QUEST_WITH_MPI +#include "quest/include/environment.h" +#endif #if CQ_CONF_QUEST_WITH_MPI #include "quest/include/subcommunicator.h" @@ -160,7 +160,7 @@ void host_device_sync_comms(void) { int finalise_device(const unsigned int VERBOSITY) { if (mpi_env.world_size == 1) { int res = serial_finalise_device(VERBOSITY); - // finalise_host_device_mpi(VERBOSITY); + finalise_host_device_mpi(VERBOSITY); return res; } @@ -193,13 +193,12 @@ bool is_device(void) { } void init_quest_env(void) { -// if (mpi_env.world_size == 1) { -// initQuESTEnv(); -// return; -// } #if CQ_WITH_MPI_COMMS && CQ_CONF_QUEST_WITH_MPI - if (is_quantum_worker()) { + if (mpi_env.world_size == 1) { + initCustomMpiQuESTEnv(0, 1, 0, 0); + } else if (is_quantum_worker()) { initCustomMpiCommQuESTEnv(CQ_MPI_SPLIT_COMM, 0, 0); + } else { } #endif #ifndef CQ_CONF_QUEST_WITH_MPI @@ -246,11 +245,11 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { if (mpi_env.world_size == 1) { MPI_Barrier(CQ_MPI_COMM_WORLD); + MPI_Comm_dup(CQ_MPI_COMM_WORLD, &CQ_MPI_SPLIT_COMM); return; } #if CQ_CONF_QUEST_WITH_MPI - // const int QUANTUM_WORKER = mpi_env.rank > 0; const int QUANTUM_WORKER = mpi_env.rank >= CQ_MPI_DEVICE_RANK; MPI_Barrier(CQ_MPI_COMM_WORLD); MPI_Comm_split(CQ_MPI_COMM_WORLD, QUANTUM_WORKER, mpi_env.rank, @@ -1222,10 +1221,6 @@ bool is_quantum_worker(void) { return mpi_env.rank > 0; } -// MPI_Comm get_quest_comm(void) { -// return CQ_MPI_SPLIT_COMM; -// } - void validate_nproc(const int nproc) { const int device_nproc = nproc - 1; if (nproc == 1) { diff --git a/src/host-device/comms/comms_mpi.h b/src/host-device/comms/comms_mpi.h index ec412ff..05309e6 100644 --- a/src/host-device/comms/comms_mpi.h +++ b/src/host-device/comms/comms_mpi.h @@ -42,7 +42,7 @@ void device_finalise_comms(const unsigned int VERBOSITY); /// signature /// @return arbitrary return data to satisfy pthread function signature. /// Currently always returns NULL. -void* device_listen(void* args); +void * device_listen(void * args); /// /// dispatches recieved control operation to the worker thread. @@ -63,20 +63,20 @@ void device_wait_comms(void); /// communicates control parameters between host and device. /// @param OP an enum argument specifying CQ_CTRL_OP /// @param[in,out] params void pointer to arbitrary params -void host_comm_params(const enum ctrl_code OP, void* params); +void host_comm_params(const enum ctrl_code OP, void * params); /// /// receives allocation parameters from the source. /// @param[out] params reference to parameters to store the results of /// communication /// @param src source rank of incoming message -void recv_alloc_params(device_alloc_params* params, const int src); +void recv_alloc_params(device_alloc_params * params, const int src); /// /// sends allocation parameters to the destination. /// @param[in] params reference to parameters to communicate /// @param dest destination rank of outgoing message -void send_alloc_params(const device_alloc_params* params, const int dest); +void send_alloc_params(const device_alloc_params * params, const int dest); /// /// receives executor id used for matching results with correct executor. @@ -96,17 +96,17 @@ void send_exec_id(const size_t id, const int dest); /// device_free_exec. /// @param[out] ehp executor handle used for host-device offloading /// @param src source rank of incoming message -void recv_exec_params(cq_exec** ehp, const int src); +void recv_exec_params(cq_exec ** ehp, const int src); /// sends updated executor handle to the destination. /// @param[in] ehp executor handle used for host-device offloading /// @param dest destination rank of outgoing message -void send_exec_params(cq_exec* ehp, const int dest); +void send_exec_params(cq_exec * ehp, const int dest); /// /// frees on-device memory pointed to by executor handle. /// @param[in,out] ehp executor handle used for host-device offloading -void device_free_exec(cq_exec** ehp); +void device_free_exec(cq_exec ** ehp); // ---------------------------------------------------------------------------- // Helpers @@ -116,7 +116,7 @@ void device_free_exec(cq_exec** ehp); /// returns the name of either host or the device. Used for printing /// diagnostics. /// @return name of the device (or host). -const char* get_comm_source(void); +const char * get_comm_source(void); /// /// returns the current MPI rank. @@ -127,17 +127,17 @@ int get_rank(void); /// converts CQ control code to string. /// @param OP an enum argument specifying CQ_CTRL_OP /// @return string represntation of OP. -const char* op_to_str(const enum ctrl_code OP); +const char * op_to_str(const enum ctrl_code OP); /// /// prints values of the alloc parameters. /// @param[in] params reference to parameters -void print_alloc_params(const device_alloc_params* params); +void print_alloc_params(const device_alloc_params * params); /// /// prints members of the executor. /// @param[in] ehp reference to executor -void print_ehp(const cq_exec* ehp); +void print_ehp(const cq_exec * ehp); // /// @@ -146,11 +146,6 @@ void print_ehp(const cq_exec* ehp); /// @return bool is_quantum_worker(void); -/// -/// accessor to the subcommunicator intended for QuEST simulation. -/// @return MPI Subcommunicator used to initialise QuEST environment -MPI_Comm get_quest_comm(void); - /// validates the number of MPI processes to meet QuEST constraints. /// Exits program if check failed. /// @param nproc number of MPI processes From e970ff8da44b4fc6abe32c4ea463bd8f16aca7a7 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 13 Jul 2026 20:51:10 +0100 Subject: [PATCH 31/38] Updated MPI example. --- examples/qft/mpi_aqft.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/qft/mpi_aqft.c b/examples/qft/mpi_aqft.c index 8f8fb5c..bc520c1 100644 --- a/examples/qft/mpi_aqft.c +++ b/examples/qft/mpi_aqft.c @@ -55,7 +55,7 @@ int main(void) { CQ_PROG_END() } - if (rank < 3) { + if (rank <= 3) { printf("Hello from the host, pretend I'm doing something useful!\n"); sleep(2); printf("Hello again, I'm done being 'useful' and will now wait for "); From 884863b267bc5eb50bcf0b81bb1038b04f56eb5b Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Mon, 13 Jul 2026 21:14:58 +0100 Subject: [PATCH 32/38] Modified MPI comms to pass all tests. --- src/host-device/comms/comms_mpi.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index 87a0b6c..00f39ed 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -50,6 +50,7 @@ static int CQ_MPI_HOST_RANK = 0; static int CQ_MPI_DEVICE_RANK = 0; struct cq_mpi_env { + bool is_init; int rank; int subcomm_rank; unsigned int verbosity; @@ -59,6 +60,7 @@ struct cq_mpi_env { static cq_exec * executor_handles[__CQ_DEVICE_QUEUE_SIZE__]; static size_t num_active_executors = 0; static struct cq_mpi_env mpi_env = { + .is_init = false, .rank = -1, .subcomm_rank = -1, .verbosity = 0, @@ -93,6 +95,10 @@ static bool cq_validate_mpi_rank(const int rank) { return true; } +static bool is_serial() { + return mpi_env.world_size == 1 || !mpi_env.is_init; +} + int initialise_device_with_custom_mpi_comm(MPI_Comm cq_comm, const unsigned int VERBOSITY) { MPI_Comm_dup(cq_comm, &CQ_MPI_COMM_WORLD); @@ -100,8 +106,12 @@ int initialise_device_with_custom_mpi_comm(MPI_Comm cq_comm, } int initialise_device(const unsigned int VERBOSITY) { - init_host_device_mpi(VERBOSITY); - if (mpi_env.world_size == 1) { + if (!mpi_env.is_init) { + init_host_device_mpi(VERBOSITY); + mpi_env.is_init = true; + } + + if (is_serial()) { return serial_initialise_device(VERBOSITY); } @@ -113,7 +123,7 @@ int initialise_device(const unsigned int VERBOSITY) { } size_t host_send_ctrl_op(const enum ctrl_code OP, void * params) { - if (mpi_env.world_size == 1) { + if (is_serial()) { return serial_host_send_ctrl_op(OP, params); } @@ -130,7 +140,7 @@ size_t host_send_ctrl_op(const enum ctrl_code OP, void * params) { } size_t host_wait_all_ops(void) { - if (mpi_env.world_size == 1) { + if (is_serial()) { return serial_host_wait_all_ops(); } @@ -147,7 +157,7 @@ size_t host_wait_all_ops(void) { } void host_device_sync_comms(void) { - if (mpi_env.world_size == 1) { + if (is_serial()) { return serial_host_device_sync_comms(); } @@ -158,7 +168,7 @@ void host_device_sync_comms(void) { } int finalise_device(const unsigned int VERBOSITY) { - if (mpi_env.world_size == 1) { + if (is_serial()) { int res = serial_finalise_device(VERBOSITY); finalise_host_device_mpi(VERBOSITY); return res; @@ -183,6 +193,10 @@ int finalise_device(const unsigned int VERBOSITY) { } bool is_device(void) { + if (!mpi_env.is_init) { + return false; + } + if (mpi_env.rank < 0) { cq_log("%s [is_device]: rank is < 0 => MPI not initialised. Exiting!\n", get_comm_source()); @@ -194,7 +208,7 @@ bool is_device(void) { void init_quest_env(void) { #if CQ_WITH_MPI_COMMS && CQ_CONF_QUEST_WITH_MPI - if (mpi_env.world_size == 1) { + if (is_serial()) { initCustomMpiQuESTEnv(0, 1, 0, 0); } else if (is_quantum_worker()) { initCustomMpiCommQuESTEnv(CQ_MPI_SPLIT_COMM, 0, 0); @@ -243,7 +257,7 @@ void init_host_device_mpi(const unsigned int VERBOSITY) { // (nproc - 1) == n_device * power of 2 validate_nproc(mpi_env.world_size); - if (mpi_env.world_size == 1) { + if (is_serial()) { MPI_Barrier(CQ_MPI_COMM_WORLD); MPI_Comm_dup(CQ_MPI_COMM_WORLD, &CQ_MPI_SPLIT_COMM); return; From 9a39a0632a8012d8207d45a7fd6bc8dddb927e54 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 14 Jul 2026 11:10:53 +0100 Subject: [PATCH 33/38] Added fortran wrappers and interface to use new macros and user-owned subcommunicator. Updated examples. --- examples/fortran/plus_qft.f90 | 12 ++++++++---- examples/fortran/zero_qft.f90 | 17 ++++++++++------- include/env.h | 4 ++++ include/fortran/cq.f90 | 13 +++++++++++++ include/fortran/cqf.h | 10 +++++++--- src/host/env.c | 20 +++++++++++++++++--- src/host/fortran/host_int.f90 | 13 +++++++++++++ src/host/fortran/host_ops.f90 | 8 ++++++++ 8 files changed, 80 insertions(+), 17 deletions(-) diff --git a/examples/fortran/plus_qft.f90 b/examples/fortran/plus_qft.f90 index 222e7df..7c92fcb 100644 --- a/examples/fortran/plus_qft.f90 +++ b/examples/fortran/plus_qft.f90 @@ -19,6 +19,12 @@ program plus_qft write(*,'(A,I4)') 'cq_init returned: ',ireturn +reg_status = cq_register_qkern(plus_state_qft) + +write(*,'(A,I4)') 'after register_qkern: ', reg_status + +CQ_PROG_BEGIN() + alloc_status = cq_alloc_qureg(qrc, NQUBITS) write(*,'(A,I4)') 'alloc_qureg returned: ', alloc_status @@ -29,10 +35,6 @@ program plus_qft write(*,'(A)') 'after init_creg' -reg_status = cq_register_qkern(plus_state_qft) - -write(*,'(A,I4)') 'after register_qkern: ', reg_status - qrun_status = cq_sm_qrun(plus_state_qft, qrc, NQUBITS, cr, NMEASURE, NSHOTS) write(*,'(A,I4)') 'after sm_qrun: ', qrun_status @@ -45,6 +47,8 @@ program plus_qft write(*,'(A,I4)') 'free_status returned: ', free_status +CQ_PROG_END() + freturn = cq_finalise(0) write(*,'(A,I4)') 'cq_finalise returned: ',freturn diff --git a/examples/fortran/zero_qft.f90 b/examples/fortran/zero_qft.f90 index 6e5558a..b146ee1 100644 --- a/examples/fortran/zero_qft.f90 +++ b/examples/fortran/zero_qft.f90 @@ -15,10 +15,16 @@ program zero_qft write(*,'(A)') 'before init' -ireturn = cq_init(0) +ireturn = cq_init(1) write(*,'(A,I4)') 'cq_init returned: ',ireturn +reg_status = cq_register_qkern(zero_state_qft) + +write(*,'(A,I4)') 'after register_qkern: ', reg_status + +CQ_PROG_BEGIN() + alloc_status = cq_alloc_qureg(qrc, NQUBITS) write(*,'(A,I4)') 'alloc_qureg returned: ', alloc_status @@ -29,11 +35,6 @@ program zero_qft write(*,'(A)') 'after init_creg' - -reg_status = cq_register_qkern(zero_state_qft) - -write(*,'(A,I4)') 'after register_qkern: ', reg_status - qrun_status = cq_sm_qrun(zero_state_qft, qrc, NQUBITS, cr, NMEASURE, NSHOTS) write(*,'(A,I4)') 'after sm_qrun: ', qrun_status @@ -46,7 +47,9 @@ program zero_qft write(*,'(A,I4)') 'free_status returned: ', free_status -freturn = cq_finalise(0) +CQ_PROG_END() + +freturn = cq_finalise(1) write(*,'(A,I4)') 'cq_finalise returned: ',freturn diff --git a/include/env.h b/include/env.h index 9fdce72..69f1cc7 100644 --- a/include/env.h +++ b/include/env.h @@ -23,4 +23,8 @@ cq_status cq_init_custom_mpi_comm(MPI_Comm cq_comm, cq_status cq_finalise(const unsigned int VERBOSITY); +// fortran helper -- just don't use it from C +cq_status fort_init_custom_mpi_comm(int * cq_comm, + const unsigned int VERBOSITY); + #endif diff --git a/include/fortran/cq.f90 b/include/fortran/cq.f90 index 10eb3fe..5b211dc 100644 --- a/include/fortran/cq.f90 +++ b/include/fortran/cq.f90 @@ -50,12 +50,25 @@ module function cq_init(VERBOSITY) result(status) integer :: status end function cq_init + module function cq_init_custom_mpi_comm(cq_comm, VERBOSITY) result(status) + implicit none + integer :: cq_comm + integer, value :: VERBOSITY + integer :: status + end function cq_init_custom_mpi_comm + module function cq_finalise(VERBOSITY) result(status) implicit none integer, value :: VERBOSITY integer :: status end function cq_finalise + module function cq_is_device() result(res) + implicit none + logical :: res + end function cq_is_device + + ! Resource management module function cq_alloc_qubit(qhp) result(status) diff --git a/include/fortran/cqf.h b/include/fortran/cqf.h index 8b6af02..5ece40a 100644 --- a/include/fortran/cqf.h +++ b/include/fortran/cqf.h @@ -1,3 +1,7 @@ -#define CQ_REGISTER_KERNEL(fname, reg) \ - status = cq_register_fort_kernel(fname, reg); \ - if (status == 0) return +#define CQ_REGISTER_KERNEL(fname, reg) \ + status = cq_register_fort_kernel(fname, reg); \ + if (status == 0) return + +#define CQ_PROG_BEGIN() \ + if (.NOT.cq_is_device()) then +#define CQ_PROG_END() end if diff --git a/src/host/env.c b/src/host/env.c index 4f2c85f..4d08763 100644 --- a/src/host/env.c +++ b/src/host/env.c @@ -1,10 +1,12 @@ #include "env.h" + +#include "datatypes.h" +#include "src/host-device/comms.h" + #include #include #include -#include "datatypes.h" -#include "opcodes.h" -#include "src/host-device/comms.h" +#include struct cq_environment cq_env = { .initialised = false, .finalised = false }; @@ -91,3 +93,15 @@ cq_status cq_finalise(const unsigned int VERBOSITY) { return status; } + +// fortran helper -- just don't use it from C +cq_status fort_init_custom_mpi_comm(int * cq_comm, + const unsigned int VERBOSITY) { +#if CQ_WITH_MPI_COMMS + MPI_Comm c_cq_comm; + c_cq_comm = MPI_Comm_f2c(*cq_comm); + return cq_init_custom_mpi_comm(c_cq_comm, VERBOSITY); +#endif + + return cq_init(VERBOSITY); +} diff --git a/src/host/fortran/host_int.f90 b/src/host/fortran/host_int.f90 index 6abb607..d466892 100644 --- a/src/host/fortran/host_int.f90 +++ b/src/host/fortran/host_int.f90 @@ -13,6 +13,14 @@ function fortran_cq_init(VERBOSITY) bind(C, name="cq_init") integer(c_int) :: fortran_cq_init end function fortran_cq_init + function fortran_cq_init_custom_mpi_comm(cq_comm, VERBOSITY) bind(C, name="fort_init_custom_mpi_comm") + use, intrinsic :: iso_c_binding, only: c_int + implicit none + integer(c_int) :: cq_comm + integer(c_int), value :: VERBOSITY ! passes integer + integer(c_int) :: fortran_cq_init_custom_mpi_comm + end function fortran_cq_init_custom_mpi_comm + function fortran_cq_finalise(VERBOSITY) bind(C, name="cq_finalise") use, intrinsic :: iso_c_binding, only: c_int implicit none @@ -20,6 +28,11 @@ function fortran_cq_finalise(VERBOSITY) bind(C, name="cq_finalise") integer(c_int) :: fortran_cq_finalise end function fortran_cq_finalise + function is_device() bind(C, name="is_device") + use, intrinsic :: iso_c_binding, only: c_bool + logical(c_bool) :: is_device + end function is_device + !cq_status alloc_qubit(qubit ** qhp); function alloc_qubit(qhp) bind(C) use, intrinsic :: iso_c_binding, only: c_int, c_ptr diff --git a/src/host/fortran/host_ops.f90 b/src/host/fortran/host_ops.f90 index 5020735..c86c90e 100644 --- a/src/host/fortran/host_ops.f90 +++ b/src/host/fortran/host_ops.f90 @@ -11,10 +11,18 @@ status = fortran_cq_init(VERBOSITY) end procedure cq_init + module procedure cq_init_custom_mpi_comm !(cq_comm, VERBOSITY) result(status) + status = fortran_cq_init_custom_mpi_comm(cq_comm, VERBOSITY) + end procedure cq_init_custom_mpi_comm + module procedure cq_finalise !(VERBOSITY) result(status) status = fortran_cq_finalise(VERBOSITY) end procedure cq_finalise + module procedure cq_is_device !() result(res) + res = is_device() + end procedure cq_is_device + module procedure cq_alloc_qubit !(qhp) result(status) status = alloc_qubit(qhp%this) end procedure cq_alloc_qubit From a4e66e8156fadb55036ca3d226be120bc4dd17a1 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 14 Jul 2026 11:11:18 +0100 Subject: [PATCH 34/38] Fixed bug when initialising MPI. --- include/utils.h | 3 --- src/host-device/comms/comms_mpi.c | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/utils.h b/include/utils.h index a8ad938..a82c6ca 100644 --- a/include/utils.h +++ b/include/utils.h @@ -17,7 +17,4 @@ void report_results(cstate const * const CR, } \ }; -#define CQ_PROG_BEGIN() if (!is_device()) { -#define CQ_PROG_END() } - #endif diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index 00f39ed..4e66eb5 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -107,8 +107,8 @@ int initialise_device_with_custom_mpi_comm(MPI_Comm cq_comm, int initialise_device(const unsigned int VERBOSITY) { if (!mpi_env.is_init) { - init_host_device_mpi(VERBOSITY); mpi_env.is_init = true; + init_host_device_mpi(VERBOSITY); } if (is_serial()) { From 51968f4c60221fc9d13e27805179c9ee517a8a10 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 14 Jul 2026 11:11:41 +0100 Subject: [PATCH 35/38] Updated code formating of vqe example. --- examples/vqe/vqe.h | 45 +++++++++++++++----- examples/vqe/vqe_utils.c | 88 ++++++++++++++++++++++++++-------------- 2 files changed, 92 insertions(+), 41 deletions(-) diff --git a/examples/vqe/vqe.h b/examples/vqe/vqe.h index 868248b..2bbdf83 100644 --- a/examples/vqe/vqe.h +++ b/examples/vqe/vqe.h @@ -37,18 +37,43 @@ typedef struct hamiltonian { extern hamiltonian h2_hamil; void init_hf_state(qubit * qr, int num_spin_orbitals); -cq_status ansatz(const size_t NQUBITS, qubit * qr, const size_t NMEASURE, cstate * cr, void * kernpar, pqkern_map * reg); +cq_status ansatz(const size_t NQUBITS, + qubit * qr, + const size_t NMEASURE, + cstate * cr, + void * kernpar, + pqkern_map * reg); -static double get_term_expectation(int *histogram, int num_bins, - const size_t NMEASURE, const size_t NSHOTS, double coeff); +static double get_term_expectation(int * histogram, + int num_bins, + const size_t NMEASURE, + const size_t NSHOTS, + double coeff); -void init_vqe_settings(vqe_settings *settings, qubit * qr, cstate * cr, - const size_t NQUBITS, const size_t NSHOTS); -void init_vqe_params(double *params, ptrdiff_t num_params); -double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHOTS, const double * x); -double vqe_iter_nlopt(unsigned int n, const double *x, double *grad, void *f_data); -double vqe_optimize(qubit * qr, cstate * cr, const size_t NQUBITS, - const size_t NMEASURE, const size_t NSHOTS); +void init_vqe_settings(vqe_settings * settings, + qubit * qr, + cstate * cr, + const size_t NQUBITS, + const size_t NSHOTS); + +void init_vqe_params(double * params, ptrdiff_t num_params); + +double vqe_iter(qubit * qr, + cstate * cr, + const size_t NQUBITS, + const size_t NSHOTS, + const double * x); + +double vqe_iter_nlopt(unsigned int n, + const double * x, + double * grad, + void * f_data); + +double vqe_optimize(qubit * qr, + cstate * cr, + const size_t NQUBITS, + const size_t NMEASURE, + const size_t NSHOTS); #endif diff --git a/examples/vqe/vqe_utils.c b/examples/vqe/vqe_utils.c index 8408a4f..47f2e82 100644 --- a/examples/vqe/vqe_utils.c +++ b/examples/vqe/vqe_utils.c @@ -1,5 +1,5 @@ -#include "vqe.h" #include +#include "vqe.h" #include "nlopt.h" @@ -9,7 +9,12 @@ void init_hf_state(qubit * qr, int num_spin_orbitals) { } } -cq_status ansatz(const size_t NQUBITS, qubit * qr, const size_t NMEASURE, cstate * cr, void * kernpar, pqkern_map * reg) { +cq_status ansatz(const size_t NQUBITS, + qubit * qr, + const size_t NMEASURE, + cstate * cr, + void * kernpar, + pqkern_map * reg) { CQ_REGISTER_KERNEL(reg) double * params = (double *)kernpar; @@ -26,24 +31,24 @@ cq_status ansatz(const size_t NQUBITS, qubit * qr, const size_t NMEASURE, cstate if (j < NQUBITS - 1) { int control = j; - int target = j + 1; - cpauliz(&qr[control], &qr[target]); + int target = j + 1; + cpauliz(&qr[control], &qr[target]); } // Changing basis to get expectation value if (i == NLAYERS - 1) { - char pauli = h2_hamil.paulis[h2_hamil.term_start_idx + j]; + char pauli = h2_hamil.paulis[h2_hamil.term_start_idx + j]; if (pauli == 'X') { hadamard(&qr[j]); measure_qubit(&qr[j], &cr[j]); - + } else if (pauli == 'Y') { rotx(&qr[j], M_PI / 2.0); measure_qubit(&qr[j], &cr[j]); - + } else if (pauli == 'Z') { measure_qubit(&qr[j], &cr[j]); - + } else { // On I -- do nothing } @@ -62,8 +67,11 @@ static int count_ones(int dec_idx, const size_t NMEASURE) { return num_ones; } -double get_term_expectation(int *histogram, int num_bins, - const size_t NMEASURE, const size_t NSHOTS, double coeff) { +double get_term_expectation(int * histogram, + int num_bins, + const size_t NMEASURE, + const size_t NSHOTS, + double coeff) { double term_expectation = 0.0; for (ptrdiff_t i = 0; i < num_bins; ++i) { @@ -79,7 +87,7 @@ double get_term_expectation(int *histogram, int num_bins, } #define EXAMPLE_SEED 102141 -void init_vqe_params(double *params, ptrdiff_t num_params) { +void init_vqe_params(double * params, ptrdiff_t num_params) { const double min_val = PARAM_MIN; const double max_val = PARAM_MAX; const int min = 0; @@ -94,15 +102,18 @@ void init_vqe_params(double *params, ptrdiff_t num_params) { } #undef EXAMPLE_SEED -void init_vqe_settings(vqe_settings *settings, qubit * qr, cstate * cr, - const size_t NQUBITS, const size_t NSHOTS) { +void init_vqe_settings(vqe_settings * settings, + qubit * qr, + cstate * cr, + const size_t NQUBITS, + const size_t NSHOTS) { settings->qr = qr; settings->cr = cr; settings->NQUBITS = NQUBITS; settings->NSHOTS = NSHOTS; } -static int result_to_int(cstate *cr, int num_qubits) { +static int result_to_int(cstate * cr, int num_qubits) { int result = 0; int base = 1; for (ptrdiff_t i = num_qubits - 1; i >= 0; --i) { @@ -116,8 +127,12 @@ static int result_to_int(cstate *cr, int num_qubits) { #define MAX_BINS 1 << MAX_NQUBITS -double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHOTS, const double * x) { - int histogram[MAX_BINS] = {0}; +double vqe_iter(qubit * qr, + cstate * cr, + const size_t NQUBITS, + const size_t NSHOTS, + const double * x) { + int histogram[MAX_BINS] = { 0 }; const ptrdiff_t num_bins = (ptrdiff_t)1 << NQUBITS; double expectation = 0.0; const size_t NMEASURE = NQUBITS; @@ -126,7 +141,8 @@ double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHO for (ptrdiff_t i = 0; i < NTERMS; ++i) { ptrdiff_t paulis_start = i * NQUBITS; - smp_qrun(ansatz, x, NPARAMS * sizeof(double), qr, NQUBITS, cr, NMEASURE, NSHOTS); + smp_qrun(ansatz, x, NPARAMS * sizeof(double), qr, NQUBITS, cr, NMEASURE, + NSHOTS); for (ptrdiff_t j = 0; j < NSHOTS; ++j) { ptrdiff_t cr_start = j * NQUBITS; @@ -134,7 +150,8 @@ double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHO } double coeff = h2_hamil.coeffs[i]; - expectation += get_term_expectation(histogram, num_bins, NMEASURE, NSHOTS, coeff); + expectation + += get_term_expectation(histogram, num_bins, NMEASURE, NSHOTS, coeff); h2_hamil.term_start_idx += NQUBITS; } return expectation; @@ -142,10 +159,14 @@ double vqe_iter(qubit * qr, cstate * cr, const size_t NQUBITS, const size_t NSHO #undef MAX_BINS -double vqe_iter_nlopt(unsigned int n, const double *x, double *grad, void *f_data) { - vqe_settings *settings = (vqe_settings *)(f_data); +double vqe_iter_nlopt(unsigned int n, + const double * x, + double * grad, + void * f_data) { + vqe_settings * settings = (vqe_settings *)(f_data); - double energy = vqe_iter(settings->qr, settings->cr, settings->NQUBITS, settings->NSHOTS, x); + double energy = vqe_iter(settings->qr, settings->cr, settings->NQUBITS, + settings->NSHOTS, x); printf("Iter: %td -- Expectation: %f\n", settings->iter, energy); ++settings->iter; @@ -154,7 +175,8 @@ double vqe_iter_nlopt(unsigned int n, const double *x, double *grad, void *f_dat // estimate gradient if using gradient-based optimizer for (ptrdiff_t i = 0; i < NPARAMS; ++i) { - grad[i] = (energy - settings->prev_energy) / (x[i] - settings->prev_params[i]); + grad[i] + = (energy - settings->prev_energy) / (x[i] - settings->prev_params[i]); settings->prev_params[i] = x[i]; } settings->prev_energy = energy; @@ -162,26 +184,30 @@ double vqe_iter_nlopt(unsigned int n, const double *x, double *grad, void *f_dat return energy; } -double vqe_optimize(qubit * qr, cstate * cr, const size_t NQUBITS, - const size_t NMEASURE, const size_t NSHOTS) { - vqe_settings settings = {0}; +double vqe_optimize(qubit * qr, + cstate * cr, + const size_t NQUBITS, + const size_t NMEASURE, + const size_t NSHOTS) { + vqe_settings settings = { 0 }; init_vqe_settings(&settings, qr, cr, NQUBITS, NSHOTS); init_vqe_params(settings.params, NPARAMS); // gradient-free optimizers nlopt_opt opt = nlopt_create(NLOPT_LN_COBYLA, NPARAMS); - //nlopt_opt opt = nlopt_create(NLOPT_LN_SBPLX, NPARAMS); - + // nlopt_opt opt = nlopt_create(NLOPT_LN_SBPLX, NPARAMS); + // gradient-based optimizers - //nlopt_opt opt = nlopt_create(NLOPT_LD_LBFGS, NPARAMS); - //nlopt_opt opt = nlopt_create(NLOPT_LD_SLSQP, NPARAMS); - + // nlopt_opt opt = nlopt_create(NLOPT_LD_LBFGS, NPARAMS); + // nlopt_opt opt = nlopt_create(NLOPT_LD_SLSQP, NPARAMS); + nlopt_set_maxeval(opt, 200); double ftol = 0.0001; nlopt_set_ftol_rel(opt, ftol); - nlopt_result res = nlopt_set_min_objective(opt, vqe_iter_nlopt, (void *)&settings); + nlopt_result res + = nlopt_set_min_objective(opt, vqe_iter_nlopt, (void *)&settings); nlopt_set_lower_bounds1(opt, PARAM_MIN); nlopt_set_upper_bounds1(opt, PARAM_MAX); From bce8020b7e5cd0917ad4b47accabdb1ac702d412 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 14 Jul 2026 11:40:36 +0100 Subject: [PATCH 36/38] Added Fortran MPI example using CQ with subcommunicator. --- CMakeLists.txt | 4 +- examples/fortran/CMakeLists.txt | 16 ++++ examples/fortran/mpi_plus_qft.f90 | 129 ++++++++++++++++++++++++++++++ 3 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 examples/fortran/mpi_plus_qft.f90 diff --git a/CMakeLists.txt b/CMakeLists.txt index e956f8f..640533d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ cmake_minimum_required(VERSION 3.21) project(CQ-SIMBE VERSION 0.1.0 DESCRIPTION "CQ compliant simulated backend." - LANGUAGES C) + LANGUAGES C Fortran) # User configure options @@ -65,7 +65,7 @@ add_library(cq-simbe) add_library(CQ-SIMBE::cq-simbe ALIAS cq-simbe) if (BUILD_WITH_MPI) - find_package(MPI REQUIRED) + find_package(MPI COMPONENTS Fortran C REQUIRED) include_directories(SYSTEM ${MPI_INCLUDE_PATH}) target_compile_definitions(cq-simbe PUBLIC CQ_WITH_MPI_COMMS=1 diff --git a/examples/fortran/CMakeLists.txt b/examples/fortran/CMakeLists.txt index e8a807a..472c07e 100644 --- a/examples/fortran/CMakeLists.txt +++ b/examples/fortran/CMakeLists.txt @@ -25,3 +25,19 @@ target_link_libraries(plus-qft ) set_target_properties(plus-qft PROPERTIES Fortran_PREPROCESS ON) + +if (BUILD_WITH_MPI) + add_executable(mpi-plus-qft + mpi_plus_qft.f90 + utils.f90 + ) + + target_link_libraries(mpi-plus-qft + PRIVATE + CQ-SIMBE::cq-simbe + ${MATHS_LIBRARY} + ${MPI_Fortran_LIBRARIES} + ) + + set_target_properties(mpi-plus-qft PROPERTIES Fortran_PREPROCESS ON) +endif() diff --git a/examples/fortran/mpi_plus_qft.f90 b/examples/fortran/mpi_plus_qft.f90 new file mode 100644 index 0000000..761f2dc --- /dev/null +++ b/examples/fortran/mpi_plus_qft.f90 @@ -0,0 +1,129 @@ +program plus_qft +use mpi +use cq +#include "cqf.h" +use example_utils +implicit none + +integer :: ireturn, freturn, alloc_status, free_status, reg_status, qrun_status, ierr +integer(kind=8) :: NQUBITS, NSHOTS, NMEASURE +type(qubit) :: qrc +integer(kind=2), allocatable, target :: cr(:) + +integer :: id, nproc, split_cond, cq_comm + +call MPI_Init(ierr) +call MPI_Comm_size(MPI_COMM_WORLD, nproc, ierr) +call MPI_Comm_rank(MPI_COMM_WORLD, id, ierr) + +if (id >= 3) then + split_cond = 1 +else + split_cond = 0 +end if + +call MPI_Comm_split(MPI_COMM_WORLD, split_cond, id, cq_comm, ierr) + +NQUBITS = 10 +NSHOTS = 10 +NMEASURE = NQUBITS + +write(*,'(A)') 'before init' + +if (split_cond == 1) then +ireturn = cq_init_custom_mpi_comm(cq_comm, 1) + +write(*,'(A,I4)') 'cq_init returned: ',ireturn + +reg_status = cq_register_qkern(plus_state_qft) + +write(*,'(A,I4)') 'after register_qkern: ', reg_status + +CQ_PROG_BEGIN() + +alloc_status = cq_alloc_qureg(qrc, NQUBITS) + +write(*,'(A,I4)') 'alloc_qureg returned: ', alloc_status + +allocate(cr(NMEASURE * NSHOTS)) + +CALL cq_init_creg(NMEASURE * NSHOTS, -1, cr) + +write(*,'(A)') 'after init_creg' + +qrun_status = cq_sm_qrun(plus_state_qft, qrc, NQUBITS, cr, NMEASURE, NSHOTS) + +write(*,'(A,I4)') 'after sm_qrun: ', qrun_status + +CALL report_results(cr, NMEASURE, NSHOTS) + +write(*,'(A)') 'after report results' + +free_status = cq_free_qureg(qrc) + +write(*,'(A,I4)') 'free_status returned: ', free_status + +CQ_PROG_END() + +freturn = cq_finalise(0) + +write(*,'(A,I4)') 'cq_finalise returned: ',freturn + +write(*,'(A)') 'after finalise' + +end if + +if (split_cond == 0) then + write(*,*) 'I am user-owned rank doing something unrelated to CQ' +end if + +call MPI_Barrier(MPI_COMM_WORLD, ierr) +call MPI_Finalize(ierr) + +contains + + subroutine qft(NQUBITS, qr) + implicit none + integer(kind=8), value :: NQUBITS + type(qubit), value :: qr + integer(kind=8) :: i, j + integer :: status + real(8), parameter :: PI = 3.1415926535897932384626433832795028841971694 + real(8) :: angle + + do i = 0, NQUBITS - 1 + status = cq_hadamard(qr, i) + do j = i + 1, NQUBITS - 1 + angle = PI / (2.0 ** j) + status = cq_cphase(qr, j, i, angle) + end do + end do + + do i = 0, (NQUBITS / 2) - 1 + j = NQUBITS - (i + 1) + status = cq_swap(qr, i, j) + end do + end subroutine qft + + function plus_state_qft(NQUBITS, qr, NMEASURE, cr, reg) bind(C) result(status) + implicit none + integer(kind=8), value :: NQUBITS + type(qubit), value :: qr + integer(kind=8), value :: NMEASURE + integer(kind=2), intent(inout) :: cr(0:NMEASURE) + type(qkern_map), value :: reg + integer(kind=8) :: i + integer :: status + integer(kind=8) :: STATE_IDX = 0 + CQ_REGISTER_KERNEL("plus_state_qft", reg) + status = cq_set_qureg(qr, STATE_IDX, NQUBITS) + + do i = 0, NQUBITS-1 + status = cq_hadamard(qr, i) + end do + + call qft(NQUBITS, qr) + status = cq_measure_qureg(qr, NQUBITS, cr) + end function plus_state_qft + +end program From 763a8f66d99bf7bb2b1f51c5058a25c49bf24a75 Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 21 Jul 2026 11:49:13 +0100 Subject: [PATCH 37/38] Updated include to be compatible with upcoming QuEST v4.3. --- src/host-device/comms/comms_mpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/host-device/comms/comms_mpi.c b/src/host-device/comms/comms_mpi.c index 4e66eb5..68e1d3f 100644 --- a/src/host-device/comms/comms_mpi.c +++ b/src/host-device/comms/comms_mpi.c @@ -11,7 +11,7 @@ #endif #if CQ_CONF_QUEST_WITH_MPI -#include "quest/include/subcommunicator.h" +#include "quest/include/experimental.h" #endif #include From 121d77f7747711560dbf190c635318cd3dbd1cbd Mon Sep 17 00:00:00 2001 From: Mateusz Meller Date: Tue, 21 Jul 2026 12:17:54 +0100 Subject: [PATCH 38/38] Updated Fortran targets properties to ensure builds with Ninja generator succeed. --- examples/fortran/CMakeLists.txt | 42 +++++++++++++++++++++------------ tests/fortran/CMakeLists.txt | 12 ++++++++++ 2 files changed, 39 insertions(+), 15 deletions(-) diff --git a/examples/fortran/CMakeLists.txt b/examples/fortran/CMakeLists.txt index 472c07e..5feaafe 100644 --- a/examples/fortran/CMakeLists.txt +++ b/examples/fortran/CMakeLists.txt @@ -11,7 +11,11 @@ target_link_libraries(zero-qft ${MATHS_LIBRARY} ) -set_target_properties(zero-qft PROPERTIES Fortran_PREPROCESS ON) +set_target_properties(zero-qft + PROPERTIES + Fortran_PREPROCESS ON + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mod/zero-qft" +) add_executable(plus-qft plus_qft.f90 @@ -24,20 +28,28 @@ target_link_libraries(plus-qft ${MATHS_LIBRARY} ) -set_target_properties(plus-qft PROPERTIES Fortran_PREPROCESS ON) +set_target_properties(plus-qft + PROPERTIES + Fortran_PREPROCESS ON + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mod/plus-qft" +) if (BUILD_WITH_MPI) - add_executable(mpi-plus-qft - mpi_plus_qft.f90 - utils.f90 - ) - - target_link_libraries(mpi-plus-qft - PRIVATE - CQ-SIMBE::cq-simbe - ${MATHS_LIBRARY} - ${MPI_Fortran_LIBRARIES} - ) - - set_target_properties(mpi-plus-qft PROPERTIES Fortran_PREPROCESS ON) + add_executable(mpi-plus-qft + mpi_plus_qft.f90 + utils.f90 + ) + + target_link_libraries(mpi-plus-qft + PRIVATE + CQ-SIMBE::cq-simbe + ${MATHS_LIBRARY} + ${MPI_Fortran_LIBRARIES} + ) + + set_target_properties(mpi-plus-qft + PROPERTIES + Fortran_PREPROCESS ON + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mod/mpi-plus-qft" + ) endif() diff --git a/tests/fortran/CMakeLists.txt b/tests/fortran/CMakeLists.txt index 709e9a7..8089e25 100644 --- a/tests/fortran/CMakeLists.txt +++ b/tests/fortran/CMakeLists.txt @@ -8,6 +8,10 @@ add_executable(test_fort_host_ops utils.f90 ) +set_target_properties(test_fort_host_ops PROPERTIES + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mod/test_fort_host_ops" +) + target_link_libraries(test_fort_host_ops PRIVATE cq-simbe @@ -23,6 +27,10 @@ add_executable(test_fort_device_ops utils.f90 ) +set_target_properties(test_fort_device_ops PROPERTIES + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mod/test_fort_device_ops" +) + target_link_libraries(test_fort_device_ops PRIVATE cq-simbe @@ -38,6 +46,10 @@ add_executable(test_fort_qasm_gates utils.f90 ) +set_target_properties(test_fort_qasm_gates PROPERTIES + Fortran_MODULE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/mod/test_fort_qasm_gates" +) + target_link_libraries(test_fort_qasm_gates PRIVATE cq-simbe