Skip to content

Instantly share code, notes, and snippets.

@j-c-cook
Created November 23, 2025 19:58
Show Gist options
  • Select an option

  • Save j-c-cook/876a37804ca1f8cace22b1147198a243 to your computer and use it in GitHub Desktop.

Select an option

Save j-c-cook/876a37804ca1f8cace22b1147198a243 to your computer and use it in GitHub Desktop.
AgIsoStack TP Hybrid Optimization Performance Test

Transport Protocol Hybrid Optimization Test

Tests the hybrid optimization (+24-35% performance improvement) that intelligently chooses between inline byte-copying (≤4 bytes) and memcpy (>4 bytes) for AgIsoStack transport protocols.

Quick Test

# Test baseline (main branch)
sed -i 's/GIT_TAG.*/GIT_TAG main/' CMakeLists.txt
rm -rf build-baseline && mkdir build-baseline && cd build-baseline
cmake .. && cmake --build . --target tp_integration_test
./tp_integration_test | tee ../baseline.txt
cd ..

# Test optimized (TP-Optimization branch)
sed -i 's/GIT_TAG.*/GIT_TAG TP-Optimization/' CMakeLists.txt
rm -rf build-opt && mkdir build-opt && cd build-opt
cmake .. && cmake --build . --target tp_integration_test
./tp_integration_test | tee ../optimized.txt
cd ..

# Compare
diff baseline.txt optimized.txt

Results

Message Size Improvement
100 bytes +24.4% faster
1000 bytes +32.5% faster
1785 bytes +34.7% faster

Files Modified

The hybrid optimization is in these files (TP-Optimization branch):

  • isobus/src/can_transport_protocol.cpp (lines 510-531)
  • isobus/src/can_extended_transport_protocol.cpp (lines 440-461)
  • isobus/src/nmea2000_fast_packet_protocol.cpp (lines 328-351, 414-437)
# Edit CMakeLists.txt: Change GIT_TAG to 'main'
sed -i 's/GIT_TAG .*/GIT_TAG main # Change to test different branches/' CMakeLists.txt
# Clean up any old builds
rm -rf build-baseline
# Create build directory
mkdir build-baseline
cd build-baseline
# Configure CMake
cmake ..
# Build the integration test (this uses REAL AgIsoStack library code)
cmake --build . --target tp_integration_test
# Run and save results
./tp_integration_test | tee ../baseline-integration.txt
cd ..
# Edit CMakeLists.txt: Change GIT_TAG to 'TP-Optimization'
sed -i 's/GIT_TAG .*/GIT_TAG TP-Optimization # Change to test different branches/' CMakeLists.txt
# Clean up any old builds
rm -rf build-optimized
# Create separate build directory
mkdir build-optimized
cd build-optimized
# Configure CMake
cmake ..
# Build the integration test (this uses REAL AgIsoStack library code)
cmake --build . --target tp_integration_test
# Run and save results
./tp_integration_test | tee ../optimized-integration.txt
cd ..
cmake_minimum_required(VERSION 3.16)
project(TPOptimizationValidation)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Enable FetchContent
include(FetchContent)
# Fetch AgIsoStack-plus-plus from GitHub
# NOTE: Change GIT_TAG to test different branches
FetchContent_Declare(
AgIsoStack
GIT_REPOSITORY https://github.com/j-c-cook/AgIsoStack-plus-plus.git
GIT_TAG TP-Optimization # Change to test different branches
)
# Make AgIsoStack available
FetchContent_MakeAvailable(AgIsoStack)
# Find required packages
find_package(Threads REQUIRED)
# Create performance test executable
add_executable(tp_integration_test integration_test.cpp)
target_link_libraries(tp_integration_test
PRIVATE
isobus::Isobus
isobus::HardwareIntegration
isobus::Utility
Threads::Threads
)
# Enable SocketCAN driver for Linux
if(UNIX AND NOT APPLE)
target_compile_definitions(tp_integration_test PRIVATE ISOBUS_SOCKETCAN_AVAILABLE)
endif()
# Set compiler flags
if(CMAKE_BUILD_TYPE MATCHES Release)
target_compile_options(tp_integration_test PRIVATE -O3)
elseif(CMAKE_BUILD_TYPE MATCHES Debug)
target_compile_options(tp_integration_test PRIVATE -g -O0)
endif()
#include "isobus/isobus/can_transport_protocol.hpp"
#include "isobus/isobus/can_message.hpp"
#include "isobus/isobus/can_network_configuration.hpp"
#include "isobus/isobus/can_internal_control_function.hpp"
#include "isobus/isobus/can_partnered_control_function.hpp"
#include "isobus/isobus/can_control_function.hpp"
#include "isobus/isobus/can_identifier.hpp"
#include "isobus/isobus/can_constants.hpp"
#include "isobus/isobus/can_NAME.hpp"
#include "isobus/isobus/can_callbacks.hpp"
#include <iostream>
#include <vector>
#include <chrono>
#include <cstring>
#include <iomanip>
#include <memory>
#include <functional>
using namespace isobus;
// Test configuration
const int NUM_ITERATIONS = 1000;
// Helper to create test data
std::vector<std::uint8_t> createTestData(size_t size) {
std::vector<std::uint8_t> data(size);
for (size_t i = 0; i < size; i++) {
data[i] = static_cast<std::uint8_t>(i & 0xFF);
}
return data;
}
// Helper to verify received data
bool verifyData(const std::vector<std::uint8_t>& expected, const std::vector<std::uint8_t>& actual) {
if (expected.size() != actual.size()) {
std::cout << " [FAIL] Size mismatch: expected " << expected.size() << ", got " << actual.size() << std::endl;
return false;
}
for (size_t i = 0; i < expected.size(); i++) {
if (expected[i] != actual[i]) {
std::cout << " [FAIL] Data mismatch at byte " << i << std::endl;
return false;
}
}
return true;
}
// Test TP with real library
void testTPWithLibrary(size_t messageSize, int iterations) {
std::cout << "\nTesting TP with AgIsoStack library (" << messageSize << " bytes, " << iterations << " iterations)..." << std::endl;
// Create test data
auto testData = createTestData(messageSize);
// Track received messages
std::vector<std::uint8_t> receivedData;
bool messageReceived = false;
// Receive callback
auto receiveCallback = [&](const CANMessage& message) {
messageReceived = true;
receivedData = message.get_data();
};
// Send callback (no-op for this test, uses CANDataSpan)
CANMessageFrameCallback sendCallback = [](std::uint32_t, CANDataSpan, std::shared_ptr<InternalControlFunction>, std::shared_ptr<ControlFunction>, CANIdentifier::CANPriority) {
return true;
};
// Create network configuration
CANNetworkConfiguration config;
// Create TP manager
TransportProtocolManager tpManager(sendCallback, receiveCallback, &config);
// Create mock control function (use proper constructor with CANPort)
NAME sourceName(0);
auto source = std::make_shared<ControlFunction>(sourceName, 0x01, 0, ControlFunction::Type::External);
// Calculate packets needed
const std::uint8_t PROTOCOL_BYTES_PER_FRAME = 7;
std::uint16_t numPackets = (messageSize + PROTOCOL_BYTES_PER_FRAME - 1) / PROTOCOL_BYTES_PER_FRAME;
// Performance timing
auto start = std::chrono::high_resolution_clock::now();
for (int iter = 0; iter < iterations; iter++) {
messageReceived = false;
receivedData.clear();
// 1. Send BAM (Broadcast Announce Message)
std::vector<std::uint8_t> bamData = {
32, // BAM mux
static_cast<std::uint8_t>(messageSize & 0xFF), // Length LSB
static_cast<std::uint8_t>((messageSize >> 8) & 0xFF), // Length MSB
static_cast<std::uint8_t>(numPackets), // Total packets
0xFF, // Reserved
0xEC, // PGN LSB (using 0xFEEC as test PGN)
0xFE, // PGN middle
0x00 // PGN MSB
};
CANIdentifier bamId(CANIdentifier::Type::Extended, 0xEC00, CANIdentifier::CANPriority::PriorityDefault6, 0xFF, 0x01);
CANMessage bamMessage(CANMessage::Type::Receive, bamId, bamData, source, nullptr, 0);
tpManager.process_message(bamMessage);
// 2. Send data transfer packets (THIS IS WHERE THE OPTIMIZED memcpy CODE RUNS)
std::uint32_t dataOffset = 0;
for (std::uint16_t seq = 1; seq <= numPackets; seq++) {
std::vector<std::uint8_t> frameData(8, 0xFF);
frameData[0] = static_cast<std::uint8_t>(seq);
std::size_t bytesToCopy = std::min(
static_cast<std::size_t>(PROTOCOL_BYTES_PER_FRAME),
messageSize - dataOffset
);
std::memcpy(&frameData[1], &testData[dataOffset], bytesToCopy);
dataOffset += bytesToCopy;
CANIdentifier dtId(CANIdentifier::Type::Extended, 0xEB00, CANIdentifier::CANPriority::PriorityDefault6, 0xFF, 0x01);
CANMessage dtMessage(CANMessage::Type::Receive, dtId, frameData, source, nullptr, 0);
// THIS CALL TRIGGERS THE OPTIMIZED CODE PATH!
tpManager.process_message(dtMessage);
}
tpManager.update();
}
auto end = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
// Verify last iteration
if (!messageReceived) {
std::cout << " [FAIL] Message not received" << std::endl;
return;
}
if (!verifyData(testData, receivedData)) {
std::cout << " [FAIL] Data verification failed" << std::endl;
return;
}
std::cout << " [PASS] Data integrity verified" << std::endl;
// Performance metrics
double avgTimePerMessage = static_cast<double>(duration) / iterations;
double packetsPerSecond = (iterations * numPackets * 1000000.0) / duration;
double throughputMBps = (iterations * messageSize * 1000000.0) / (duration * 1024.0 * 1024.0);
std::cout << " Performance:" << std::endl;
std::cout << " Average time per message: " << std::fixed << std::setprecision(2)
<< avgTimePerMessage << " µs" << std::endl;
std::cout << " Packets per second: " << std::fixed << std::setprecision(0)
<< packetsPerSecond << std::endl;
std::cout << " Throughput: " << std::fixed << std::setprecision(2)
<< throughputMBps << " MB/s" << std::endl;
}
int main() {
std::cout << "========================================" << std::endl;
std::cout << "AgIsoStack TP Optimization Integration Test" << std::endl;
std::cout << "Testing ACTUAL library code with hybrid optimization" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "\n============================================" << std::endl;
std::cout << "TRANSPORT PROTOCOL (TP) TESTS" << std::endl;
std::cout << "============================================" << std::endl;
testTPWithLibrary(100, NUM_ITERATIONS);
testTPWithLibrary(1000, NUM_ITERATIONS);
testTPWithLibrary(1785, NUM_ITERATIONS);
std::cout << "\n========================================" << std::endl;
std::cout << "INTEGRATION TEST COMPLETE" << std::endl;
std::cout << "========================================" << std::endl;
std::cout << "\nThis test uses the ACTUAL AgIsoStack library code." << std::endl;
std::cout << "Performance differences reflect real optimization impact." << std::endl;
std::cout << "\nNote: ETP uses identical optimization code (same 7-byte frame logic)." << std::endl;
std::cout << "TP results demonstrate the performance gains for all protocols." << std::endl;
std::cout << "\nTo compare:" << std::endl;
std::cout << "1. Build with GIT_TAG=main -> save as baseline-integration.txt" << std::endl;
std::cout << "2. Build with GIT_TAG=TP-Optimization -> save as optimized-integration.txt" << std::endl;
std::cout << "3. Compare the timing results" << std::endl;
std::cout << "========================================" << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment