From 427e33e9bddb55e78fc16e7c67a8982e1efaa678 Mon Sep 17 00:00:00 2001 From: pelesh Date: Fri, 3 Jul 2026 23:54:24 -0400 Subject: [PATCH 01/10] Add templated memory utilities. --- GridKit/LinearAlgebra/CMakeLists.txt | 2 + GridKit/LinearAlgebra/MemoryUtils.hpp | 372 +++++------------- GridKit/LinearAlgebra/MemoryUtils.tpp | 81 ++++ .../LinearAlgebra/SparseMatrix/CMakeLists.txt | 3 +- GridKit/LinearAlgebra/Vector/CMakeLists.txt | 11 +- GridKit/LinearAlgebra/cpu/CMakeLists.txt | 23 ++ GridKit/LinearAlgebra/cpu/CpuMemory.hpp | 148 +++++++ GridKit/LinearAlgebra/cpu/MemoryUtils.cpp | 24 ++ GridKit/Model/PhasorDynamics/Bus/Bus.hpp | 2 +- GridKit/Model/PhasorDynamics/Component.hpp | 2 +- examples/Enzyme/Standalone/EnzymeSparse.cpp | 12 +- 11 files changed, 393 insertions(+), 287 deletions(-) create mode 100644 GridKit/LinearAlgebra/MemoryUtils.tpp create mode 100644 GridKit/LinearAlgebra/cpu/CMakeLists.txt create mode 100644 GridKit/LinearAlgebra/cpu/CpuMemory.hpp create mode 100644 GridKit/LinearAlgebra/cpu/MemoryUtils.cpp diff --git a/GridKit/LinearAlgebra/CMakeLists.txt b/GridKit/LinearAlgebra/CMakeLists.txt index 98d64e05a..bd8a487c5 100644 --- a/GridKit/LinearAlgebra/CMakeLists.txt +++ b/GridKit/LinearAlgebra/CMakeLists.txt @@ -1,3 +1,5 @@ +add_subdirectory(cpu) + add_subdirectory(SparseMatrix) add_subdirectory(DenseMatrix) add_subdirectory(Vector) diff --git a/GridKit/LinearAlgebra/MemoryUtils.hpp b/GridKit/LinearAlgebra/MemoryUtils.hpp index 9738b890a..48c3a41bc 100644 --- a/GridKit/LinearAlgebra/MemoryUtils.hpp +++ b/GridKit/LinearAlgebra/MemoryUtils.hpp @@ -4,315 +4,133 @@ namespace GridKit { - namespace LinearAlgebra + namespace memory { - - namespace memory + enum MemorySpace { - enum MemorySpace - { - HOST = 0, - DEVICE - }; - - /** - * @brief Class containing dummy functions when there is no GPU support. - * - * @author Slaven Peles - */ - struct Cpu - { - /** - * @brief Dummy function to stand in when GPU support is not enabled. - */ - static void deviceSynchronize() - { - // Nothing to synchronize - } - - /** - * @brief Dummy function to stand in when GPU support is not enabled. - * - * @return Always return success! - */ - static int getLastDeviceError() - { - // not on device, nothing to get - return 0; - } - - /** - * @brief Dummy function to notify us something is wrong. - * - * This will be called only if GPU device support is not built, so - * trying to access a device should indicate a bug in the code. - * - * @return Always return failure! - */ - static int deleteOnDevice(void* /* v */) - { - std::cerr << "Trying to delete on a GPU device, but GPU support not available.\n"; - return -1; - } - - /** - * @brief Dummy function to notify us something is wrong. - * - * This will be called only if GPU device support is not built, so - * trying to access a device should indicate a bug in the code. - * - * @return Always return failure! - */ - template - static int allocateArrayOnDevice(T** /* v */, I /* n */) - { - std::cerr << "Trying to allocate on a GPU device, but GPU support not available.\n"; - return -1; - } - - /** - * @brief Dummy function to notify us something is wrong. - * - * This will be called only if GPU device support is not built, so - * trying to access a device should indicate a bug in the code. - * - * @return Always return failure! - */ - template - static int allocateBufferOnDevice(T** /* v */, I /* n */) - { - std::cerr << "Trying to allocate on a GPU device, but GPU support not available.\n"; - return -1; - } - - /** - * @brief Dummy function to notify us something is wrong. - * - * This will be called only if GPU device support is not built, so - * trying to access a device should indicate a bug in the code. - * - * @return Always return failure! - */ - template - static int setZeroArrayOnDevice(T* /* v */, I /* n */) - { - std::cerr << "Trying to initialize array on a GPU device, but GPU support not available.\n"; - return -1; - } - - /** - * @brief Dummy function to notify us something is wrong. - * - * This will be called only if GPU device support is not built, so - * trying to access a device should indicate a bug in the code. - * - * @return Always return failure! - */ - template - static int setArrayToConstOnDevice(T* /* v */, T /* c */, I /* n */) - { - std::cerr << "Trying to initialize array on a GPU device, but GPU support not available.\n"; - return -1; - } - - /** - * @brief Dummy function to notify us something is wrong. - * - * This will be called only if GPU device support is not built, so - * trying to access a device should indicate a bug in the code. - * - * @return Always return failure! - */ - template - static int copyArrayDeviceToHost(T* /* dst */, const T* /* src */, I /* n */) - { - std::cerr << "Trying to copy from a GPU device, but GPU support not available.\n"; - return -1; - } - - /** - * @brief Dummy function to notify us something is wrong. - * - * This will be called only if GPU device support is not built, so - * trying to access a device should indicate a bug in the code. - * - * @return Always return failure! - */ - template - static int copyArrayDeviceToDevice(T* /* dst */, const T* /* src */, I /* n */) - { - std::cerr << "Trying to copy to a GPU device, but GPU support not available.\n"; - return -1; - } - - template - static int copyArrayHostToDevice(T* /* dst */, const T* /* src */, I /* n */) - { - std::cerr << "Trying to copy to a GPU device, but GPU support not available.\n"; - return -1; - } - - }; // struct Cpu - }; // namespace memory - - /** - * @class MemoryUtils - * - * @brief Provides basic memory allocation, free and copy functions. - * - * This class provides abstractions for memory management functions for - * different GPU programming models. - * - * @tparam Policy - Memory management policy (vendor specific) - * - * @author Slaven Peles - */ - template - class MemoryUtils - { - public: - MemoryUtils() = default; - ~MemoryUtils() = default; - - void deviceSynchronize(); - int getLastDeviceError(); - int deleteOnDevice(void* v); - - template - int allocateArrayOnDevice(T** v, I n); - - template - int allocateBufferOnDevice(T** v, I n); - - template - int setZeroArrayOnDevice(T* v, I n); - - template - int setArrayToConstOnDevice(T* v, T c, I n); + HOST = 0, + DEVICE + }; + } // namespace memory +} // namespace GridKit - template - int copyArrayDeviceToHost(T* dst, const T* src, I n); +namespace GridKit +{ + /** + * @class MemoryUtils + * + * @brief Provides basic memory allocation, free and copy functions. + * + * This class provides abstractions for memory management functions for + * different GPU programming models. + * + * @tparam Policy - Memory management policy (vendor specific) + * + * @author Slaven Peles + */ + template + class MemoryUtils + { + public: + MemoryUtils() = default; + ~MemoryUtils() = default; - template - int copyArrayDeviceToDevice(T* dst, const T* src, I n); + void deviceSynchronize(); + int getLastDeviceError(); + int deleteOnDevice(void* v); - template - int copyArrayHostToDevice(T* dst, const T* src, I n); + template + int allocateArrayOnDevice(T** v, I n); - /// - /// Methods implemented here are always needed - /// + template + int allocateBufferOnDevice(T** v, I n); - template - int allocateArrayOnHost(T** v, I n) - { - std::size_t arraysize = static_cast(n) * sizeof(T); - *v = new T[arraysize]; - return *v == nullptr ? 1 : 0; - } + template + int setZeroArrayOnDevice(T* v, I n); - template - int deleteOnHost(T* v) - { - delete[] v; - v = nullptr; - return 0; - } + template + int setArrayToConstOnDevice(T* v, T c, I n); - template - int copyArrayHostToHost(T* dst, const T* src, I n) - { - std::size_t arraysize = static_cast(n) * sizeof(T); - memcpy(dst, src, arraysize); - return 0; - } + template + int copyArrayDeviceToHost(T* dst, const T* src, I n); - template - int setZeroArrayOnHost(T* v, I n) - { - std::size_t arraysize = static_cast(n) * sizeof(T); - memset(v, 0, arraysize); - return 0; - } + template + int copyArrayDeviceToDevice(T* dst, const T* src, I n); - template - int setArrayToConstOnHost(T* v, T c, I n) - { - for (I i = 0; i < n; ++i) - { - v[i] = c; - } - return 0; - } - }; + template + int copyArrayHostToDevice(T* dst, const T* src, I n); - template - void MemoryUtils::deviceSynchronize() - { - Policy::deviceSynchronize(); - } + /// + /// Methods implemented here are always needed + /// - template - int MemoryUtils::getLastDeviceError() + template + int allocateArrayOnHost(T** v, I n) { - return Policy::getLastDeviceError(); + *v = new T[static_cast(n)]; + return 0; } - template - int MemoryUtils::deleteOnDevice(void* v) + template + int deleteOnHost(T* v) { - return Policy::deleteOnDevice(v); + delete[] v; + v = nullptr; + return 0; } - template template - int MemoryUtils::allocateArrayOnDevice(T** v, I n) + int copyArrayHostToHost(T* dst, const T* src, I n) { - return Policy::template allocateArrayOnDevice(v, n); + std::size_t arraysize = static_cast(n) * sizeof(T); + memcpy(dst, src, arraysize); + return 0; } - template template - int MemoryUtils::allocateBufferOnDevice(T** v, I n) + int setZeroArrayOnHost(T* v, I n) { - return Policy::template allocateBufferOnDevice(v, n); + std::size_t arraysize = static_cast(n) * sizeof(T); + memset(v, 0, arraysize); + return 0; } - template template - int MemoryUtils::setZeroArrayOnDevice(T* v, I n) + int setArrayToConstOnHost(T* v, T c, I n) { - return Policy::template setZeroArrayOnDevice(v, n); + for (I i = 0; i < n; ++i) + { + v[i] = c; + } + return 0; } + }; // class MemoryUtils - template - template - int MemoryUtils::setArrayToConstOnDevice(T* v, T c, I n) - { - return Policy::template setArrayToConstOnDevice(v, c, n); - } +} // namespace GridKit - template - template - int MemoryUtils::copyArrayDeviceToHost(T* dst, const T* src, I n) - { - return Policy::template copyArrayDeviceToHost(dst, src, n); - } +// Device-side member templates are defined here so they are visible (and +// implicitly instantiated on demand for whatever a caller needs) in +// every translation unit that uses MemoryUtils, instead of relying on a +// hand-maintained list of explicit instantiations for one backend. +#include - template - template - int MemoryUtils::copyArrayDeviceToDevice(T* dst, const T* src, I n) - { - return Policy::template copyArrayDeviceToDevice(dst, src, n); - } +#ifdef GridKit_ENABLE_GPU - template - template - int MemoryUtils::copyArrayHostToDevice(T* dst, const T* src, I n) - { - return Policy::template copyArrayHostToDevice(dst, src, n); - } +// Check if GPU support is enabled in GridKit and set appropriate device memory manager. +#if defined GridKit_ENABLE_CUDA +#include +using MemoryHandler = GridKit::MemoryUtils; +#elif defined GridKit_ENABLE_HIP +#include +using MemoryHandler = GridKit::MemoryUtils; +#else +#error Unrecognized device, probably bug in CMake configuration +#endif - using MemoryHandler = MemoryUtils; - } // namespace LinearAlgebra -} // namespace GridKit +#else + +// If no GPU support is present, set device memory manager to a dummy object. +#include +using MemoryHandler = GridKit::MemoryUtils; + +#endif diff --git a/GridKit/LinearAlgebra/MemoryUtils.tpp b/GridKit/LinearAlgebra/MemoryUtils.tpp new file mode 100644 index 000000000..fd28ddda2 --- /dev/null +++ b/GridKit/LinearAlgebra/MemoryUtils.tpp @@ -0,0 +1,81 @@ +/** + * @file MemoryUtils.tpp + * + * Contains implementation of memory utility functions wrappers. + * All it does it calls vendor specific functions frm an abstract interface. + * + * @author Slaven Peles + */ + +#pragma once + +namespace GridKit +{ + template + void MemoryUtils::deviceSynchronize() + { + Policy::deviceSynchronize(); + } + + template + int MemoryUtils::getLastDeviceError() + { + return Policy::getLastDeviceError(); + } + + template + int MemoryUtils::deleteOnDevice(void* v) + { + return Policy::deleteOnDevice(v); + } + + template + template + int MemoryUtils::allocateArrayOnDevice(T** v, I n) + { + return Policy::template allocateArrayOnDevice(v, n); + } + + template + template + int MemoryUtils::allocateBufferOnDevice(T** v, I n) + { + return Policy::template allocateBufferOnDevice(v, n); + } + + template + template + int MemoryUtils::setZeroArrayOnDevice(T* v, I n) + { + return Policy::template setZeroArrayOnDevice(v, n); + } + + template + template + int MemoryUtils::setArrayToConstOnDevice(T* v, T c, I n) + { + return Policy::template setArrayToConstOnDevice(v, c, n); + } + + template + template + int MemoryUtils::copyArrayDeviceToHost(T* dst, const T* src, I n) + { + return Policy::template copyArrayDeviceToHost(dst, src, n); + } + + template + template + int MemoryUtils::copyArrayDeviceToDevice(T* dst, const T* src, I n) + { + return Policy::template copyArrayDeviceToDevice(dst, src, n); + } + + template + template + int MemoryUtils::copyArrayHostToDevice(T* dst, const T* src, I n) + { + return Policy::template copyArrayHostToDevice(dst, src, n); + } + +} // namespace GridKit diff --git a/GridKit/LinearAlgebra/SparseMatrix/CMakeLists.txt b/GridKit/LinearAlgebra/SparseMatrix/CMakeLists.txt index c7f5fb4f5..f112aef37 100644 --- a/GridKit/LinearAlgebra/SparseMatrix/CMakeLists.txt +++ b/GridKit/LinearAlgebra/SparseMatrix/CMakeLists.txt @@ -1,4 +1,5 @@ gridkit_add_library( sparse_matrix SOURCES CooMatrix.cpp CsrMatrix.cpp - HEADERS CooMatrix.hpp CsrMatrix.hpp) + HEADERS CooMatrix.hpp CsrMatrix.hpp + LINK_LIBRARIES GridKit::utilities_logger GridKit::cpu_backend) diff --git a/GridKit/LinearAlgebra/Vector/CMakeLists.txt b/GridKit/LinearAlgebra/Vector/CMakeLists.txt index a32b0cade..834becaf0 100644 --- a/GridKit/LinearAlgebra/Vector/CMakeLists.txt +++ b/GridKit/LinearAlgebra/Vector/CMakeLists.txt @@ -1,5 +1,14 @@ + + gridkit_add_library( dense_vector SOURCES Vector.cpp HEADERS Vector.hpp - LINK_LIBRARIES GridKit::utilities_logger) + LINK_LIBRARIES GridKit::utilities_logger GridKit::cpu_backend) + +# # If no GPU is enabled link to dummy device backend +# if(NOT GridKit_ENABLE_GPU) +# target_link_libraries(dense_vector PUBLIC GridKit::cpu_backend) +# endif(NOT GridKit_ENABLE_GPU) + + diff --git a/GridKit/LinearAlgebra/cpu/CMakeLists.txt b/GridKit/LinearAlgebra/cpu/CMakeLists.txt new file mode 100644 index 000000000..9116986eb --- /dev/null +++ b/GridKit/LinearAlgebra/cpu/CMakeLists.txt @@ -0,0 +1,23 @@ +#[[ + +@brief Build GridKit backend when there is no GPU support + +@author Slaven Peles + +]] + +set(GridKit_CPU_SRC MemoryUtils.cpp) + +set(GridKit_CPU_HEADER_INSTALL CpuMemory.hpp) + +# First create dummy backend +gridkit_add_library( + cpu_backend + SOURCES MemoryUtils.cpp + HEADERS CpuMemory.hpp) + +# add_library(resolve_backend_cpu SHARED ${GridKit_CPU_SRC}) +# target_link_libraries(resolve_backend_cpu PRIVATE resolve_logger) + +# install include headers +# install(FILES ${GridKit_CPU_HEADER_INSTALL} DESTINATION include/resolve/cpu) diff --git a/GridKit/LinearAlgebra/cpu/CpuMemory.hpp b/GridKit/LinearAlgebra/cpu/CpuMemory.hpp new file mode 100644 index 000000000..2b4ec595a --- /dev/null +++ b/GridKit/LinearAlgebra/cpu/CpuMemory.hpp @@ -0,0 +1,148 @@ +#pragma once + +#include + +namespace GridKit +{ + namespace memory + { + /** + * @brief Class containing dummy functions when there is no GPU support. + * + * @author Slaven Peles + */ + struct Cpu + { + /** + * @brief Dummy function to stand in when GPU support is not enabled. + */ + static void deviceSynchronize() + { + // Nothing to synchronize + } + + /** + * @brief Dummy function to stand in when GPU support is not enabled. + * + * @return Allways return success! + */ + static int getLastDeviceError() + { + // not on device, nothing to get + return 0; + } + + /** + * @brief Dummy function to notify us something is wrong. + * + * This will be called only if GPU device support is not built, so + * trying to access a device should indicate a bug in the code. + * + * @return Always return failure! + */ + static int deleteOnDevice(void* /* v */) + { + std::cerr << "Trying to delete on a GPU device, but GPU support not available.\n"; + return -1; + } + + /** + * @brief Dummy function to notify us something is wrong. + * + * This will be called only if GPU device support is not built, so + * trying to access a device should indicate a bug in the code. + * + * @return Always return failure! + */ + template + static int allocateArrayOnDevice(T** /* v */, I /* n */) + { + std::cerr << "Trying to allocate on a GPU device, but GPU support not available.\n"; + return -1; + } + + /** + * @brief Dummy function to notify us something is wrong. + * + * This will be called only if GPU device support is not built, so + * trying to access a device should indicate a bug in the code. + * + * @return Always return failure! + */ + template + static int allocateBufferOnDevice(T** /* v */, I /* n */) + { + std::cerr << "Trying to allocate on a GPU device, but GPU support not available.\n"; + return -1; + } + + /** + * @brief Dummy function to notify us something is wrong. + * + * This will be called only if GPU device support is not built, so + * trying to access a device should indicate a bug in the code. + * + * @return Always return failure! + */ + template + static int setZeroArrayOnDevice(T* /* v */, I /* n */) + { + std::cerr << "Trying to initialize array on a GPU device, but GPU support not available.\n"; + return -1; + } + + /** + * @brief Dummy function to notify us something is wrong. + * + * This will be called only if GPU device support is not built, so + * trying to access a device should indicate a bug in the code. + * + * @return Always return failure! + */ + template + static int setArrayToConstOnDevice(T* /* v */, T /* c */, I /* n */) + { + std::cerr << "Trying to initialize array on a GPU device, but GPU support not available.\n"; + return -1; + } + + /** + * @brief Dummy function to notify us something is wrong. + * + * This will be called only if GPU device support is not built, so + * trying to access a device should indicate a bug in the code. + * + * @return Always return failure! + */ + template + static int copyArrayDeviceToHost(T* /* dst */, const T* /* src */, I /* n */) + { + std::cerr << "Trying to copy from a GPU device, but GPU support not available.\n"; + return -1; + } + + /** + * @brief Dummy function to notify us something is wrong. + * + * This will be called only if GPU device support is not built, so + * trying to access a device should indicate a bug in the code. + * + * @return Always return failure! + */ + template + static int copyArrayDeviceToDevice(T* /* dst */, const T* /* src */, I /* n */) + { + std::cerr << "Trying to copy to a GPU device, but GPU support not available.\n"; + return -1; + } + + template + static int copyArrayHostToDevice(T* /* dst */, const T* /* src */, I /* n */) + { + std::cerr << "Trying to copy to a GPU device, but GPU support not available.\n"; + return -1; + } + }; // struct Cpu + + } // namespace memory +} // namespace GridKit diff --git a/GridKit/LinearAlgebra/cpu/MemoryUtils.cpp b/GridKit/LinearAlgebra/cpu/MemoryUtils.cpp new file mode 100644 index 000000000..09136c3ef --- /dev/null +++ b/GridKit/LinearAlgebra/cpu/MemoryUtils.cpp @@ -0,0 +1,24 @@ +/** + * @file MemoryUtils.cpp + * + * Explicitly instantiates the non-template members of + * MemoryUtils. The device-side member templates + * (allocateArrayOnDevice, copyArrayDeviceToHost, etc.) are defined in + * MemoryUtils.tpp, which is included directly by MemoryUtils.hpp, so they + * are implicitly instantiated on demand for whatever combination + * each caller (CooMatrix, CsrMatrix, Vector, ...) actually needs. + * + * @author Slaven Peles + */ + +#include + +#include +#include + +namespace GridKit +{ + template void MemoryUtils::deviceSynchronize(); + template int MemoryUtils::getLastDeviceError(); + template int MemoryUtils::deleteOnDevice(void*); +} // namespace GridKit diff --git a/GridKit/Model/PhasorDynamics/Bus/Bus.hpp b/GridKit/Model/PhasorDynamics/Bus/Bus.hpp index c7c78f6ee..229325710 100644 --- a/GridKit/Model/PhasorDynamics/Bus/Bus.hpp +++ b/GridKit/Model/PhasorDynamics/Bus/Bus.hpp @@ -117,7 +117,7 @@ namespace GridKit } } coo_jac_ = new CooMatrixT(num_rows, num_cols, nnz_); - coo_jac_->setDataPointers(J_rows_buffer_, J_cols_buffer_, J_vals_buffer_, LinearAlgebra::memory::HOST); + coo_jac_->setDataPointers(J_rows_buffer_, J_cols_buffer_, J_vals_buffer_, memory::HOST); } return 0; diff --git a/GridKit/Model/PhasorDynamics/Component.hpp b/GridKit/Model/PhasorDynamics/Component.hpp index 20dbd0403..8cfdd0c78 100644 --- a/GridKit/Model/PhasorDynamics/Component.hpp +++ b/GridKit/Model/PhasorDynamics/Component.hpp @@ -239,7 +239,7 @@ namespace GridKit } } coo_jac_ = new CooMatrixT(num_rows, num_cols, nnz_); - coo_jac_->setDataPointers(J_rows_buffer_, J_cols_buffer_, J_vals_buffer_, LinearAlgebra::memory::HOST); + coo_jac_->setDataPointers(J_rows_buffer_, J_cols_buffer_, J_vals_buffer_, memory::HOST); } return 0; diff --git a/examples/Enzyme/Standalone/EnzymeSparse.cpp b/examples/Enzyme/Standalone/EnzymeSparse.cpp index 4f1818c4e..de65d1847 100644 --- a/examples/Enzyme/Standalone/EnzymeSparse.cpp +++ b/examples/Enzyme/Standalone/EnzymeSparse.cpp @@ -123,14 +123,14 @@ __attribute__((noinline)) SparseMatrix* jac_f(size_t N, ScalarT* input) void check(SparseMatrix* matrix_1, SparseMatrix* matrix_2, int& fail) { size_t nnz_1 = matrix_1->getNnz(); - size_t* rows_1 = matrix_1->getRowData(GridKit::LinearAlgebra::memory::HOST); - size_t* cols_1 = matrix_1->getColData(GridKit::LinearAlgebra::memory::HOST); - double* vals_1 = matrix_1->getValues(GridKit::LinearAlgebra::memory::HOST); + size_t* rows_1 = matrix_1->getRowData(GridKit::memory::HOST); + size_t* cols_1 = matrix_1->getColData(GridKit::memory::HOST); + double* vals_1 = matrix_1->getValues(GridKit::memory::HOST); size_t nnz_2 = matrix_2->getNnz(); - size_t* rows_2 = matrix_2->getRowData(GridKit::LinearAlgebra::memory::HOST); - size_t* cols_2 = matrix_2->getColData(GridKit::LinearAlgebra::memory::HOST); - double* vals_2 = matrix_2->getValues(GridKit::LinearAlgebra::memory::HOST); + size_t* rows_2 = matrix_2->getRowData(GridKit::memory::HOST); + size_t* cols_2 = matrix_2->getColData(GridKit::memory::HOST); + double* vals_2 = matrix_2->getValues(GridKit::memory::HOST); if (nnz_1 != nnz_2) fail++; From 13676ec1e85e430ad0d6be3af44876ea75c10ddb Mon Sep 17 00:00:00 2001 From: pelesh Date: Sat, 4 Jul 2026 00:13:06 -0400 Subject: [PATCH 02/10] Minor CMake cleanup. --- GridKit/LinearAlgebra/cpu/CMakeLists.txt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/GridKit/LinearAlgebra/cpu/CMakeLists.txt b/GridKit/LinearAlgebra/cpu/CMakeLists.txt index 9116986eb..8a0f4017a 100644 --- a/GridKit/LinearAlgebra/cpu/CMakeLists.txt +++ b/GridKit/LinearAlgebra/cpu/CMakeLists.txt @@ -13,11 +13,5 @@ set(GridKit_CPU_HEADER_INSTALL CpuMemory.hpp) # First create dummy backend gridkit_add_library( cpu_backend - SOURCES MemoryUtils.cpp - HEADERS CpuMemory.hpp) - -# add_library(resolve_backend_cpu SHARED ${GridKit_CPU_SRC}) -# target_link_libraries(resolve_backend_cpu PRIVATE resolve_logger) - -# install include headers -# install(FILES ${GridKit_CPU_HEADER_INSTALL} DESTINATION include/resolve/cpu) + SOURCES ${GridKit_CPU_SRC} + HEADERS ${GridKit_CPU_HEADER_INSTALL}) From f35babedfaf1f7a40421bd777dd8232e217bb1c2 Mon Sep 17 00:00:00 2001 From: pelesh Date: Sat, 4 Jul 2026 00:48:01 -0400 Subject: [PATCH 03/10] Optimize DependencyTracking::Variable for performance. --- .../DependencyTracking/Variable.hpp | 103 ++++++++++++------ .../VariableImplementation.hpp | 31 +++--- .../DependencyTracking/VariableOperators.hpp | 76 ++++++++----- 3 files changed, 136 insertions(+), 74 deletions(-) diff --git a/GridKit/AutomaticDifferentiation/DependencyTracking/Variable.hpp b/GridKit/AutomaticDifferentiation/DependencyTracking/Variable.hpp index b6285abc0..d02172c4e 100644 --- a/GridKit/AutomaticDifferentiation/DependencyTracking/Variable.hpp +++ b/GridKit/AutomaticDifferentiation/DependencyTracking/Variable.hpp @@ -12,6 +12,7 @@ #include #include #include +#include #include #include @@ -38,8 +39,7 @@ namespace GridKit Variable() : value_(0.0), variable_number_(INVALID_VAR_NUMBER), - is_fixed_(false), - dependencies_(new DependencyMap) + is_fixed_(false) { } @@ -49,8 +49,7 @@ namespace GridKit explicit Variable(double value) : value_(value), variable_number_(INVALID_VAR_NUMBER), - is_fixed_(false), - dependencies_(new DependencyMap) + is_fixed_(false) { } @@ -62,10 +61,9 @@ namespace GridKit Variable(double value, size_t variable_number) : value_(value), variable_number_(variable_number), - is_fixed_(false), - dependencies_(new DependencyMap) + is_fixed_(false) { - (*dependencies_)[variable_number_] = 1.0; + dependencies_[variable_number_] = 1.0; } /** @@ -75,18 +73,33 @@ namespace GridKit : value_(v.value_), variable_number_(INVALID_VAR_NUMBER), is_fixed_(false), - dependencies_(new DependencyMap(*v.dependencies_)) + dependencies_(v.dependencies_) { } /** - @brief Destructor deletes the dependency map. + @brief Move constructor. + + Unlike the copy constructor, this relocates @a v rather than + creating a new, detached temporary from it, so (unlike copy) + it carries over `variable_number_`/`is_fixed_` as-is instead + of resetting them. The dependency map is relocated via + `std::map`'s own move constructor (O(1)) instead of being + deep-copied. */ - ~Variable() + Variable(Variable&& v) noexcept + : value_(v.value_), + variable_number_(v.variable_number_), + is_fixed_(v.is_fixed_), + dependencies_(std::move(v.dependencies_)) { - delete dependencies_; } + /** + @brief Destructor. + */ + ~Variable() = default; + /** @brief Assignment operator. Assigning double value to Variable removes its dependencies. Use only if you know @@ -96,7 +109,7 @@ namespace GridKit { value_ = rhs; - dependencies_->clear(); + dependencies_.clear(); return *this; } @@ -120,8 +133,34 @@ namespace GridKit setFixed(rhs.is_fixed()); // set dependencies from rhs - dependencies_->clear(); // clear map just in case - addDependencies(rhs); // use only dependencies from the rhs + dependencies_.clear(); // clear map just in case + addDependencies(rhs); // use only dependencies from the rhs + + return *this; + } + + /** + @brief Move assignment operator. + + Same contract as the copy assignment operator (value and + dependencies come from rhs, variable ID of *this is left + unchanged), but relocates rhs's dependency map via + `std::map`'s own move assignment instead of deep-copying its + entries. + */ + Variable& operator=(Variable&& rhs) noexcept + { + if (this == &rhs) // self-assignment + return *this; + + // set value from rhs + value_ = rhs.value_; + + // if rhs is a constant so is *this + setFixed(rhs.is_fixed()); + + // relocate dependencies from rhs instead of copying them + dependencies_ = std::move(rhs.dependencies_); return *this; } @@ -170,7 +209,7 @@ namespace GridKit */ double der(size_t i) const { - return (*dependencies_)[i]; + return dependencies_[i]; } /** @@ -189,9 +228,9 @@ namespace GridKit */ void setVariableNumber(size_t variable_number) { - dependencies_->clear(); - variable_number_ = variable_number; - (*dependencies_)[variable_number_] = 1.0; + dependencies_.clear(); + variable_number_ = variable_number; + dependencies_[variable_number_] = 1.0; } /** @@ -266,7 +305,7 @@ namespace GridKit size_t variable_number_; ///< Independent variable ID bool is_fixed_; ///< Constant parameter flag. - mutable DependencyMap* dependencies_; + mutable DependencyMap dependencies_; static const size_t INVALID_VAR_NUMBER = INVALID_INDEX; }; @@ -275,27 +314,27 @@ namespace GridKit //------------------------------------ // unary - - inline const Variable operator-(const Variable& v); + inline Variable operator-(const Variable& v); // + - inline const Variable operator+(const Variable& lhs, const Variable& rhs); - inline const Variable operator+(const Variable& lhs, const double& rhs); - inline const Variable operator+(const double& lhs, const Variable& rhs); + inline Variable operator+(const Variable& lhs, const Variable& rhs); + inline Variable operator+(const Variable& lhs, const double& rhs); + inline Variable operator+(const double& lhs, const Variable& rhs); // - - inline const Variable operator-(const Variable& lhs, const Variable& rhs); - inline const Variable operator-(const Variable& lhs, const double& rhs); - inline const Variable operator-(const double& lhs, const Variable& rhs); + inline Variable operator-(const Variable& lhs, const Variable& rhs); + inline Variable operator-(const Variable& lhs, const double& rhs); + inline Variable operator-(const double& lhs, const Variable& rhs); // * - inline const Variable operator*(const Variable& lhs, const Variable& rhs); - inline const Variable operator*(const Variable& lhs, const double& rhs); - inline const Variable operator*(const double& lhs, const Variable& rhs); + inline Variable operator*(const Variable& lhs, const Variable& rhs); + inline Variable operator*(const Variable& lhs, const double& rhs); + inline Variable operator*(const double& lhs, const Variable& rhs); // / - inline const Variable operator/(const Variable& lhs, const Variable& rhs); - inline const Variable operator/(const Variable& lhs, const double& rhs); - inline const Variable operator/(const double& lhs, const Variable& rhs); + inline Variable operator/(const Variable& lhs, const Variable& rhs); + inline Variable operator/(const Variable& lhs, const double& rhs); + inline Variable operator/(const double& lhs, const Variable& rhs); // == inline bool operator==(const Variable& lhs, const Variable& rhs); diff --git a/GridKit/AutomaticDifferentiation/DependencyTracking/VariableImplementation.hpp b/GridKit/AutomaticDifferentiation/DependencyTracking/VariableImplementation.hpp index c685848ee..ae55dc3e4 100644 --- a/GridKit/AutomaticDifferentiation/DependencyTracking/VariableImplementation.hpp +++ b/GridKit/AutomaticDifferentiation/DependencyTracking/VariableImplementation.hpp @@ -13,8 +13,7 @@ namespace GridKit */ const Variable::DependencyMap& Variable::getDependencies() const { - assert(dependencies_ != 0); - return *dependencies_; + return dependencies_; } /** @@ -35,8 +34,8 @@ namespace GridKit */ void Variable::addDependencies(const Variable& v) { - for (auto& p : *(v.dependencies_)) - (*dependencies_)[p.first] = p.second; + for (auto& p : v.dependencies_) + dependencies_[p.first] = p.second; } /** @@ -44,8 +43,8 @@ namespace GridKit */ void Variable::scaleDependencies(double c) { - for (auto& p : *dependencies_) - (*dependencies_)[p.first] *= c; + for (auto& p : dependencies_) + p.second *= c; } /** @@ -67,10 +66,10 @@ namespace GridKit return; } - if (dependencies_ != NULL && !dependencies_->empty()) + if (!dependencies_.empty()) { os << " dependencies: [ "; - for (auto& p : *dependencies_) + for (auto& p : dependencies_) os << "(" << p.first << ", " << p.second << ") "; os << "]"; } @@ -97,8 +96,8 @@ namespace GridKit Variable& Variable::operator+=(const Variable& rhs) { // compute partial derivatives of *this - for (auto& p : *(rhs.dependencies_)) - (*dependencies_)[p.first] += (p.second); + for (auto& p : rhs.dependencies_) + dependencies_[p.first] += (p.second); // compute value of *this value_ += rhs.value_; @@ -124,8 +123,8 @@ namespace GridKit Variable& Variable::operator-=(const Variable& rhs) { // compute partial derivatives of *this - for (auto& p : *(rhs.dependencies_)) - (*dependencies_)[p.first] -= (p.second); + for (auto& p : rhs.dependencies_) + dependencies_[p.first] -= (p.second); // compute value of *this value_ -= rhs.value_; @@ -159,8 +158,8 @@ namespace GridKit scaleDependencies(rhs.value_); // compute partial derivatives of rhs and add them to *this - for (auto& p : *(rhs.dependencies_)) - (*dependencies_)[p.first] += (p.second * value_); + for (auto& p : rhs.dependencies_) + dependencies_[p.first] += (p.second * value_); // compute value of this value_ *= rhs.value_; @@ -195,8 +194,8 @@ namespace GridKit // derivation by parts of @a *this scaleDependencies(inverseRhs); - for (auto& p : *(rhs.dependencies_)) - (*dependencies_)[p.first] -= (p.second * value_ * inverseRhsSq); + for (auto& p : rhs.dependencies_) + dependencies_[p.first] -= (p.second * value_ * inverseRhsSq); // compute value of this value_ *= inverseRhs; diff --git a/GridKit/AutomaticDifferentiation/DependencyTracking/VariableOperators.hpp b/GridKit/AutomaticDifferentiation/DependencyTracking/VariableOperators.hpp index de7a29008..02504a87b 100644 --- a/GridKit/AutomaticDifferentiation/DependencyTracking/VariableOperators.hpp +++ b/GridKit/AutomaticDifferentiation/DependencyTracking/VariableOperators.hpp @@ -13,73 +13,97 @@ namespace GridKit //------------------------------------ // unary - - const Variable operator-(const Variable& v) + Variable operator-(const Variable& v) { return -1.0 * v; } // + - const Variable operator+(const Variable& lhs, const Variable& rhs) + Variable operator+(const Variable& lhs, const Variable& rhs) { - return Variable(lhs) += rhs; + Variable result(lhs); + result += rhs; + return result; } - const Variable operator+(const Variable& lhs, const double& rhs) + Variable operator+(const Variable& lhs, const double& rhs) { - return Variable(lhs) += rhs; + Variable result(lhs); + result += rhs; + return result; } - const Variable operator+(const double& lhs, const Variable& rhs) + Variable operator+(const double& lhs, const Variable& rhs) { - return Variable(rhs) += lhs; + Variable result(rhs); + result += lhs; + return result; } // - - const Variable operator-(const Variable& lhs, const Variable& rhs) + Variable operator-(const Variable& lhs, const Variable& rhs) { - return Variable(lhs) -= rhs; + Variable result(lhs); + result -= rhs; + return result; } - const Variable operator-(const Variable& lhs, const double& rhs) + Variable operator-(const Variable& lhs, const double& rhs) { - return Variable(lhs) -= rhs; + Variable result(lhs); + result -= rhs; + return result; } - const Variable operator-(const double& lhs, const Variable& rhs) + Variable operator-(const double& lhs, const Variable& rhs) { - return Variable(lhs) -= rhs; + Variable result(lhs); + result -= rhs; + return result; } // * - const Variable operator*(const Variable& lhs, const Variable& rhs) + Variable operator*(const Variable& lhs, const Variable& rhs) { - return Variable(lhs) *= rhs; + Variable result(lhs); + result *= rhs; + return result; } - const Variable operator*(const Variable& lhs, const double& rhs) + Variable operator*(const Variable& lhs, const double& rhs) { - return Variable(lhs) *= rhs; + Variable result(lhs); + result *= rhs; + return result; } - const Variable operator*(const double& lhs, const Variable& rhs) + Variable operator*(const double& lhs, const Variable& rhs) { - return Variable(lhs) *= rhs; + Variable result(lhs); + result *= rhs; + return result; } // / - const Variable operator/(const Variable& lhs, const Variable& rhs) + Variable operator/(const Variable& lhs, const Variable& rhs) { - return Variable(lhs) /= rhs; + Variable result(lhs); + result /= rhs; + return result; } - const Variable operator/(const Variable& lhs, const double& rhs) + Variable operator/(const Variable& lhs, const double& rhs) { - return Variable(lhs) /= rhs; + Variable result(lhs); + result /= rhs; + return result; } - const Variable operator/(const double& lhs, const Variable& rhs) + Variable operator/(const double& lhs, const Variable& rhs) { - return Variable(lhs) /= rhs; + Variable result(lhs); + result /= rhs; + return result; } // == @@ -367,7 +391,7 @@ namespace std res2.setValue(val); /* set function value f(x, y) */ \ res1.scaleDependencies(der1); /* compute derivatives of f(x, y) with respect to x */ \ res2.scaleDependencies(der2); /* compute derivatives of f(x, y) with respect to y */ \ - GridKit::DependencyTracking::Variable res(res1); /* add derivatives with respect to x to return */ \ + GridKit::DependencyTracking::Variable res(std::move(res1)); /* relocate derivatives w.r.t. x into return */ \ res.addDependencies(res2); /* add derivatives with respect to y to return */ \ return res; \ } From f895d7d0570dfa38577b5c81cc130e8d41a48e93 Mon Sep 17 00:00:00 2001 From: pelesh Date: Sat, 4 Jul 2026 01:35:53 -0400 Subject: [PATCH 04/10] Improvements to Vector class. --- GridKit/LinearAlgebra/MemoryUtils.hpp | 2 +- GridKit/LinearAlgebra/Vector/Vector.cpp | 128 ++++++++++++++++++++---- GridKit/LinearAlgebra/Vector/Vector.hpp | 2 +- 3 files changed, 111 insertions(+), 21 deletions(-) diff --git a/GridKit/LinearAlgebra/MemoryUtils.hpp b/GridKit/LinearAlgebra/MemoryUtils.hpp index 48c3a41bc..13d1bfb49 100644 --- a/GridKit/LinearAlgebra/MemoryUtils.hpp +++ b/GridKit/LinearAlgebra/MemoryUtils.hpp @@ -72,7 +72,7 @@ namespace GridKit } template - int deleteOnHost(T* v) + int deleteOnHost(T*& v) { delete[] v; v = nullptr; diff --git a/GridKit/LinearAlgebra/Vector/Vector.cpp b/GridKit/LinearAlgebra/Vector/Vector.cpp index abb77228e..8bf2be557 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.cpp +++ b/GridKit/LinearAlgebra/Vector/Vector.cpp @@ -107,8 +107,9 @@ namespace GridKit * @param[in] data - Pointer to data * @param[in] memspace - Memory space (HOST or DEVICE) * - * @pre Vector data pointers must be null. If the vector data already - * exists this function returns error message. + * @pre The vector's data pointer for the given `memspace` must be null. + * If data for that memspace already exists this function returns an + * error message; the other memspace's pointer is unaffected. * * @warning This function DOES NOT ALLOCATE any data, it only assigns the * pointer. @@ -120,21 +121,26 @@ namespace GridKit int Vector::setData(ScalarT* data, memory::MemorySpace memspace) { using namespace memory; - if (d_data_ || h_data_) - { - out::error() << "Vector::setData - data already exists, ignoring call\n"; - return 1; - } switch (memspace) { case HOST: + if (h_data_) + { + out::error() << "Vector::setData - host data already exists, ignoring call\n"; + return 1; + } h_data_ = data; setHostUpdated(true); setDeviceUpdated(false); owns_cpu_data_ = false; break; case DEVICE: + if (d_data_) + { + out::error() << "Vector::setData - device data already exists, ignoring call\n"; + return 1; + } d_data_ = data; setHostUpdated(false); setDeviceUpdated(true); @@ -194,6 +200,15 @@ namespace GridKit assert(cpu_updated_ && gpu_updated_ && "Update flags not allocated"); using namespace memory; + + if (k_ <= j) + { + out::error() << "Vector::setDataUpdated - vector index " << j + << " out of range, multivector has only " << k_ + << " vectors\n"; + return 1; + } + switch (memspace) { case HOST: @@ -241,6 +256,12 @@ namespace GridKit memory::MemorySpace memspaceSrc, memory::MemorySpace memspaceDst) { + if (source == nullptr) + { + out::error() << "Vector::copyFromExternal - source data is null or stale\n"; + return 1; + } + switch (memspaceDst) { case memory::HOST: @@ -572,6 +593,14 @@ namespace GridKit { using namespace memory; + if (k_ <= j) + { + out::error() << "Vector::syncData - vector index " << j + << " out of range, multivector has only " << k_ + << " vectors\n"; + return 1; + } + switch (memspaceDst) { case DEVICE: // cpu->gpu @@ -631,6 +660,7 @@ namespace GridKit switch (memspace) { case HOST: + { if (!owns_cpu_data_) { out::error() << "Vector::allocate - cannot reallocate host data," @@ -638,11 +668,18 @@ namespace GridKit return 1; } mem_.deleteOnHost(h_data_); - mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); + int rc = mem_.allocateArrayOnHost(&h_data_, n_capacity_ * k_); + if (rc != 0) + { + out::error() << "Vector::allocate - failed to allocate host data\n"; + return 1; + } owns_cpu_data_ = true; setHostUpdated(false); break; + } case DEVICE: + { if (!owns_gpu_data_) { out::error() << "Vector::allocate - cannot reallocate device data," @@ -650,11 +687,17 @@ namespace GridKit return 1; } mem_.deleteOnDevice(d_data_); - mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + int rc = mem_.allocateArrayOnDevice(&d_data_, n_capacity_ * k_); + if (rc != 0) + { + out::error() << "Vector::allocate - failed to allocate device data\n"; + return 1; + } owns_gpu_data_ = true; setDeviceUpdated(false); break; } + } return 0; } @@ -708,6 +751,15 @@ namespace GridKit int Vector::setToZero(IdxT j, memory::MemorySpace memspace) { using namespace memory; + + if (k_ <= j) + { + out::error() << "Vector::setToZero - vector index " << j + << " out of range, multivector has only " << k_ + << " vectors\n"; + return 1; + } + switch (memspace) { case HOST: @@ -787,6 +839,15 @@ namespace GridKit int Vector::setToConst(IdxT j, ScalarT C, memory::MemorySpace memspace) { using namespace memory; + + if (k_ <= j) + { + out::error() << "Vector::setToConst - vector index " << j + << " out of range, multivector has only " << k_ + << " vectors\n"; + return 1; + } + switch (memspace) { case HOST: @@ -817,16 +878,22 @@ namespace GridKit * @brief Resize vector to `new_n_size`. * * Use for vectors and multivectors that change size throughout computation. + * If `new_n_size` is within the current capacity, this simply adjusts + * `n_size_`. If `new_n_size` exceeds the current capacity, the vector + * reallocates - in whichever memory spaces (HOST and/or DEVICE) are + * currently allocated - to a buffer sized for `new_n_size`, copies over + * the existing per-column data, and adopts `new_n_size` as the new + * capacity. Data beyond the previous size is left uninitialized, same + * as freshly allocated data. * - * @note Vector needs to have capacity set to maximum expected size. * @warning This method is not to be used in vectors who do not own their * data. * * @param[in] new_n_size - New vector length * * @return 0 if successful, 1 otherwise. - * - * @pre `new_n_size` <= `n_capacity_` + * + * @todo Decide if we need to preserve data when resizing. */ template int Vector::resize(IdxT new_n_size) @@ -834,17 +901,40 @@ namespace GridKit assert(owns_cpu_data_ && owns_gpu_data_ && "Cannot resize if vector is not owning the data."); - if (new_n_size > n_capacity_) - { - out::error() << "Vector::resize - requested size " << new_n_size - << " exceeds capacity " << n_capacity_ << "\n"; - return 1; - } - else + if (new_n_size <= n_capacity_) { n_size_ = new_n_size; return 0; } + + // Grow beyond current capacity: reallocate and copy over existing + // per-column data. Columns are stored on n_size_-element strides, so + // the old stride must be captured before n_size_/n_capacity_ change. + const IdxT old_n_size = n_size_; + + if (h_data_ != nullptr) + { + ScalarT* new_data = nullptr; + mem_.allocateArrayOnHost(&new_data, new_n_size * k_); + for (IdxT j = 0; j < k_; ++j) + mem_.copyArrayHostToHost(&new_data[j * new_n_size], &h_data_[j * old_n_size], old_n_size); + mem_.deleteOnHost(h_data_); + h_data_ = new_data; + } + + if (d_data_ != nullptr) + { + ScalarT* new_data = nullptr; + mem_.allocateArrayOnDevice(&new_data, new_n_size * k_); + for (IdxT j = 0; j < k_; ++j) + mem_.copyArrayDeviceToDevice(&new_data[j * new_n_size], &d_data_[j * old_n_size], old_n_size); + mem_.deleteOnDevice(d_data_); + d_data_ = new_data; + } + + n_capacity_ = new_n_size; + n_size_ = new_n_size; + return 0; } /** diff --git a/GridKit/LinearAlgebra/Vector/Vector.hpp b/GridKit/LinearAlgebra/Vector/Vector.hpp index 2690fb0d9..d3f403b0f 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.hpp +++ b/GridKit/LinearAlgebra/Vector/Vector.hpp @@ -31,7 +31,7 @@ namespace GridKit class Vector { public: - Vector() = default; + Vector() : Vector(0) {} Vector(IdxT n); Vector(IdxT n, IdxT k); ~Vector(); From d29f2d0ba6a2d83a652f2e3a56282aff23e9b67b Mon Sep 17 00:00:00 2001 From: pelesh Date: Sat, 4 Jul 2026 13:11:24 -0400 Subject: [PATCH 05/10] MemoryUtils can handle complex data types. --- GridKit/LinearAlgebra/MemoryUtils.hpp | 43 ++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/GridKit/LinearAlgebra/MemoryUtils.hpp b/GridKit/LinearAlgebra/MemoryUtils.hpp index 13d1bfb49..e5a075c92 100644 --- a/GridKit/LinearAlgebra/MemoryUtils.hpp +++ b/GridKit/LinearAlgebra/MemoryUtils.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include namespace GridKit { @@ -79,19 +80,53 @@ namespace GridKit return 0; } + /** + * @brief Copy an array from HOST to HOST. + * + * Trivially copyable types (plain scalars, etc.) are copied with a + * byte-wise memcpy. Types that are not trivially copyable (e.g. a type + * owning a heap-allocated data structure) would have that structure + * corrupted by a byte-wise copy, so those are copied element-by-element + * using the type's own copy assignment instead. + */ template int copyArrayHostToHost(T* dst, const T* src, I n) { - std::size_t arraysize = static_cast(n) * sizeof(T); - memcpy(dst, src, arraysize); + if constexpr (std::is_trivially_copyable_v) + { + std::size_t arraysize = static_cast(n) * sizeof(T); + memcpy(dst, src, arraysize); + } + else + { + for (I i = 0; i < n; ++i) + dst[i] = src[i]; + } return 0; } + /** + * @brief Set an array on HOST to zero. + * + * Trivially copyable types are zeroed with a byte-wise memset. Types + * that are not trivially copyable would have their internal state + * corrupted by a byte-wise zeroing, so those are reset to a + * value-initialized (default-constructed) state element-by-element + * instead. + */ template int setZeroArrayOnHost(T* v, I n) { - std::size_t arraysize = static_cast(n) * sizeof(T); - memset(v, 0, arraysize); + if constexpr (std::is_trivially_copyable_v) + { + std::size_t arraysize = static_cast(n) * sizeof(T); + memset(v, 0, arraysize); + } + else + { + for (I i = 0; i < n; ++i) + v[i] = T{}; + } return 0; } From b5daec13c390f8f960f33116bbbc32a3696f833e Mon Sep 17 00:00:00 2001 From: pelesh Date: Sat, 4 Jul 2026 15:26:23 -0400 Subject: [PATCH 06/10] Move memory utilities to their own folder. --- GridKit/CMakeLists.txt | 3 +++ GridKit/LinearAlgebra/CMakeLists.txt | 4 ---- GridKit/LinearAlgebra/SparseMatrix/CooMatrix.hpp | 2 +- GridKit/LinearAlgebra/SparseMatrix/CsrMatrix.hpp | 2 +- GridKit/LinearAlgebra/Vector/Vector.hpp | 2 +- GridKit/MemoryUtilities/CMakeLists.txt | 12 ++++++++++++ .../MemoryUtils.hpp | 4 ++-- .../MemoryUtils.tpp | 0 .../cpu/CMakeLists.txt | 0 .../cpu/CpuMemory.hpp | 0 .../cpu/MemoryUtils.cpp | 4 ++-- examples/Enzyme/Standalone/EnzymeSparse.cpp | 2 +- tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp | 2 +- 13 files changed, 24 insertions(+), 13 deletions(-) create mode 100644 GridKit/MemoryUtilities/CMakeLists.txt rename GridKit/{LinearAlgebra => MemoryUtilities}/MemoryUtils.hpp (97%) rename GridKit/{LinearAlgebra => MemoryUtilities}/MemoryUtils.tpp (100%) rename GridKit/{LinearAlgebra => MemoryUtilities}/cpu/CMakeLists.txt (100%) rename GridKit/{LinearAlgebra => MemoryUtilities}/cpu/CpuMemory.hpp (100%) rename GridKit/{LinearAlgebra => MemoryUtilities}/cpu/MemoryUtils.cpp (87%) diff --git a/GridKit/CMakeLists.txt b/GridKit/CMakeLists.txt index f1ce7b5a4..4c3f7ffef 100644 --- a/GridKit/CMakeLists.txt +++ b/GridKit/CMakeLists.txt @@ -11,6 +11,9 @@ install(TARGETS definitions EXPORT gridkit-targets) # General Utilities and File IO add_subdirectory(Utilities) +# Memory management utilities +add_subdirectory(MemoryUtilities) + # Create component models add_subdirectory(Model) diff --git a/GridKit/LinearAlgebra/CMakeLists.txt b/GridKit/LinearAlgebra/CMakeLists.txt index bd8a487c5..e645e23ed 100644 --- a/GridKit/LinearAlgebra/CMakeLists.txt +++ b/GridKit/LinearAlgebra/CMakeLists.txt @@ -1,7 +1,3 @@ -add_subdirectory(cpu) - add_subdirectory(SparseMatrix) add_subdirectory(DenseMatrix) add_subdirectory(Vector) - -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.hpp DESTINATION include/GridKit/LinearAlgebra) diff --git a/GridKit/LinearAlgebra/SparseMatrix/CooMatrix.hpp b/GridKit/LinearAlgebra/SparseMatrix/CooMatrix.hpp index b167d2760..54313e863 100644 --- a/GridKit/LinearAlgebra/SparseMatrix/CooMatrix.hpp +++ b/GridKit/LinearAlgebra/SparseMatrix/CooMatrix.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include namespace GridKit { diff --git a/GridKit/LinearAlgebra/SparseMatrix/CsrMatrix.hpp b/GridKit/LinearAlgebra/SparseMatrix/CsrMatrix.hpp index ce1a89c50..d661a07cd 100644 --- a/GridKit/LinearAlgebra/SparseMatrix/CsrMatrix.hpp +++ b/GridKit/LinearAlgebra/SparseMatrix/CsrMatrix.hpp @@ -4,7 +4,7 @@ #include #include -#include +#include namespace GridKit { diff --git a/GridKit/LinearAlgebra/Vector/Vector.hpp b/GridKit/LinearAlgebra/Vector/Vector.hpp index d3f403b0f..900b8fe18 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.hpp +++ b/GridKit/LinearAlgebra/Vector/Vector.hpp @@ -1,7 +1,7 @@ #pragma once #include -#include +#include namespace GridKit { diff --git a/GridKit/MemoryUtilities/CMakeLists.txt b/GridKit/MemoryUtilities/CMakeLists.txt new file mode 100644 index 000000000..0e6db005d --- /dev/null +++ b/GridKit/MemoryUtilities/CMakeLists.txt @@ -0,0 +1,12 @@ +#[[ + +@brief Build GridKit memory management utilities + +@author Slaven Peles + +]] + +add_subdirectory(cpu) + +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.hpp ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.tpp + DESTINATION include/GridKit/MemoryUtilities) diff --git a/GridKit/LinearAlgebra/MemoryUtils.hpp b/GridKit/MemoryUtilities/MemoryUtils.hpp similarity index 97% rename from GridKit/LinearAlgebra/MemoryUtils.hpp rename to GridKit/MemoryUtilities/MemoryUtils.hpp index e5a075c92..64cd01ef8 100644 --- a/GridKit/LinearAlgebra/MemoryUtils.hpp +++ b/GridKit/MemoryUtilities/MemoryUtils.hpp @@ -147,7 +147,7 @@ namespace GridKit // implicitly instantiated on demand for whatever a caller needs) in // every translation unit that uses MemoryUtils, instead of relying on a // hand-maintained list of explicit instantiations for one backend. -#include +#include #ifdef GridKit_ENABLE_GPU @@ -165,7 +165,7 @@ using MemoryHandler = GridKit::MemoryUtils; #else // If no GPU support is present, set device memory manager to a dummy object. -#include +#include using MemoryHandler = GridKit::MemoryUtils; #endif diff --git a/GridKit/LinearAlgebra/MemoryUtils.tpp b/GridKit/MemoryUtilities/MemoryUtils.tpp similarity index 100% rename from GridKit/LinearAlgebra/MemoryUtils.tpp rename to GridKit/MemoryUtilities/MemoryUtils.tpp diff --git a/GridKit/LinearAlgebra/cpu/CMakeLists.txt b/GridKit/MemoryUtilities/cpu/CMakeLists.txt similarity index 100% rename from GridKit/LinearAlgebra/cpu/CMakeLists.txt rename to GridKit/MemoryUtilities/cpu/CMakeLists.txt diff --git a/GridKit/LinearAlgebra/cpu/CpuMemory.hpp b/GridKit/MemoryUtilities/cpu/CpuMemory.hpp similarity index 100% rename from GridKit/LinearAlgebra/cpu/CpuMemory.hpp rename to GridKit/MemoryUtilities/cpu/CpuMemory.hpp diff --git a/GridKit/LinearAlgebra/cpu/MemoryUtils.cpp b/GridKit/MemoryUtilities/cpu/MemoryUtils.cpp similarity index 87% rename from GridKit/LinearAlgebra/cpu/MemoryUtils.cpp rename to GridKit/MemoryUtilities/cpu/MemoryUtils.cpp index 09136c3ef..501fabb20 100644 --- a/GridKit/LinearAlgebra/cpu/MemoryUtils.cpp +++ b/GridKit/MemoryUtilities/cpu/MemoryUtils.cpp @@ -13,8 +13,8 @@ #include -#include -#include +#include +#include namespace GridKit { diff --git a/examples/Enzyme/Standalone/EnzymeSparse.cpp b/examples/Enzyme/Standalone/EnzymeSparse.cpp index de65d1847..eb9db9d2a 100644 --- a/examples/Enzyme/Standalone/EnzymeSparse.cpp +++ b/examples/Enzyme/Standalone/EnzymeSparse.cpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include diff --git a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp index 8cb36bcd7..61c125f51 100644 --- a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp +++ b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp @@ -6,7 +6,7 @@ #include #include -#include +#include #include #include From 01514977e4d19ede35771fd9aed6cf8edc38478d Mon Sep 17 00:00:00 2001 From: pelesh Date: Sat, 4 Jul 2026 15:29:34 -0400 Subject: [PATCH 07/10] Add placeholders for CUDA and HIP backends. --- GridKit/MemoryUtilities/CMakeLists.txt | 2 ++ GridKit/MemoryUtilities/cuda/.gitkeep | 0 GridKit/MemoryUtilities/hip/.gitkeep | 0 3 files changed, 2 insertions(+) create mode 100644 GridKit/MemoryUtilities/cuda/.gitkeep create mode 100644 GridKit/MemoryUtilities/hip/.gitkeep diff --git a/GridKit/MemoryUtilities/CMakeLists.txt b/GridKit/MemoryUtilities/CMakeLists.txt index 0e6db005d..67e867be3 100644 --- a/GridKit/MemoryUtilities/CMakeLists.txt +++ b/GridKit/MemoryUtilities/CMakeLists.txt @@ -7,6 +7,8 @@ ]] add_subdirectory(cpu) +# add_subdirectory(cuda) +# add_subdirectory(hip) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.hpp ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.tpp DESTINATION include/GridKit/MemoryUtilities) diff --git a/GridKit/MemoryUtilities/cuda/.gitkeep b/GridKit/MemoryUtilities/cuda/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/GridKit/MemoryUtilities/hip/.gitkeep b/GridKit/MemoryUtilities/hip/.gitkeep new file mode 100644 index 000000000..e69de29bb From e09dd3d9e581537f856b1d227ac94466cda922bf Mon Sep 17 00:00:00 2001 From: pelesh Date: Sat, 4 Jul 2026 19:45:12 +0000 Subject: [PATCH 08/10] Apply pre-commit fixes --- .../DependencyTracking/Variable.hpp | 4 +-- .../DependencyTracking/VariableOperators.hpp | 32 +++++++++---------- GridKit/LinearAlgebra/Vector/CMakeLists.txt | 3 -- GridKit/LinearAlgebra/Vector/Vector.cpp | 2 +- GridKit/LinearAlgebra/Vector/Vector.hpp | 6 +++- GridKit/MemoryUtilities/CMakeLists.txt | 3 +- examples/Enzyme/Standalone/EnzymeSparse.cpp | 2 +- .../LinearAlgebra/Vector/VectorTests.hpp | 2 +- 8 files changed, 28 insertions(+), 26 deletions(-) diff --git a/GridKit/AutomaticDifferentiation/DependencyTracking/Variable.hpp b/GridKit/AutomaticDifferentiation/DependencyTracking/Variable.hpp index d02172c4e..6e2f216d0 100644 --- a/GridKit/AutomaticDifferentiation/DependencyTracking/Variable.hpp +++ b/GridKit/AutomaticDifferentiation/DependencyTracking/Variable.hpp @@ -229,7 +229,7 @@ namespace GridKit void setVariableNumber(size_t variable_number) { dependencies_.clear(); - variable_number_ = variable_number; + variable_number_ = variable_number; dependencies_[variable_number_] = 1.0; } @@ -306,7 +306,7 @@ namespace GridKit bool is_fixed_; ///< Constant parameter flag. mutable DependencyMap dependencies_; - static const size_t INVALID_VAR_NUMBER = INVALID_INDEX; + static const size_t INVALID_VAR_NUMBER = INVALID_INDEX; }; //------------------------------------ diff --git a/GridKit/AutomaticDifferentiation/DependencyTracking/VariableOperators.hpp b/GridKit/AutomaticDifferentiation/DependencyTracking/VariableOperators.hpp index 02504a87b..ec786da3d 100644 --- a/GridKit/AutomaticDifferentiation/DependencyTracking/VariableOperators.hpp +++ b/GridKit/AutomaticDifferentiation/DependencyTracking/VariableOperators.hpp @@ -378,22 +378,22 @@ namespace std return res; \ } -#define IMPL_FUN_2(FUN, DER1, DER2) \ - inline GridKit::DependencyTracking::Variable FUN(const GridKit::DependencyTracking::Variable& x, \ - const GridKit::DependencyTracking::Variable& y) \ - { \ - double val = FUN(x(), y()); \ - double der1 = DER1(x(), y()); \ - double der2 = DER2(x(), y()); \ - GridKit::DependencyTracking::Variable res1(x); /* copy derivatives of x*/ \ - GridKit::DependencyTracking::Variable res2(y); /* copy derivatives of y*/ \ - res1.setValue(val); /* set function value f(x, y) */ \ - res2.setValue(val); /* set function value f(x, y) */ \ - res1.scaleDependencies(der1); /* compute derivatives of f(x, y) with respect to x */ \ - res2.scaleDependencies(der2); /* compute derivatives of f(x, y) with respect to y */ \ - GridKit::DependencyTracking::Variable res(std::move(res1)); /* relocate derivatives w.r.t. x into return */ \ - res.addDependencies(res2); /* add derivatives with respect to y to return */ \ - return res; \ +#define IMPL_FUN_2(FUN, DER1, DER2) \ + inline GridKit::DependencyTracking::Variable FUN(const GridKit::DependencyTracking::Variable& x, \ + const GridKit::DependencyTracking::Variable& y) \ + { \ + double val = FUN(x(), y()); \ + double der1 = DER1(x(), y()); \ + double der2 = DER2(x(), y()); \ + GridKit::DependencyTracking::Variable res1(x); /* copy derivatives of x*/ \ + GridKit::DependencyTracking::Variable res2(y); /* copy derivatives of y*/ \ + res1.setValue(val); /* set function value f(x, y) */ \ + res2.setValue(val); /* set function value f(x, y) */ \ + res1.scaleDependencies(der1); /* compute derivatives of f(x, y) with respect to x */ \ + res2.scaleDependencies(der2); /* compute derivatives of f(x, y) with respect to y */ \ + GridKit::DependencyTracking::Variable res(std::move(res1)); /* relocate derivatives w.r.t. x into return */ \ + res.addDependencies(res2); /* add derivatives with respect to y to return */ \ + return res; \ } IMPL_FUN_1(sin, GridKit::DependencyTracking::sin_derivative) diff --git a/GridKit/LinearAlgebra/Vector/CMakeLists.txt b/GridKit/LinearAlgebra/Vector/CMakeLists.txt index 834becaf0..6b700c3d4 100644 --- a/GridKit/LinearAlgebra/Vector/CMakeLists.txt +++ b/GridKit/LinearAlgebra/Vector/CMakeLists.txt @@ -1,5 +1,4 @@ - gridkit_add_library( dense_vector SOURCES Vector.cpp @@ -10,5 +9,3 @@ gridkit_add_library( # if(NOT GridKit_ENABLE_GPU) # target_link_libraries(dense_vector PUBLIC GridKit::cpu_backend) # endif(NOT GridKit_ENABLE_GPU) - - diff --git a/GridKit/LinearAlgebra/Vector/Vector.cpp b/GridKit/LinearAlgebra/Vector/Vector.cpp index 8bf2be557..bbbf82c98 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.cpp +++ b/GridKit/LinearAlgebra/Vector/Vector.cpp @@ -892,7 +892,7 @@ namespace GridKit * @param[in] new_n_size - New vector length * * @return 0 if successful, 1 otherwise. - * + * * @todo Decide if we need to preserve data when resizing. */ template diff --git a/GridKit/LinearAlgebra/Vector/Vector.hpp b/GridKit/LinearAlgebra/Vector/Vector.hpp index 900b8fe18..f337d71e2 100644 --- a/GridKit/LinearAlgebra/Vector/Vector.hpp +++ b/GridKit/LinearAlgebra/Vector/Vector.hpp @@ -31,7 +31,11 @@ namespace GridKit class Vector { public: - Vector() : Vector(0) {} + Vector() + : Vector(0) + { + } + Vector(IdxT n); Vector(IdxT n, IdxT k); ~Vector(); diff --git a/GridKit/MemoryUtilities/CMakeLists.txt b/GridKit/MemoryUtilities/CMakeLists.txt index 67e867be3..3cf65f94a 100644 --- a/GridKit/MemoryUtilities/CMakeLists.txt +++ b/GridKit/MemoryUtilities/CMakeLists.txt @@ -10,5 +10,6 @@ add_subdirectory(cpu) # add_subdirectory(cuda) # add_subdirectory(hip) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.hpp ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.tpp +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/MemoryUtils.tpp DESTINATION include/GridKit/MemoryUtilities) diff --git a/examples/Enzyme/Standalone/EnzymeSparse.cpp b/examples/Enzyme/Standalone/EnzymeSparse.cpp index eb9db9d2a..8649d1ddf 100644 --- a/examples/Enzyme/Standalone/EnzymeSparse.cpp +++ b/examples/Enzyme/Standalone/EnzymeSparse.cpp @@ -6,8 +6,8 @@ #include #include -#include #include +#include #include /** diff --git a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp index 61c125f51..afc4a7f30 100644 --- a/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp +++ b/tests/UnitTests/LinearAlgebra/Vector/VectorTests.hpp @@ -6,8 +6,8 @@ #include #include -#include #include +#include #include namespace GridKit From de7db759810570a3bc279eb919568f3b490be310 Mon Sep 17 00:00:00 2001 From: pelesh Date: Sat, 4 Jul 2026 19:46:11 +0000 Subject: [PATCH 09/10] Apply pre-commit fixes --- GridKit/LinearAlgebra/Vector/CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/GridKit/LinearAlgebra/Vector/CMakeLists.txt b/GridKit/LinearAlgebra/Vector/CMakeLists.txt index 6b700c3d4..314eed26e 100644 --- a/GridKit/LinearAlgebra/Vector/CMakeLists.txt +++ b/GridKit/LinearAlgebra/Vector/CMakeLists.txt @@ -1,4 +1,3 @@ - gridkit_add_library( dense_vector SOURCES Vector.cpp From 4b41565b22cc301c290ede74d6db54b0b33cab2d Mon Sep 17 00:00:00 2001 From: pelesh Date: Mon, 6 Jul 2026 16:29:45 -0400 Subject: [PATCH 10/10] Minor fixes and changelog update. --- CHANGELOG.md | 1 + GridKit/MemoryUtilities/MemoryUtils.hpp | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba0f1dde6..ec0df53b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,7 @@ - Added `ConstantSignalSource` component as first use case for `BusToSignalAdapter`. - Added IDA option to suppress algebraic variables in local error tests. - Removed `COO_Matrix` class. +- Added portable `Vector` class and policy-based memory utilities. ## v0.1 diff --git a/GridKit/MemoryUtilities/MemoryUtils.hpp b/GridKit/MemoryUtilities/MemoryUtils.hpp index 64cd01ef8..ca9419932 100644 --- a/GridKit/MemoryUtilities/MemoryUtils.hpp +++ b/GridKit/MemoryUtilities/MemoryUtils.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -95,7 +96,7 @@ namespace GridKit if constexpr (std::is_trivially_copyable_v) { std::size_t arraysize = static_cast(n) * sizeof(T); - memcpy(dst, src, arraysize); + std::memcpy(dst, src, arraysize); } else { @@ -120,7 +121,7 @@ namespace GridKit if constexpr (std::is_trivially_copyable_v) { std::size_t arraysize = static_cast(n) * sizeof(T); - memset(v, 0, arraysize); + std::memset(v, 0, arraysize); } else {