Files
openairinterface5g/openair1/PHY/TOOLS/tests/benchmark_channel_pipeline.cpp
Bartosz Podrygajlo 47ba5d3ca5 Acceleration of channel convolution and noise generation
Added two versions of channel convolution and noise generation:
 - accelerated via threadpool
 - accelerated using CUDA

The main difference between this and previous versions of channel
convolution implementations is that these functions take a real-world
approach to input/output where both could be split unevenly over a set of
buffers, e.g. ring buffers used in vrtsim.

The CUDA-accelerated version only works on systems with unified memory, e.g. NVidia
DGX Spark or GH
2026-03-30 09:48:50 +02:00

240 lines
6.5 KiB
C++

/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <time.h>
#include <getopt.h>
#include "oai_cuda.h"
#include "common/config/config_userapi.h"
#include <memory>
#include "benchmark/benchmark.h"
#include "test_channel_pipeline_tools.h"
#include "channel_pipeline.h"
extern "C" {
#include "openair1/SIMULATION/TOOLS/sim.h"
}
configmodule_interface_t *uniqCfg = NULL;
extern "C" void exit_function(const char *file, const char *function, const int line, const char *s, const int assert)
{
fprintf(stderr, "FATAL: %s at %s:%s:%d\n", s, file, function, line);
exit(EXIT_FAILURE);
}
#ifdef CUDA_ENABLE
static void BM_channel_convolution_gpu(benchmark::State &state)
{
int nb_rx = state.range(0);
int nb_tx = state.range(1);
int num_samples = state.range(2);
int channel_length = 16;
size_t num_input_samples = num_samples + channel_length - 1;
std::vector<c16_t *> input(nb_tx);
for (int i = 0; i < nb_tx; ++i) {
input[i] = new c16_t[num_input_samples];
}
std::vector<c16_t *> output(nb_rx);
for (int i = 0; i < nb_rx; ++i) {
output[i] = new c16_t[num_samples];
}
std::vector<cf_t *> channel(nb_rx * nb_tx);
for (int i = 0; i < nb_rx * nb_tx; ++i) {
channel[i] = new cf_t[channel_length];
}
for (int i = 0; i < nb_rx * nb_tx; ++i) {
generate_random_signal_float(channel[i], channel_length);
}
void *gpu_context = cuda_channel_pipeline_init(61440 * 4);
for (int aatx = 0; aatx < nb_tx; aatx++) {
generate_random_signal(input[aatx], num_input_samples);
}
size_t total_samples = 0;
for (auto _ : state) {
cuda_channel_pipeline(gpu_context,
(const cf_t **)channel.data(),
(const c16_t **)input.data(),
nullptr,
num_input_samples,
output.data(),
nullptr,
num_samples,
num_samples,
channel_length,
nb_tx,
nb_rx,
0.0f);
total_samples += num_samples;
}
state.counters["MSPS"] = benchmark::Counter(total_samples / 1000000.f, benchmark::Counter::kIsRate);
cuda_channel_pipeline_shutdown(gpu_context);
}
#endif
static void BM_channel_convolution_cpu(benchmark::State &state)
{
int nb_rx = state.range(0);
int nb_tx = state.range(1);
int num_samples = state.range(2);
int channel_length = 16;
size_t num_input_samples = num_samples + channel_length - 1;
std::vector<c16_t *> input(nb_tx);
for (int i = 0; i < nb_tx; ++i) {
input[i] = new c16_t[num_input_samples];
}
std::vector<c16_t *> output(nb_rx);
for (int i = 0; i < nb_rx; ++i) {
output[i] = new c16_t[num_samples];
}
std::vector<cf_t *> channel(nb_rx * nb_tx);
for (int i = 0; i < nb_rx * nb_tx; ++i) {
channel[i] = new cf_t[channel_length];
}
for (int i = 0; i < nb_rx * nb_tx; ++i) {
generate_random_signal_float(channel[i], channel_length);
}
for (int aatx = 0; aatx < nb_tx; aatx++) {
generate_random_signal(input[aatx], num_input_samples);
}
size_t total_samples = 0;
for (auto _ : state) {
channel_convolution_cpu((const cf_t **)channel.data(),
(const c16_t **)input.data(),
nullptr,
num_input_samples,
output.data(),
nullptr,
num_samples,
num_samples,
channel_length,
nb_tx,
nb_rx);
total_samples += num_samples;
}
state.counters["MSPS"] = benchmark::Counter(total_samples / 1000000.0f, benchmark::Counter::kIsRate);
for (int i = 0; i < nb_tx; ++i)
delete[] input[i];
for (int i = 0; i < nb_rx * nb_tx; ++i)
delete[] channel[i];
for (int i = 0; i < nb_rx; ++i) {
delete[] output[i];
}
}
static void BM_channel_convolution_tpool(benchmark::State &state)
{
int nb_rx = state.range(0);
int nb_tx = state.range(1);
int num_samples = state.range(2);
int channel_length = 16;
size_t num_input_samples = num_samples + channel_length - 1;
std::vector<c16_t *> input(nb_tx);
for (int i = 0; i < nb_tx; ++i) {
input[i] = new c16_t[num_input_samples];
}
std::vector<c16_t *> output(nb_rx);
for (int i = 0; i < nb_rx; ++i) {
output[i] = new c16_t[num_samples];
}
std::vector<cf_t *> channel(nb_rx * nb_tx);
for (int i = 0; i < nb_rx * nb_tx; ++i) {
channel[i] = new cf_t[channel_length];
}
for (int i = 0; i < nb_rx * nb_tx; ++i) {
generate_random_signal_float(channel[i], channel_length);
}
for (int aatx = 0; aatx < nb_tx; aatx++) {
generate_random_signal(input[aatx], num_input_samples);
}
void *tpool = init_tpool(16);
channel_pipeline_init(0.0f);
size_t total_samples = 0;
for (auto _ : state) {
channel_pipeline(tpool,
(const cf_t **)channel.data(),
(const c16_t **)input.data(),
nullptr,
num_input_samples,
output.data(),
nullptr,
num_samples,
num_samples,
channel_length,
nb_tx,
nb_rx,
0.0f);
total_samples += num_samples;
}
state.counters["MSPS"] = benchmark::Counter(total_samples / 1000000.f, benchmark::Counter::kIsRate);
channel_pipeline_shutdown();
destroy_tpool(tpool);
for (int i = 0; i < nb_tx; ++i)
delete[] input[i];
for (int i = 0; i < nb_rx * nb_tx; ++i)
delete[] channel[i];
for (int i = 0; i < nb_rx; ++i) {
delete[] output[i];
}
}
#ifdef CUDA_ENABLE
BENCHMARK(BM_channel_convolution_gpu)
->ArgsProduct({
{1, 2, 4, 16, 64}, // nb_rx
{1, 2, 4, 16, 64}, // nb_tx
{61440}, // num_samples
})
->Iterations(100);
#endif
BENCHMARK(BM_channel_convolution_cpu)
->ArgsProduct({
{1, 2, 4}, // nb_rx
{1, 2, 4}, // nb_tx
{61440}, // num_samples
})
->Iterations(50);
BENCHMARK(BM_channel_convolution_tpool)
->ArgsProduct({
{1, 2, 4, 8, 16}, // nb_rx
{1, 2, 4, 8, 16}, // nb_tx
{61440}, // num_samples
})
->Iterations(50);
int main(int argc, char **argv)
{
logInit();
randominit();
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
return 0;
}