Commit 5f5d8596 authored by Robert Schmidt's avatar Robert Schmidt

Merge remote-tracking branch 'origin/rlc-benchmark' into integration_2024_w42 (!3046)

Introduce two tests for NR RLC AM entity:

- test_nr_rlc_am_entity
- benchmark_nr_rlc_am_entity

The benchmark can be used to verify processing time improvements for NR RLC AM
entity. The benchmark creates two entities and creates an unbalanced
communication channel between them with 10% loss packet loss and 100:1 PDU sent
ratio. This allows the lists in RLC to build up and triggers RLC AM code
related to looking through the segment/pdu lists.

Example benchmark output:

    ubuntu@zeus:~/bpodrygajlo/openairinterface5g/cmake_targets/build$ ./openair2/LAYER2/nr_rlc/tests/benchmark_nr_rlc_am_entity
    [LOG] init aborted, configuration couldn't be performed
    log init done
    CMDLINE: "./openair2/LAYER2/nr_rlc/tests/benchmark_nr_rlc_am_entity"
    [CONFIG] debug flags: 0x00400000
    2024-10-16T14:58:20+02:00
    Running ./openair2/LAYER2/nr_rlc/tests/benchmark_nr_rlc_am_entity
    Run on (8 X 4700 MHz CPU s)
    CPU Caches:
      L1 Data 48 KiB (x4)
      L1 Instruction 32 KiB (x4)
      L2 Unified 1280 KiB (x4)
      L3 Unified 12288 KiB (x1)
    Load Average: 0.48, 0.27, 0.20
    ***WARNING*** CPU scaling is enabled, the benchmark real time measurements may be noisy and will incur extra overhead.
    --------------------------------------------------------------------
    Benchmark                          Time             CPU   Iterations
    --------------------------------------------------------------------
    BM_nr_rlc_am_entity/100        50096 ns        50097 ns        13101
    BM_nr_rlc_am_entity/256       140386 ns       140387 ns         4868
    BM_nr_rlc_am_entity/1024      536323 ns       536311 ns         1309
    BM_nr_rlc_am_entity/4096     2084464 ns      2084385 ns          338
    BM_nr_rlc_am_entity/16384    8309318 ns      8309458 ns           81
    BM_nr_rlc_am_entity/20000    9843792 ns      9843774 ns           68
parents 08972f35 698b5e3d
......@@ -767,8 +767,10 @@ void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *_entity,
nr_rlc_pdu_decoder_init(&decoder, buffer, size);
dc = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
if (dc == 0)
if (dc == 0) {
LOG_D(RLC, "RLC received control PDU\n");
return process_control_pdu(entity, buffer, size);
}
/* data PDU */
p = nr_rlc_pdu_decoder_get_bits(&decoder, 1); R(decoder);
......@@ -839,6 +841,7 @@ void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *_entity,
entity->rx_list, pdu);
/* do reception actions (38.322 5.2.3.2.3) */
LOG_D(RLC, "RLC received PDU sn %d so %d is_first %d is_last %d data_size = %d \n", sn, so, is_first, is_last, data_size);
reception_actions(entity, pdu);
if (p) {
......@@ -852,15 +855,14 @@ void nr_rlc_entity_am_recv_pdu(nr_rlc_entity_t *_entity,
entity->status_triggered = 1;
if (!(sn_compare_rx(entity, sn, entity->rx_highest_status) < 0 ||
sn_compare_rx(entity, sn, v) >= 0)) {
LOG_D(RLC, "%s:%d:%s: warning: STATUS trigger should be delayed, according to specs\n",
__FILE__, __LINE__, __FUNCTION__);
LOG_D(RLC, "warning: STATUS triggerered but should be delayed according to specs\n");
}
}
return;
err:
LOG_W(RLC, "%s:%d:%s: error decoding PDU, discarding\n", __FILE__, __LINE__, __FUNCTION__);
LOG_W(RLC, "RX error decoding PDU, discarding\n");
goto discard;
discard:
......@@ -978,10 +980,7 @@ static nr_rlc_sdu_segment_t *resegment(nr_rlc_sdu_segment_t *sdu,
pdu_header_size = compute_pdu_header_size(entity, sdu);
next = calloc(1, sizeof(nr_rlc_sdu_segment_t));
if (next == NULL) {
LOG_E(RLC, "%s:%d:%s: out of memory\n", __FILE__, __LINE__, __FUNCTION__);
exit(1);
}
AssertFatal(next != NULL, "out of memory\n");
*next = *sdu;
over_size = pdu_header_size + sdu->size - pdu_size;
......@@ -994,7 +993,6 @@ static nr_rlc_sdu_segment_t *resegment(nr_rlc_sdu_segment_t *sdu,
next->size = over_size;
next->so = sdu->so + sdu->size;
next->is_first = 0;
return next;
}
......@@ -1594,6 +1592,8 @@ static int generate_retx_pdu(nr_rlc_entity_am_t *entity, char *buffer,
entity->force_poll = 0;
}
LOG_D(RLC, "RLC TX: sending sdu sn = %d is_first = %d, is_last = %d size = %d\n", sdu->sdu->sn, sdu->is_first, sdu->is_last, size);
int ret_size = serialize_sdu(entity, sdu, buffer, size, p);
entity->common.stats.txpdu_pkts++;
entity->common.stats.txpdu_bytes += ret_size;
......@@ -1612,11 +1612,14 @@ static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size)
int p;
/* sn out of window (that is: we have window stalling)? do nothing */
if (is_window_stalling(entity))
if (is_window_stalling(entity)) {
LOG_D(RLC, "Abort transmit due to window stall\n");
return 0;
}
if (entity->tx_list == NULL)
if (entity->tx_list == NULL) {
return 0;
}
sdu = entity->tx_list;
......@@ -1645,6 +1648,7 @@ static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size)
/* segment if necessary */
if (pdu_size > size) {
LOG_D(RLC, "Segmentation (header %d + data %d) / (%d)\n", pdu_header_size, size - pdu_header_size, pdu_size - pdu_header_size);
nr_rlc_sdu_segment_t *next_sdu;
next_sdu = resegment(sdu, entity, size);
/* put the second SDU back at the head of the TX list */
......@@ -1693,7 +1697,7 @@ static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size)
entity->common.stats.txpdu_pkts++;
entity->common.stats.txpdu_bytes += ret_size;
/* No need to 'zero' time-of-arrival;
/* No need to 'zero' time-of-arrival;
Segmented packets do need to be duplicated in time-sensitive use cases */
if (entity->common.avg_time_is_on) {
uint64_t time_now = time_average_now();
......@@ -1701,6 +1705,14 @@ static int generate_tx_pdu(nr_rlc_entity_am_t *entity, char *buffer, int size)
time_average_add(entity->common.txsdu_avg_time_to_tx, time_now, waited_time);
}
LOG_D(RLC,
"RLC TX: sending sdu sn = %d is_first = %d, is_last = %d header size = %d data size = %d\n",
sdu->sdu->sn,
sdu->is_first,
sdu->is_last,
pdu_header_size,
size - pdu_header_size);
return ret_size;
// return serialize_sdu(entity, sdu, buffer, size, p);
}
......@@ -1730,14 +1742,18 @@ int nr_rlc_entity_am_generate_pdu(nr_rlc_entity_t *_entity,
if (status_to_report(entity)) {
ret = generate_status(entity, buffer, size);
if (ret != 0)
if (ret != 0) {
LOG_D(RLC, "RLC transmit status pdu PDU\n");
return ret;
}
}
if (entity->retransmit_list != NULL) {
ret = generate_retx_pdu(entity, buffer, size);
if (ret != 0)
if (ret != 0) {
LOG_D(RLC, "RLC retransmit PDU\n");
return ret;
}
}
return generate_tx_pdu(entity, buffer, size);
......@@ -1756,11 +1772,7 @@ void nr_rlc_entity_am_recv_sdu(nr_rlc_entity_t *_entity,
entity->common.stats.rxsdu_pkts++;
if (size > NR_SDU_MAX) {
LOG_E(RLC, "%s:%d:%s: fatal: SDU size too big (%d bytes)\n",
__FILE__, __LINE__, __FUNCTION__, size);
exit(1);
}
AssertFatal(size <= NR_SDU_MAX, "Fatal: SDU size too big (%d bytes)\n", size);
/* log SDUs rejected, at most once per second */
if (entity->sdu_rejected != 0
......
......@@ -12,3 +12,13 @@ add_test(NAME nr_rlc_tests
COMMAND exec_nr_rlc_test.sh ${CMAKE_CURRENT_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_executable(test_nr_rlc_am_entity test_nr_rlc_am_entity.cpp)
target_link_libraries(test_nr_rlc_am_entity PRIVATE nr_rlc_core GTest::gtest minimal_lib)
add_dependencies(tests test_nr_rlc_am_entity)
add_test(NAME test_nr_rlc_am_entity COMMAND ./test_nr_rlc_am_entity)
add_executable(benchmark_nr_rlc_am_entity benchmark_nr_rlc_am_entity.cpp)
target_link_libraries(benchmark_nr_rlc_am_entity PRIVATE benchmark::benchmark nr_rlc_core UTIL ${T_LIB} minimal_lib)
add_dependencies(tests benchmark_nr_rlc_am_entity)
add_test(NAME benchmark_nr_rlc_am_entity
COMMAND ./benchmark_nr_rlc_am_entity)
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "benchmark/benchmark.h"
extern "C" {
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity.h"
#include "common/utils/LOG/log.h"
#include "common/config/config_load_configmodule.h"
extern configmodule_interface_t *uniqCfg;
}
#include <iostream>
void deliver_sdu(void *deliver_sdu_data, struct nr_rlc_entity_t *entity, char *buf, int size)
{
}
void sdu_successful_delivery(void *sdu_successful_delivery_data, struct nr_rlc_entity_t *entity, int sdu_id)
{
}
void max_retx_reached(void *max_retx_reached_data, struct nr_rlc_entity_t *entity)
{
}
static void BM_nr_rlc_am_entity(benchmark::State &state)
{
nr_rlc_entity_t *tx_entity = new_nr_rlc_entity_am(10000,
10000,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
5,
35,
0,
4,
-1,
8,
12);
nr_rlc_entity_t *rx_entity = new_nr_rlc_entity_am(10000,
10000,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
5,
35,
0,
4,
-1,
8,
12);
char message[] = "Message to the other RLC AM entity.";
for (auto _ : state) {
char pdu_buf[40];
for (int i = 0; i < state.range(0); i++) {
tx_entity->recv_sdu(tx_entity, message, sizeof(message), i);
int size = tx_entity->generate_pdu(tx_entity, pdu_buf, sizeof(pdu_buf));
if (size != 0) {
// 10% packet loss
if (i % 10 != 0)
rx_entity->recv_pdu(rx_entity, pdu_buf, size);
}
// Allow the lists to build up before sending messages in the other direction
if (i % 100 == 0) {
size = rx_entity->generate_pdu(rx_entity, pdu_buf, sizeof(pdu_buf));
if (size != 0) {
// No packet lost in the other direction
tx_entity->recv_pdu(tx_entity, pdu_buf, size);
}
}
tx_entity->set_time(tx_entity, i + 1);
rx_entity->set_time(tx_entity, i + 1);
}
}
tx_entity->delete_entity(tx_entity);
rx_entity->delete_entity(rx_entity);
}
BENCHMARK(BM_nr_rlc_am_entity)->RangeMultiplier(4)->Range(100, 20000);
int main(int argc, char **argv)
{
logInit();
uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY);
g_log->log_component[RLC].level = -1;
benchmark::Initialize(&argc, argv);
benchmark::RunSpecifiedBenchmarks();
return 0;
}
/*
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The OpenAirInterface Software Alliance licenses this file to You under
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
* except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.openairinterface.org/?page_id=698
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*-------------------------------------------------------------------------------
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "gtest/gtest.h"
extern "C" {
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity_am.h"
#include "openair2/LAYER2/nr_rlc/nr_rlc_entity.h"
#include "common/utils/LOG/log.h"
#include "common/config/config_load_configmodule.h"
extern configmodule_interface_t *uniqCfg;
}
#include <iostream>
int sdu_delivered_count = 0;
int sdu_acked_count = 0;
void deliver_sdu(void *deliver_sdu_data, struct nr_rlc_entity_t *entity, char *buf, int size)
{
sdu_delivered_count++;
char payload[300];
ASSERT_LE(size, sizeof(payload));
snprintf(payload, size, "%s", buf);
std::cout << "Delivered sdu: " << payload << std::endl;
}
void sdu_successful_delivery(void *sdu_successful_delivery_data, struct nr_rlc_entity_t *entity, int sdu_id)
{
sdu_acked_count++;
std::cout << "SDU " << sdu_id << " acked" << std::endl;
}
void max_retx_reached(void *max_retx_reached_data, struct nr_rlc_entity_t *entity)
{
}
TEST(nr_rlc_am_entity, test_init)
{
nr_rlc_entity_t *entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
45,
35,
0,
-1,
-1,
8,
12);
char buf[30];
EXPECT_EQ(entity->generate_pdu(entity, buf, sizeof(buf)), 0) << "No PDCP SDU provided to RLC, expected no RLC PDUS";
entity->delete_entity(entity);
}
TEST(nr_rlc_am_entity, test_segmentation_reassembly)
{
nr_rlc_entity_t *tx_entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
45,
35,
0,
-1,
-1,
8,
12);
nr_rlc_entity_t *rx_entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
45,
35,
0,
-1,
-1,
8,
12);
char buf[30] = {0};
snprintf(buf, sizeof(buf), "%s", "Message");
EXPECT_EQ(tx_entity->generate_pdu(tx_entity, buf, sizeof(buf)), 0) << "No PDCP SDU provided to RLC, expected no RLC PDUS";
// Higher layer IF -> deliver PDCP SDU
tx_entity->recv_sdu(tx_entity, buf, sizeof(buf), 0);
sdu_delivered_count = 0;
for (auto i = 0; i < 10; i++) {
if (sdu_delivered_count == 1) {
break;
}
int size = tx_entity->generate_pdu(tx_entity, buf, 10);
EXPECT_GT(size, 0);
rx_entity->recv_pdu(rx_entity, buf, size);
}
EXPECT_EQ(sdu_delivered_count, 1);
sdu_acked_count = 0;
int size = rx_entity->generate_pdu(rx_entity, buf, 30);
tx_entity->recv_pdu(tx_entity, buf, size);
EXPECT_EQ(sdu_acked_count, 1);
tx_entity->delete_entity(tx_entity);
rx_entity->delete_entity(rx_entity);
}
TEST(nr_rlc_am_entity, test_ack_out_of_order)
{
nr_rlc_entity_t *tx_entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
5,
35,
0,
4,
-1,
8,
12);
nr_rlc_entity_t *rx_entity = new_nr_rlc_entity_am(100,
100,
deliver_sdu,
NULL,
sdu_successful_delivery,
NULL,
max_retx_reached,
NULL,
5,
35,
0,
4,
-1,
8,
12);
char buf[30] = {0};
EXPECT_EQ(tx_entity->generate_pdu(tx_entity, buf, sizeof(buf)), 0) << "No PDCP SDU provided to RLC, expected no RLC PDUS";
// Higher layer IF -> deliver PDCP SDU
// Generate 4 PDCP SDUS
snprintf(buf, sizeof(buf), "%s", "Message 1");
tx_entity->recv_sdu(tx_entity, buf, sizeof(buf), 0);
snprintf(buf, sizeof(buf), "%s", "Message 2");
tx_entity->recv_sdu(tx_entity, buf, sizeof(buf), 1);
snprintf(buf, sizeof(buf), "%s", "Message 3");
tx_entity->recv_sdu(tx_entity, buf, sizeof(buf), 2);
sdu_delivered_count = 0;
sdu_acked_count = 0;
char pdu_buf[40];
uint64_t time = 0;
for (auto i = 0; i < 3; i++) {
int size = tx_entity->generate_pdu(tx_entity, pdu_buf, sizeof(pdu_buf));
EXPECT_GT(size, 0);
// Do not deliver PDU 2 to RX entity
if (i != 1) {
rx_entity->recv_pdu(rx_entity, pdu_buf, size);
}
tx_entity->set_time(tx_entity, time);
rx_entity->set_time(tx_entity, time);
time++;
}
EXPECT_EQ(sdu_delivered_count, 2) << "Expected 2 out of 3 SDUs delivered";
int size = rx_entity->generate_pdu(rx_entity, pdu_buf, sizeof(pdu_buf));
EXPECT_GT(size, 0);
tx_entity->recv_pdu(tx_entity, pdu_buf, size);
// Triggers t-poll-retransmit and retransmission of the lost PDU
for (auto i = 0; i < 5; i++) {
tx_entity->set_time(tx_entity, time);
rx_entity->set_time(tx_entity, time);
size = tx_entity->generate_pdu(tx_entity, pdu_buf, sizeof(pdu_buf));
if (size != 0)
rx_entity->recv_pdu(rx_entity, pdu_buf, size);
size = rx_entity->generate_pdu(rx_entity, pdu_buf, sizeof(pdu_buf));
if (size != 0)
tx_entity->recv_pdu(tx_entity, pdu_buf, size);
time++;
}
EXPECT_EQ(sdu_acked_count, 3);
EXPECT_EQ(sdu_delivered_count, 3);
tx_entity->delete_entity(tx_entity);
rx_entity->delete_entity(rx_entity);
}
int main(int argc, char **argv)
{
logInit();
uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY);
g_log->log_component[RLC].level = OAILOG_TRACE;
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment