Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 68 additions & 10 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ OPTION ( CO_SIM_IO_BUILD_PYTHON "Building the CoSimIO for Python"
OPTION ( CO_SIM_IO_BUILD_FORTRAN "Building the CoSimIO for Fortran" OFF )
OPTION ( CO_SIM_IO_STRICT_COMPILER "Compiler has more warnings" OFF )
OPTION ( CO_SIM_IO_BUILD_TESTING "Build tests" ${BUILD_TESTING} )
OPTION ( CO_SIM_IO_BUILD_BENCHMARK "Build benchmarks" OFF )

if(NOT DEFINED CO_SIM_IO_BUILD_TYPE)
if(CMAKE_BUILD_TYPE)
Expand All @@ -44,9 +45,10 @@ message(" CO_SIM_IO_BUILD_C: " ${CO_SIM_IO_BUILD_C})
message(" CO_SIM_IO_BUILD_PYTHON: " ${CO_SIM_IO_BUILD_PYTHON})
message(" CO_SIM_IO_BUILD_FORTRAN: " ${CO_SIM_IO_BUILD_FORTRAN})
message(" CO_SIM_IO_STRICT_COMPILER: " ${CO_SIM_IO_STRICT_COMPILER})
message(" CO_SIM_IO_BUILD_BENCHMARK: " ${CO_SIM_IO_BUILD_BENCHMARK})
message("")

# needs to be here as otherwise doesn't find MPI properly
# Needs to be here as otherwise doesn't find MPI properly
if (CO_SIM_IO_BUILD_MPI)
find_package(MPI REQUIRED)
add_definitions( -DCO_SIM_IO_USING_MPI )
Expand All @@ -56,6 +58,7 @@ if (CO_SIM_IO_BUILD_MPI)
include_directories(SYSTEM ${MPI_INCLUDE_PATH})
endif()

# Threads are needed for the CoSimIO library
find_package (Threads REQUIRED)

# detect whether CoSimIO is integrated into another project
Expand All @@ -70,8 +73,9 @@ if (NOT hasParent)
else(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
message("-- User defined install dir ${CMAKE_INSTALL_PREFIX}")
endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
endif()
endif(NOT hasParent)

# If the compiler is MSVC, we need to set the correct flags
if(MSVC)
# /Zc:__cplusplus: required such that "__cplusplus" is set to the correct value
# see https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/
Expand Down Expand Up @@ -126,14 +130,15 @@ endif()
message("CMAKE_CXX_FLAGS:" ${CMAKE_CXX_FLAGS})
message("CMAKE_C_FLAGS:" ${CMAKE_C_FLAGS})

# In case of MacOS, we need to set CMP0042 to NEW to avoid warnings
if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
cmake_policy(SET CMP0042 NEW)
endif()
endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")

# clean the bin directory
# Clean the bin directory
if (NOT hasParent)
file(REMOVE_RECURSE bin)
endif()
endif(NOT hasParent)

# Adding CoSimIO library
file(GLOB_RECURSE co_sim_io_source_files
Expand All @@ -149,6 +154,7 @@ generate_export_header( co_sim_io EXPORT_MACRO_NAME CO_SIM_IO_API EXPORT_FILE_NA

install(TARGETS co_sim_io DESTINATION bin)

# MPI communication
if (CO_SIM_IO_BUILD_MPI)
# optionally enable communication via MPI
OPTION ( CO_SIM_IO_BUILD_MPI_COMMUNICATION "Enabling communication via MPI" OFF )
Expand All @@ -167,20 +173,23 @@ if (CO_SIM_IO_BUILD_MPI)
target_link_libraries(co_sim_io_mpi co_sim_io ${MPI_LIBRARIES})

install(TARGETS co_sim_io_mpi DESTINATION bin)
endif()
endif(CO_SIM_IO_BUILD_MPI)

target_include_directories(co_sim_io PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/co_sim_io)
target_include_directories(co_sim_io SYSTEM PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/external_libraries)
target_include_directories(co_sim_io SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/external_libraries/asio/include)

# C bindings
if (CO_SIM_IO_BUILD_C)
add_subdirectory(co_sim_io/c)
endif()
endif(CO_SIM_IO_BUILD_C)

# Python bindings
if (CO_SIM_IO_BUILD_PYTHON)
add_subdirectory(co_sim_io/python)
endif()
endif(CO_SIM_IO_BUILD_PYTHON)

# Fortran bindings
if (CO_SIM_IO_BUILD_FORTRAN)
if (NOT CO_SIM_IO_BUILD_C)
message(FATAL_ERROR "Fortran requires the C interface!")
Expand All @@ -190,8 +199,9 @@ if (CO_SIM_IO_BUILD_FORTRAN)
co_sim_io/fortran
NO_EXTERNAL_INSTALL
)
endif()
endif(CO_SIM_IO_BUILD_FORTRAN)

# Testing
if (CO_SIM_IO_BUILD_TESTING)
if(CMAKE_MAJOR_VERSION LESS 3)
message(FATAL_ERROR "Building the tests requires CMake 3")
Expand All @@ -203,4 +213,52 @@ if (CO_SIM_IO_BUILD_TESTING)
"tests/compiled_config.json.in"
"${CMAKE_CURRENT_SOURCE_DIR}/tests/compiled_config.json"
)
endif()
endif(CO_SIM_IO_BUILD_TESTING)

# Benchmarking
if(CO_SIM_IO_BUILD_BENCHMARK MATCHES ON)
# Includes for the Google Benchmark library
# This function automatically configures a given application to build its benchmarks
macro(co_sim_io_add_benchmarks)
set(options USE_MPI USE_CUSTOM_MAIN)
set(oneValueArgs TARGET WORKING_DIRECTORY)
set(multiValueArgs SOURCES)

cmake_parse_arguments(CO_SIM_IO_ADD_BENCHMARK "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if(CO_SIM_IO_ADD_BENCHMARK_SOURCES)
include(GoogleBenchmark)
endif(CO_SIM_IO_ADD_BENCHMARK_SOURCES)
endmacro(co_sim_io_add_benchmarks)

# Add the definitions if required
ADD_DEFINITIONS(-DCO_SIM_IO_BUILD_BENCHMARKING)

# Retrieve a copy of the current directory's `COMPILE_OPTIONS`
get_directory_property(co_sim_io_root_compile_options COMPILE_OPTIONS)

# Disable warnings (needed by centos. We should all love centos, it clearly needs some affection)
add_compile_options(-w)

# Fetch the Google Benchmark library
include(FetchContent)
FetchContent_Declare(
googlebenchmark
URL https://github.com/google/benchmark/archive/v1.9.0.zip
)
# For Windows: Prevent overriding the parent project's compiler/linker settings
set(BENCHMARK_ENABLE_TESTING OFF CACHE BOOL "" FORCE)
set(CMAKE_INSTALL_LIBDIR "bin")
set(CMAKE_INSTALL_BINDIR "bin")
FetchContent_GetProperties(googlebenchmark)
if(NOT googlebenchmark_POPULATED)
FetchContent_Populate(googlebenchmark)
add_subdirectory(${googlebenchmark_SOURCE_DIR} ${googlebenchmark_BINARY_DIR} EXCLUDE_FROM_ALL)
endif(NOT googlebenchmark_POPULATED)

# Restore the current directory's old `COMPILE_OPTIONS`
set_directory_properties(PROPERTIES COMPILE_OPTIONS "${co_sim_io_root_compile_options}")

# Add the benchmarking directory
add_subdirectory(benchmarks)
endif(CO_SIM_IO_BUILD_BENCHMARK MATCHES ON)
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,4 @@ Coupling requires frequent exchange of data. Therefore the _CoSimIO_ uses the me
- [filesystem](https://github.com/gulrak/filesystem) Header-only single-file std::filesystem compatible helper library, based on the C++17 specs
- [asio](https://think-async.com/Asio/) for socket based interprocess communication
- [doctest](https://github.com/onqtam/doctest) C++ testing framework for the unit tests
- [Google Benchmark](https://github.com/google/benchmark) for microbenchmarking and performance testing
18 changes: 18 additions & 0 deletions benchmarks/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
include_directories( ${CMAKE_SOURCE_DIR}/co_sim_io )

# CoSimIO benchmark sources. Disabled by default
if(${CO_SIM_IO_BUILD_BENCHMARK} MATCHES ON)
file(GLOB_RECURSE CO_SIM_IO_BENCHMARK_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)

foreach(file ${CO_SIM_IO_BENCHMARK_SOURCES})
get_filename_component(filename ${file} NAME_WE)
add_executable(${filename} ${file})
target_link_libraries(${filename} PUBLIC co_sim_io benchmark::benchmark)
set_target_properties(${filename} PROPERTIES COMPILE_DEFINITIONS "CO_SIM_IO_BENCHMARK=IMPORT,API")
install(TARGETS ${filename} DESTINATION "bin/benchmarks")
endforeach(file ${CO_SIM_IO_BENCHMARK_SOURCES})
endif(${CO_SIM_IO_BUILD_BENCHMARK} MATCHES ON)

# TODO: Add MPI support in the future if required
120 changes: 120 additions & 0 deletions benchmarks/communication_benchmark.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// ______ _____ _ ________
// / ____/___ / ___/(_)___ ___ / _/ __ |
// / / / __ \\__ \/ / __ `__ \ / // / / /
// / /___/ /_/ /__/ / / / / / / // // /_/ /
// \____/\____/____/_/_/ /_/ /_/___/\____/
// Kratos CoSimulationApplication
//
// License: BSD License, see license.txt
//
// Main authors: Vicente Mataix Ferrandiz

// System includes
#include <thread>
#include <chrono>
#include <vector>
#include <numeric>
#include <cassert>

// External includes
#include <benchmark/benchmark.h>

// Project includes
#include "includes/communication/communication.hpp"
#include "includes/communication/factory.hpp"

namespace CoSimIO
{

//--------------------------------------------------------------------------
// Helper: ExportDataHelper from tests/co_sim_io/cpp/test_communication.cpp
//--------------------------------------------------------------------------
void ExportDataHelper(
Info settings,
const std::vector<std::vector<double>>& DataToExport)
{
settings.Set<std::string>("my_name", "thread");
settings.Set<std::string>("connect_to", "main");
settings.Set<bool>("is_primary_connection", false);
settings.Set<int>("echo_level", 0);

using Communication = Internals::Communication;
std::unique_ptr<Communication> p_comm = Internals::CommunicationFactory().Create(settings, std::make_shared<Internals::DataCommunicator>());

// Give the primary thread a chance to prepare (as in your test)
std::this_thread::sleep_for(std::chrono::milliseconds(20));

Info connect_info;
p_comm->Connect(connect_info);

Info export_info;
export_info.Set<std::string>("identifier", "data_exchange");

for (const auto& data : DataToExport) {
const Internals::DataContainerStdVectorReadOnly<double> data_container(data);
p_comm->ExportData(export_info, data_container);
}

Info disconnect_info;
p_comm->Disconnect(disconnect_info);
}

//--------------------------------------------------------------------------
// Google Benchmark for "import_export_large_data" from tests/co_sim_io/cpp/test_communication.cpp
//--------------------------------------------------------------------------
static void BM_ImportExportLargeData(benchmark::State& state) {
// Get size in MB from benchmark parameter.
const std::size_t size_MB = static_cast<std::size_t>(state.range(0));
const std::size_t size_B = size_MB * 1024 * 1024;
const std::size_t size_vec = size_B / sizeof(double);

// Prepare the large data vector (size_MB MB of doubles)
std::vector<std::vector<double>> exp_data(1);
exp_data[0].resize(size_vec);
std::iota(exp_data[0].begin(), exp_data[0].end(), 0);

// Set up common communication settings
Info settings;
settings.Set<std::string>("my_name", "main");
settings.Set<std::string>("connect_to", "thread");
settings.Set<std::string>("communication_format", "pipe");
settings.Set<bool>("is_primary_connection", true);
settings.Set<int>("echo_level", 0);

// Benchmark loop: each iteration performs a full connect-import-disconnect cycle
for (auto _ : state) {
using Communication = Internals::Communication;
std::unique_ptr<Communication> main_comm = Internals::CommunicationFactory().Create(settings, std::make_shared<Internals::DataCommunicator>());

// Launch the exporter in a separate thread (it uses ExportDataHelper)
std::thread export_thread(ExportDataHelper, settings, exp_data);

// Connect the main (import) side
Info connect_info;
main_comm->Connect(connect_info);

// Prepare a container for the imported data
std::vector<double> imported_data;
Internals::DataContainerStdVector<double> data_container(imported_data);

Info import_info;
import_info.Set<std::string>("identifier", "data_exchange");

// Perform the import operation (this is what we want to measure)
main_comm->ImportData(import_info, data_container);

// Disconnect the main communication
Info disconnect_info;
main_comm->Disconnect(disconnect_info);

// Ensure the exporter thread completes before starting the next iteration
export_thread.join();
}
}

// Register the benchmark with different sizes in MB (e.g., 5 MB, 20 MB, 100 MB and 2000 MB)
BENCHMARK(BM_ImportExportLargeData)->Arg(5)->Arg(20)->Arg(100)->Arg(2000);

} // namespace CoSimIO

BENCHMARK_MAIN();
Loading