Rfsim: Optimization for simple configurations

- Added first beam optimization to combine_tx_buffers.
 - Added first beam & first peer optimization to rfsimulator_read_internal

This results in significant speed up for the first rx beam, first tx
beam and first peer which is the base case with beam simulation disabled.
This commit is contained in:
Bartosz Podrygajlo
2025-12-23 10:05:14 +01:00
parent c587aab6b6
commit 033aed736e
3 changed files with 76 additions and 37 deletions
+1 -1
View File
@@ -235,7 +235,7 @@ void update_channel_model(channel_desc_t *channelDesc, uint64_t TS)
either we regenerate the channel (call again random_channel(desc,0)), or we keep it over subframes
legacy: we regenerate each sub frame in UL, and each frame only in DL
*/
void rxAddInput(const c16_t **input_sig,
void rxAddInput(c16_t **input_sig,
cf_t *after_channel_sig,
int rxAnt,
channel_desc_t *channelDesc,
+1 -1
View File
@@ -24,7 +24,7 @@
#ifndef __RFSIMULATOR_H
#define __RFSIMULATOR_H
#include <stdbool.h>
void rxAddInput(const c16_t **input_sig,
void rxAddInput(c16_t **input_sig,
cf_t *after_channel_sig,
int rxAnt,
channel_desc_t *channelDesc,
+74 -35
View File
@@ -1045,17 +1045,18 @@ static std::vector<int> get_beam_ids(uint64_t beam_map)
* @param num_aatx The number of antennas to process.
* @param num_samples The number of samples to process.
* @param rx_beam_id Receiver configured beam id for the timestamp
* @param samples Pointer to the output buffer where combined samples will be stored.
* @return A vector of vectors containing the combined samples for each antenna.
*/
static std::vector<std::vector<c16_t>> combine_received_beams(rfsimulator_state_t *t,
std::queue<rfsim_packet_t *> &received_packets,
uint64_t start_timestamp,
int num_aatx,
size_t num_samples,
int rx_beam_id)
static void combine_received_beams(rfsimulator_state_t *t,
std::queue<rfsim_packet_t *> &received_packets,
uint64_t start_timestamp,
int num_aatx,
size_t num_samples,
int rx_beam_id,
c16_t **samples)
{
// Assume received_packets is ordered by timestamp
std::vector<std::vector<c16_t>> ant_buffers(num_aatx, std::vector<c16_t>(num_samples, {0, 0}));
std::queue<rfsim_packet_t *> packets_copy = received_packets;
while (!packets_copy.empty()) {
rfsim_packet_t *pkt = packets_copy.front();
@@ -1081,16 +1082,38 @@ static std::vector<std::vector<c16_t>> combine_received_beams(rfsimulator_state_
for (int aatx = 0; aatx < num_aatx; aatx++) {
c16_t *buffer = (c16_t *)pkt->payload;
c16_t *tx_ant_buffer_in = &buffer[(num_aatx * beam + aatx) * pkt->header.size + read_start_idx];
for (int s = write_start_idx; s < write_end_idx; s++) {
ant_buffers[aatx][s].r += tx_ant_buffer_in->r * gain_linear;
ant_buffers[aatx][s].i += tx_ant_buffer_in->i * gain_linear;
tx_ant_buffer_in++;
if (beam == 0) {
// For the first beam, we can directly copy the samples
if (gain_dB == 0.0f) {
// If gain is 0 dB, we can use memcpy for efficiency
memcpy(&samples[aatx][write_start_idx], tx_ant_buffer_in, (write_end_idx - write_start_idx) * sizeof(c16_t));
} else {
for (int s = write_start_idx; s < write_end_idx; s++) {
samples[aatx][s].r = tx_ant_buffer_in->r * gain_linear;
samples[aatx][s].i = tx_ant_buffer_in->i * gain_linear;
tx_ant_buffer_in++;
}
}
} else {
// For subsequent beams, we need to ensure we accumulate the samples
if (gain_dB == 0.0f) {
for (int s = write_start_idx; s < write_end_idx; s++) {
samples[aatx][s].r += tx_ant_buffer_in->r;
samples[aatx][s].i += tx_ant_buffer_in->i;
tx_ant_buffer_in++;
}
} else {
for (int s = write_start_idx; s < write_end_idx; s++) {
samples[aatx][s].r += tx_ant_buffer_in->r * gain_linear;
samples[aatx][s].i += tx_ant_buffer_in->i * gain_linear;
tx_ant_buffer_in++;
}
}
}
}
}
packets_copy.pop();
}
return ant_buffers;
}
static bool flushInput(rfsimulator_state_t *t, int timeout, bool first_time)
@@ -1164,11 +1187,13 @@ static void rfsimulator_read_internal(rfsimulator_state_t *t,
openair0_timestamp timestamp,
int nsamps,
int nbAnt,
int rx_beam_id)
int rx_beam_id,
bool is_first_beam)
{
cf_t temp_array[nbAnt][nsamps];
bool channel_modelling = false;
// Add all input nodes signal in the output buffer
bool is_first_peer = true;
for (int sock = 0; sock < MAX_FD_RFSIMU; sock++) {
buffer_t *ptr = &t->buf[sock];
@@ -1188,38 +1213,52 @@ static void rfsimulator_read_internal(rfsimulator_state_t *t,
}
const uint64_t channel_offset = ptr->channel_model->channel_offset;
const uint64_t channel_length = ptr->channel_model->channel_length;
std::vector<std::vector<c16_t>> ant_buffers = combine_received_beams(t,
ptr->received_packets,
timestamp - channel_offset - (channel_length - 1),
ptr->nbAnt,
nsamps + channel_length - 1,
rx_beam_id);
const c16_t *input[ant_buffers.size()];
std::vector<std::vector<c16_t>> ant_buffers(ptr->nbAnt, std::vector<c16_t>(nsamps + channel_length - 1, {0, 0}));
c16_t *input[ant_buffers.size()];
for (uint aatx = 0; aatx < ant_buffers.size(); aatx++) {
input[aatx] = ant_buffers[aatx].data();
}
combine_received_beams(t,
ptr->received_packets,
timestamp - channel_offset - (channel_length - 1),
ptr->nbAnt,
nsamps + channel_length - 1,
rx_beam_id,
input);
for (int aarx = 0; aarx < nbAnt; aarx++) {
rxAddInput(input, temp_array[aarx], aarx, ptr->channel_model, nsamps, timestamp);
}
} else {
std::vector<std::vector<c16_t>> ant_buffers =
combine_received_beams(t, ptr->received_packets, timestamp, ptr->nbAnt, nsamps, rx_beam_id);
for (int aarx = 0; aarx < nbAnt; aarx++) {
double H_awgn_mimo_coeff[ant_buffers.size()];
for (int aatx = 0; aatx < (int)ant_buffers.size(); aatx++) {
uint32_t ant_diff = std::abs(aatx - aarx);
H_awgn_mimo_coeff[aatx] = ant_diff ? (0.2 / ant_diff) : 1.0;
if (is_first_beam && is_first_peer && (ptr->nbAnt == 1 || nbAnt == 1)) {
// optimization: The buffer is uninitialized so samples can be written directly in the buffer
combine_received_beams(t, ptr->received_packets, timestamp, 1, nsamps, rx_beam_id, samples);
} else {
std::vector<std::vector<c16_t>> ant_buffers(ptr->nbAnt, std::vector<c16_t>(nsamps, {0, 0}));
c16_t *input[ant_buffers.size()];
for (uint aatx = 0; aatx < ant_buffers.size(); aatx++) {
input[aatx] = ant_buffers[aatx].data();
}
combine_received_beams(t, ptr->received_packets, timestamp, ptr->nbAnt, nsamps, rx_beam_id, input);
for (int aarx = 0; aarx < nbAnt; aarx++) {
double H_awgn_mimo_coeff[ant_buffers.size()];
for (int aatx = 0; aatx < (int)ant_buffers.size(); aatx++) {
uint32_t ant_diff = std::abs(aatx - aarx);
H_awgn_mimo_coeff[aatx] = ant_diff ? (0.2 / ant_diff) : 1.0;
}
for (uint aatx = 0; aatx < ant_buffers.size(); aatx++) { // sum up signals from nbAnt_tx antennas
for (int i = 0; i < nsamps; i++) { // loop over nsamps
samples[aarx][i].r += ant_buffers[aatx][i].r * H_awgn_mimo_coeff[aatx];
samples[aarx][i].i += ant_buffers[aatx][i].i * H_awgn_mimo_coeff[aatx];
} // end for a_tx
} // end for i (number of samps)
} // end for a_rx
for (uint aatx = 0; aatx < ant_buffers.size(); aatx++) {
for (int i = 0; i < nsamps; i++) {
samples[aarx][i].r += ant_buffers[aatx][i].r * H_awgn_mimo_coeff[aatx];
samples[aarx][i].i += ant_buffers[aatx][i].i * H_awgn_mimo_coeff[aatx];
}
}
}
}
}
}
is_first_peer = false;
}
bool apply_global_noise = get_noise_power_dBFS() != INVALID_DBFS_VALUE;
@@ -1347,7 +1386,7 @@ static int rfsimulator_read_beams(openair0_device *device,
for (int i = 0; i < nbAnt; i++) {
samples_beam[i] = (c16_t *)samplesVoid[beam][i] + timestamp - t->nextRxTstamp;
}
rfsimulator_read_internal(t, samples_beam, timestamp, nsamps_beam_map, nbAnt, rx_beam_ids[beam]);
rfsimulator_read_internal(t, samples_beam, timestamp, nsamps_beam_map, nbAnt, rx_beam_ids[beam], beam == 0);
}
timestamp += nsamps_beam_map;
nsamps_to_process -= nsamps_beam_map;