diff --git a/exec/CMakeLists.txt b/exec/CMakeLists.txt index 2bcc975e..09415811 100644 --- a/exec/CMakeLists.txt +++ b/exec/CMakeLists.txt @@ -17,6 +17,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() +include(CTest) find_package(CUDAToolkit REQUIRED) @@ -33,6 +34,15 @@ target_include_directories(flashrt_exec ${CMAKE_CURRENT_SOURCE_DIR}/backend) target_link_libraries(flashrt_exec PUBLIC CUDA::cudart) +if(BUILD_TESTING) + add_executable(test_graph_lru_alloc tests/test_graph_lru_alloc.cpp) + target_include_directories(test_graph_lru_alloc PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${CMAKE_CURRENT_SOURCE_DIR}/src) + target_link_libraries(test_graph_lru_alloc PRIVATE flashrt_exec) + add_test(NAME graph_lru_alloc COMMAND test_graph_lru_alloc) +endif() + # --- pybind dev module (optional) --- find_package(Python3 COMPONENTS Interpreter Development.Module QUIET) find_package(pybind11 CONFIG QUIET) diff --git a/exec/src/graph.cpp b/exec/src/graph.cpp index 100c67a3..b5a776fc 100644 --- a/exec/src/graph.cpp +++ b/exec/src/graph.cpp @@ -4,7 +4,10 @@ void frt_graph_s::touch(frt_shape_key key) { for (auto it = lru.begin(); it != lru.end(); ++it) { - if (*it == key) { lru.erase(it); break; } + if (*it == key) { + lru.splice(lru.end(), lru, it); + return; + } } lru.push_back(key); // back = most recently used } diff --git a/exec/tests/test_graph_lru_alloc.cpp b/exec/tests/test_graph_lru_alloc.cpp new file mode 100644 index 00000000..2343087c --- /dev/null +++ b/exec/tests/test_graph_lru_alloc.cpp @@ -0,0 +1,61 @@ +#include "internal.h" + +#include +#include +#include +#include + +namespace { + +std::atomic count_allocations{false}; +std::atomic allocation_count{0}; + +[[noreturn]] void fail(const char* expression, int line) { + std::cerr << "FAIL line " << line << ": " << expression << '\n'; + std::abort(); +} + +#define CHECK(expression) \ + do { \ + if (!(expression)) fail(#expression, __LINE__); \ + } while (false) + +} // namespace + +void* operator new(std::size_t bytes) { + if (count_allocations.load(std::memory_order_relaxed)) { + allocation_count.fetch_add(1, std::memory_order_relaxed); + } + if (void* pointer = std::malloc(bytes)) return pointer; + throw std::bad_alloc(); +} + +void operator delete(void* pointer) noexcept { std::free(pointer); } +void operator delete(void* pointer, std::size_t) noexcept { + std::free(pointer); +} + +int main() { + frt_graph_s graph; + graph.lru = {1, 2, 3}; + + allocation_count.store(0, std::memory_order_relaxed); + count_allocations.store(true, std::memory_order_relaxed); + for (int i = 0; i < 1000; ++i) graph.touch((i % 3) + 1); + count_allocations.store(false, std::memory_order_relaxed); + + CHECK(allocation_count.load(std::memory_order_relaxed) == 0); + CHECK(graph.lru.size() == 3); + CHECK(graph.lru.back() == 1); + + allocation_count.store(0, std::memory_order_relaxed); + count_allocations.store(true, std::memory_order_relaxed); + graph.touch(4); + count_allocations.store(false, std::memory_order_relaxed); + CHECK(allocation_count.load(std::memory_order_relaxed) > 0); + CHECK(graph.lru.size() == 4); + CHECK(graph.lru.back() == 4); + + std::cout << "PASS - graph LRU touch allocation contract\n"; + return 0; +}