From bc8c1e659480986b4999c0a7eddaa5f00d0cef9f Mon Sep 17 00:00:00 2001 From: Laurent Rineau Date: Wed, 8 Jan 2025 21:53:44 +0100 Subject: [PATCH] new allocator type, that allocates randomly on purpose To debug non-determinism on Linux platforms. --- .clang-format | 33 +++ Mesh_2/test/Mesh_2/CMakeLists.txt | 4 + Mesh_2/test/Mesh_2/test_meshing.cpp | 5 + STL_Extension/include/CGAL/Random_allocator.h | 191 ++++++++++++++++++ 4 files changed, 233 insertions(+) create mode 100644 .clang-format create mode 100644 STL_Extension/include/CGAL/Random_allocator.h diff --git a/.clang-format b/.clang-format new file mode 100644 index 00000000000..ea7a2200d52 --- /dev/null +++ b/.clang-format @@ -0,0 +1,33 @@ +--- +Language: Cpp +BasedOnStyle: LLVM +AccessModifierOffset: -2 +BinPackParameters: false +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: true + AfterControlStatement: MultiLine + AfterEnum: false + AfterFunction: true + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: true + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + BeforeLambdaBody: false + BeforeWhile: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +ColumnLimit: 120 +# Force pointers to the type for C++. +DerivePointerAlignment: false +PointerAlignment: Left +# Control the spaces around conditionals +SpacesInConditionalStatement: false +SpaceBeforeParens: false +... diff --git a/Mesh_2/test/Mesh_2/CMakeLists.txt b/Mesh_2/test/Mesh_2/CMakeLists.txt index 9ce291f819a..3501f20ea20 100644 --- a/Mesh_2/test/Mesh_2/CMakeLists.txt +++ b/Mesh_2/test/Mesh_2/CMakeLists.txt @@ -14,3 +14,7 @@ file( foreach(cppfile ${cppfiles}) create_single_source_cgal_program("${cppfile}") endforeach() + +if(cxx_std_20 IN_LIST CMAKE_CXX_COMPILE_FEATURES) + target_compile_features(test_meshing PRIVATE cxx_std_20) +endif() diff --git a/Mesh_2/test/Mesh_2/test_meshing.cpp b/Mesh_2/test/Mesh_2/test_meshing.cpp index 48d6ccffaf0..ed6801d28f1 100644 --- a/Mesh_2/test/Mesh_2/test_meshing.cpp +++ b/Mesh_2/test/Mesh_2/test_meshing.cpp @@ -1,5 +1,10 @@ // 154 515 565 #include +#if CGAL_CXX20 +# define CGAL_DEBUG_RANDOM_ALLOCATOR 1 +# include +# define CGAL_ALLOCATOR(T) CGAL::Random_allocator +#endif #include "test_dependencies.h" #include #if CGAL_USE_CORE || CGAL_USE_LEDA diff --git a/STL_Extension/include/CGAL/Random_allocator.h b/STL_Extension/include/CGAL/Random_allocator.h new file mode 100644 index 00000000000..51d36a901fd --- /dev/null +++ b/STL_Extension/include/CGAL/Random_allocator.h @@ -0,0 +1,191 @@ +// Copyright (c) 2025 GeometryFactory Sarl (France). +// +// This file is part of CGAL (www.cgal.org). +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// Author(s) : Laurent Rineau + +#ifndef CGAL_RANDOM_ALLOCATOR_H +#define CGAL_RANDOM_ALLOCATOR_H + +#include +#include +#include +#include + +// This header requires C++20 or later. +#include +#include +#include +#include + +namespace CGAL { + +// A random allocator that allocates blocks of memory and returns random +// locations. To use only for debugging purposes. That allocator is: +// - not efficient, +// - not thread-safe, +// - and increases memory-fragmentation and non-locality. +template > class Random_allocator +{ + constexpr static std::size_t minimal_block_size = 1024; + constexpr static std::size_t random_size = 64; + + struct Blocks + { + struct Block + { + T* data = nullptr; + boost::dynamic_bitset<> available; + std::size_t maximal_continuous_free_space = 0; + + auto size() const { return available.size(); } + }; + + Upstream_allocator alloc; + std::vector blocks; // List of allocated blocks + + void allocate_new_block(std::size_t block_size = minimal_block_size) + { + auto& block = blocks.emplace_back(nullptr, boost::dynamic_bitset<>(block_size)); + block.data = alloc.allocate(block_size); + block.available.set(); + block.maximal_continuous_free_space = block_size; + } + + Blocks(const Upstream_allocator& alloc) : alloc(alloc) { allocate_new_block(); } + + ~Blocks() + { + for(auto& block : blocks) { + alloc.deallocate(block.data, block.size()); + } + } + }; + using Block = typename Blocks::Block; + using Ptr = std::shared_ptr; + + Ptr ptr_; + std::mt19937 gen; + +public: + using value_type = T; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using pointer = T*; + using const_pointer = const T*; + using reference = T&; + using const_reference = const T&; + using allocator_type = Upstream_allocator; + + Random_allocator(const Upstream_allocator& alloc = {}) noexcept; + + pointer allocate(size_type n, const void* hint = 0); + void deallocate(pointer p, size_type n); + + template void construct(U* p, Args&&... args); + template void destroy(U* p); + + size_type max_size() const noexcept; + + template struct rebind + { + using other_upstream_allocator = std::allocator_traits::template rebind_alloc; + using other = Random_allocator; + }; + + bool operator==(const Random_allocator&) const noexcept; + bool operator!=(const Random_allocator&) const noexcept; +}; + +// Implementation of Random_allocator methods +template +Random_allocator::Random_allocator(const Upstream_allocator& alloc) noexcept + : ptr_(std::make_shared(alloc)), gen(std::random_device()()) +{ +} + +template +typename Random_allocator::pointer +Random_allocator::allocate(size_type n, const void* hint) +{ + boost::container::static_vector, random_size> found_spaces; + for(auto& block : ptr_->blocks | std::views::reverse) { + if(block.maximal_continuous_free_space < n) + continue; + auto& available = block.available; + const auto block_size = block.size(); + size_type found_max_free_space = 0; + auto index = available.find_first(); + while(index != boost::dynamic_bitset<>::npos) { + available.flip(); + const auto end_of_free_block = (std::min)(available.find_next(index), block_size); + available.flip(); + auto free_space = end_of_free_block - index; + found_max_free_space = (std::max)(found_max_free_space, free_space); + while(free_space > n && found_spaces.size() < found_spaces.capacity()) { + found_spaces.push_back({std::addressof(block), index}); + free_space -= n; + index += n; + } + index = block.available.find_next(end_of_free_block); + } + block.maximal_continuous_free_space = found_max_free_space; + if(found_spaces.size() == found_spaces.capacity()) + break; + } + if(!found_spaces.empty()) { + std::uniform_int_distribution<> dis(0, found_spaces.size() - 1); + auto i = dis(gen); + auto [block, index] = found_spaces[i]; + block->available.set(index, n, false); +#if CGAL_DEBUG_RANDOM_ALLOCATOR + std::clog << std::format("CGAL::Random_allocator debug info: n = {}, found_spaces.size() = {}, i = {}," + "block nb = {}, block size = {}, index = {}\n", + n, found_spaces.size(), i, block - ptr_->blocks.data(), block->size(), index); +#endif // CGAL_DEBUG_RANDOM_ALLOCATOR + return block->data + index; + } + size_type block_size = std::max(n * random_size, minimal_block_size); + ptr_->allocate_new_block(block_size); + return allocate(n, hint); +} + +template +void Random_allocator::deallocate(pointer p, size_type n) +{ + for(auto& block : ptr_->blocks) { + if(block.data <= p && p < block.data + block.size()) { + block.available.set(p - block.data, n, true); + return; + } + } +} + +template +template +void Random_allocator::construct(U* p, Args&&... args) +{ + ::new((void*)p) U(std::forward(args)...); +} + +template +template +void Random_allocator::destroy(U* p) +{ + p->~U(); +} + +template +typename Random_allocator::size_type +Random_allocator::max_size() const noexcept +{ + return std::numeric_limits::max() / sizeof(T); +} + +} // namespace CGAL + +#endif // CGAL_RANDOM_ALLOCATOR_H