#pragma once
#include "utils.h"
#include <assert.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define ULL unsigned long long int
class ChannelDev {
private:
int id;
volatile int *doorbell;
uint8_t *buff;
uint8_t *buff_end;
uint8_t *volatile buff_write_head_ptr;
uint8_t *volatile buff_write_tail_ptr;
public:
ChannelDev() {}
int get_id() { return this->id; }
__device__ __forceinline__ int dev_get_id() { return this->id; }
__device__ __forceinline__ void push(void *packet, uint32_t nbytes) {
assert(nbytes != 0);
uint8_t *curr_ptr = NULL;
while (curr_ptr == NULL) {
curr_ptr = (uint8_t *)atomicAdd((ULL *)&buff_write_head_ptr, (ULL)nbytes);
if (curr_ptr + nbytes > buff_end) {
if (curr_ptr <= buff_end) {
while (buff_write_tail_ptr != curr_ptr) {
}
flush();
} else {
while (buff_write_head_ptr > buff_end) {
}
}
curr_ptr = NULL;
}
}
memcpy(curr_ptr, packet, nbytes);
atomicAdd((ULL *)&buff_write_tail_ptr, (ULL)nbytes);
}
__device__ __forceinline__ void flush() {
uint32_t nbytes = (uint32_t)(buff_write_tail_ptr - buff);
if (nbytes == 0) {
return;
}
__threadfence_system();
assert(*doorbell == 0);
*doorbell = nbytes;
while (*doorbell != 0)
;
buff_write_tail_ptr = buff;
__threadfence();
buff_write_head_ptr = buff;
}
private:
void init(int id, int *h_doorbell, int buff_size) {
CUDA_SAFECALL(
cudaHostGetDevicePointer((void **)&doorbell, (void *)h_doorbell, 0));
#ifdef USE_ASYNC_STREAM
CUDA_SAFECALL(cudaMalloc((void **)&buff, buff_size));
#else
CUDA_SAFECALL(cudaMallocManaged((void **)&buff, buff_size));
#endif
buff_write_head_ptr = buff;
buff_write_tail_ptr = buff;
buff_end = buff + buff_size;
this->id = id;
}
friend class ChannelHost;
};
class ChannelHost {
private:
volatile int *doorbell;
cudaStream_t stream;
ChannelDev *ch_dev;
uint8_t *dev_buff_read_head;
uint8_t *dev_buff;
pthread_t thread;
volatile bool thread_started;
public:
int id;
int buff_size;
public:
ChannelHost() {}
void init(int id, int buff_size, ChannelDev *ch_dev,
void *(*thread_fun)(void *), void *args = NULL) {
this->buff_size = buff_size;
this->id = id;
cudaDeviceProp prop;
int device = 0;
CUDA_SAFECALL(cudaGetDeviceProperties(&prop, device));
if (prop.canMapHostMemory == 0) {
CUDA_SAFECALL(cudaSetDeviceFlags(cudaDeviceMapHost));
}
#ifdef USE_ASYNC_STREAM
int priority_high, priority_low;
CUDA_SAFECALL(
cudaDeviceGetStreamPriorityRange(&priority_low, &priority_high));
CUDA_SAFECALL(cudaStreamCreateWithPriority(&stream, cudaStreamNonBlocking,
priority_high));
#endif
CUDA_SAFECALL(
cudaHostAlloc((void **)&doorbell, sizeof(int), cudaHostAllocMapped));
*doorbell = 0;
this->ch_dev = ch_dev;
ch_dev->init(id, (int *)doorbell, buff_size);
dev_buff = ch_dev->buff;
dev_buff_read_head = dev_buff;
if (thread_fun != NULL) {
thread_started = true;
pthread_create(&thread, NULL, (void *(*)(void *))thread_fun, args);
} else {
thread_started = false;
}
}
void destroy(bool dealloc) {
if (thread_started) {
thread_started = false;
pthread_join(thread, NULL);
}
if (dealloc) {
#ifdef USE_ASYNC_STREAM
CUDA_SAFECALL(cudaStreamDestroy(stream));
#endif
CUDA_SAFECALL(cudaFree((int *)doorbell));
CUDA_SAFECALL(cudaFree(ch_dev->buff));
}
}
bool is_active() { return thread_started; }
uint32_t recv(void *buff, uint32_t max_buff_size) {
assert(max_buff_size > 0);
assert(doorbell != NULL);
uint32_t buff_nbytes = *doorbell;
if (buff_nbytes == 0) {
return 0;
}
int nbytes = buff_nbytes;
if (buff_nbytes > max_buff_size) {
nbytes = max_buff_size;
}
assert(nbytes <= this->buff_size);
#ifdef USE_ASYNC_STREAM
CUDA_SAFECALL(cudaMemcpyAsync(buff, dev_buff_read_head, nbytes,
cudaMemcpyDeviceToHost, stream));
CUDA_SAFECALL(cudaStreamSynchronize(stream));
#else
memcpy(buff, dev_buff_read_head, nbytes);
#endif
int bytes_left = buff_nbytes - nbytes;
assert(bytes_left >= 0);
if (bytes_left > 0) {
dev_buff_read_head += nbytes;
} else {
dev_buff_read_head = dev_buff;
}
*doorbell = bytes_left;
return nbytes;
}
pthread_t get_thread() { return thread; }
friend class MultiChannelHost;
};