-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
huge paging #60
huge paging #60
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -37,6 +37,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||||||||||||||||||||||||||||||
#include <cstdlib> | ||||||||||||||||||||||||||||||||||
#include <type_traits> | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
#include <limits> | ||||||||||||||||||||||||||||||||||
#include <stdlib.h> // posix_memalign | ||||||||||||||||||||||||||||||||||
#include <sys/mman.h> // madvise | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
namespace mkn::kul { | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
template <typename T, std::int32_t alignment = 32> | ||||||||||||||||||||||||||||||||||
|
@@ -98,10 +102,7 @@ class Allocator { | |||||||||||||||||||||||||||||||||
using other = Allocator<U>; | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
T* allocate(std::size_t const n) const { | ||||||||||||||||||||||||||||||||||
if (n == 0) return nullptr; | ||||||||||||||||||||||||||||||||||
return static_cast<T*>(::operator new(n * sizeof(T))); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
T* allocate(std::size_t const n) const { return static_cast<T*>(::operator new(n * sizeof(T))); } | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
void deallocate(T* const p) noexcept { | ||||||||||||||||||||||||||||||||||
if (p) ::operator delete(p); | ||||||||||||||||||||||||||||||||||
|
@@ -117,16 +118,93 @@ class Allocator { | |||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
template <typename T> | ||||||||||||||||||||||||||||||||||
class NonConstructingAllocator : public Allocator<T> { | ||||||||||||||||||||||||||||||||||
using This = NonConstructingAllocator<T>; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
public: | ||||||||||||||||||||||||||||||||||
template <typename U> | ||||||||||||||||||||||||||||||||||
struct rebind { | ||||||||||||||||||||||||||||||||||
using other = NonConstructingAllocator<U>; | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
T* allocate(std::size_t const n) const { | ||||||||||||||||||||||||||||||||||
// if (n == 0) return nullptr; | ||||||||||||||||||||||||||||||||||
return static_cast<T*>(malloc(n * sizeof(T))); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
template <typename U, typename... Args> | ||||||||||||||||||||||||||||||||||
void construct(U* /*ptr*/, Args&&... /*args*/) {} // nothing | ||||||||||||||||||||||||||||||||||
template <typename U> | ||||||||||||||||||||||||||||||||||
void construct(U* /*ptr*/) noexcept(std::is_nothrow_default_constructible<U>::value) {} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
bool operator!=(This const& that) const { return !(*this == that); } | ||||||||||||||||||||||||||||||||||
bool operator==(This const& /*that*/) const { | ||||||||||||||||||||||||||||||||||
return true; // stateless | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
template <typename T, std::size_t huge_page_size = 4096> | ||||||||||||||||||||||||||||||||||
class HugePageAllocator : public Allocator<T> { | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Set Currently, Apply this diff to adjust the default huge page size: -template <typename T, std::size_t huge_page_size = 4096>
+template <typename T, std::size_t huge_page_size = 2 * 1024 * 1024>
class HugePageAllocator : public Allocator<T> { And similarly for -template <typename T, std::size_t huge_page_size = 4096>
+template <typename T, std::size_t huge_page_size = 2 * 1024 * 1024>
class NonConstructingHugePageAllocator : public NonConstructingAllocator<T> { Alternatively, consider making Also applies to: 174-175 |
||||||||||||||||||||||||||||||||||
using This = HugePageAllocator<T, huge_page_size>; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
public: | ||||||||||||||||||||||||||||||||||
template <typename U> | ||||||||||||||||||||||||||||||||||
struct rebind { | ||||||||||||||||||||||||||||||||||
using other = HugePageAllocator<U>; | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
HugePageAllocator() = default; | ||||||||||||||||||||||||||||||||||
template <class U> | ||||||||||||||||||||||||||||||||||
constexpr HugePageAllocator(HugePageAllocator<U> const&) noexcept {} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
T* allocate(std::size_t n) { | ||||||||||||||||||||||||||||||||||
if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) throw std::bad_alloc(); | ||||||||||||||||||||||||||||||||||
void* p = nullptr; | ||||||||||||||||||||||||||||||||||
posix_memalign(&p, huge_page_size, n * sizeof(T)); | ||||||||||||||||||||||||||||||||||
madvise(p, n * sizeof(T), MADV_HUGEPAGE); | ||||||||||||||||||||||||||||||||||
if (p == nullptr) throw std::bad_alloc(); | ||||||||||||||||||||||||||||||||||
return static_cast<T*>(p); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct error handling for In the
Apply this diff to fix the error handling: void* p = nullptr;
- posix_memalign(&p, huge_page_size, n * sizeof(T));
- madvise(p, n * sizeof(T), MADV_HUGEPAGE);
- if (p == nullptr) throw std::bad_alloc();
+ int rc = posix_memalign(&p, huge_page_size, n * sizeof(T));
+ if (rc != 0) throw std::bad_alloc();
+ madvise(p, n * sizeof(T), MADV_HUGEPAGE);
return static_cast<T*>(p); This ensures that 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||
void deallocate(T* p, std::size_t n) { std::free(p); } | ||||||||||||||||||||||||||||||||||
bool operator!=(This const& that) const { return !(*this == that); } | ||||||||||||||||||||||||||||||||||
bool operator==(This const& /*that*/) const { | ||||||||||||||||||||||||||||||||||
return true; // stateless | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
template <typename T, std::size_t huge_page_size = 4096> | ||||||||||||||||||||||||||||||||||
class NonConstructingHugePageAllocator : public NonConstructingAllocator<T> { | ||||||||||||||||||||||||||||||||||
using This = NonConstructingHugePageAllocator<T, huge_page_size>; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
public: | ||||||||||||||||||||||||||||||||||
template <typename U> | ||||||||||||||||||||||||||||||||||
struct rebind { | ||||||||||||||||||||||||||||||||||
using other = NonConstructingHugePageAllocator<U>; | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
NonConstructingHugePageAllocator() = default; | ||||||||||||||||||||||||||||||||||
template <class U> | ||||||||||||||||||||||||||||||||||
constexpr NonConstructingHugePageAllocator(NonConstructingHugePageAllocator<U> const&) noexcept {} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
T* allocate(std::size_t n) { | ||||||||||||||||||||||||||||||||||
if (n > std::numeric_limits<std::size_t>::max() / sizeof(T)) throw std::bad_alloc(); | ||||||||||||||||||||||||||||||||||
void* p = nullptr; | ||||||||||||||||||||||||||||||||||
posix_memalign(&p, huge_page_size, n * sizeof(T)); | ||||||||||||||||||||||||||||||||||
madvise(p, n * sizeof(T), MADV_HUGEPAGE); | ||||||||||||||||||||||||||||||||||
if (p == nullptr) throw std::bad_alloc(); | ||||||||||||||||||||||||||||||||||
return static_cast<T*>(p); | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ensure proper error handling in The same concerns apply here as in the
Apply this diff: void* p = nullptr;
- posix_memalign(&p, huge_page_size, n * sizeof(T));
- madvise(p, n * sizeof(T), MADV_HUGEPAGE);
- if (p == nullptr) throw std::bad_alloc();
+ int rc = posix_memalign(&p, huge_page_size, n * sizeof(T));
+ if (rc != 0) throw std::bad_alloc();
+ madvise(p, n * sizeof(T), MADV_HUGEPAGE);
return static_cast<T*>(p); 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
void deallocate(T* p, std::size_t n) { std::free(p); } | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
template <typename U, typename... Args> | ||||||||||||||||||||||||||||||||||
void construct(U* /*ptr*/, Args&&... /*args*/) {} // nothing | ||||||||||||||||||||||||||||||||||
template <typename U> | ||||||||||||||||||||||||||||||||||
void construct(U* /*ptr*/) noexcept(std::is_nothrow_default_constructible<U>::value) {} | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
bool operator!=(This const& that) const { return !(*this == that); } | ||||||||||||||||||||||||||||||||||
bool operator==(This const& /*that*/) const { | ||||||||||||||||||||||||||||||||||
return true; // stateless | ||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||
} // namespace mkn::kul | ||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -8,6 +8,7 @@ | |||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
#include <array> | ||||||||||||||||||||||||||||||||||||||||||
#include <cstddef> | ||||||||||||||||||||||||||||||||||||||||||
#include <cstring> | ||||||||||||||||||||||||||||||||||||||||||
#include <algorithm> | ||||||||||||||||||||||||||||||||||||||||||
#include <stdexcept> | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
|
@@ -61,6 +62,27 @@ auto copy_manual(V const& v) { | |||||||||||||||||||||||||||||||||||||||||
return out; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
template <typename V> | ||||||||||||||||||||||||||||||||||||||||||
auto copy_mem(V const& v) { | ||||||||||||||||||||||||||||||||||||||||||
KUL_DBG_FUNC_ENTER; | ||||||||||||||||||||||||||||||||||||||||||
V out; | ||||||||||||||||||||||||||||||||||||||||||
out.reserve(v.capacity()); | ||||||||||||||||||||||||||||||||||||||||||
out.resize(v.size()); | ||||||||||||||||||||||||||||||||||||||||||
memcpy(out.data(), v.data(), v.size() * sizeof(typename V::value_type)); | ||||||||||||||||||||||||||||||||||||||||||
return out; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+65
to
+73
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add type trait check for safe memcpy usage. The template <typename V>
auto copy_mem(V const& v) {
KUL_DBG_FUNC_ENTER;
+ static_assert(std::is_trivially_copyable_v<typename V::value_type>,
+ "copy_mem can only be used with trivially copyable types");
V out;
out.reserve(v.capacity());
out.resize(v.size());
memcpy(out.data(), v.data(), v.size() * sizeof(typename V::value_type));
return out;
} 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
template <typename V> | ||||||||||||||||||||||||||||||||||||||||||
auto copy_loop(V const& v) { | ||||||||||||||||||||||||||||||||||||||||||
KUL_DBG_FUNC_ENTER; | ||||||||||||||||||||||||||||||||||||||||||
V out; | ||||||||||||||||||||||||||||||||||||||||||
out.reserve(v.capacity()); | ||||||||||||||||||||||||||||||||||||||||||
out.resize(v.size()); | ||||||||||||||||||||||||||||||||||||||||||
for (std::size_t i = 0; i < v.size(); ++i) out[i] = v[i]; | ||||||||||||||||||||||||||||||||||||||||||
// memcpy(out.data(), v.data(), v.size() * sizeof(typename V::value_type)); | ||||||||||||||||||||||||||||||||||||||||||
return out; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
template <typename V> | ||||||||||||||||||||||||||||||||||||||||||
auto make_vector(std::size_t const& size) { | ||||||||||||||||||||||||||||||||||||||||||
KUL_DBG_FUNC_ENTER; | ||||||||||||||||||||||||||||||||||||||||||
|
@@ -74,25 +96,40 @@ auto make_vector_from(V1 const& v1) { | |||||||||||||||||||||||||||||||||||||||||
return v; | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
template <typename V0> | ||||||||||||||||||||||||||||||||||||||||||
void resize(V0& v) { | ||||||||||||||||||||||||||||||||||||||||||
KUL_DBG_FUNC_ENTER; | ||||||||||||||||||||||||||||||||||||||||||
v.resize(v.capacity() + 1); // force reallocation | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
template <typename T> | ||||||||||||||||||||||||||||||||||||||||||
void do_compare() { | ||||||||||||||||||||||||||||||||||||||||||
constexpr static std::size_t N = 2000000; | ||||||||||||||||||||||||||||||||||||||||||
auto const std_vec = make_vector<std::vector<T>>(N); | ||||||||||||||||||||||||||||||||||||||||||
auto const std_vec2 = make_vector_from<std::vector<T>>(std_vec); | ||||||||||||||||||||||||||||||||||||||||||
auto const no_construct_vec = make_vector_from<mkn::kul::NonConstructingVector<T>>(std_vec); | ||||||||||||||||||||||||||||||||||||||||||
if (std_vec != no_construct_vec) throw std::runtime_error("FAIL"); | ||||||||||||||||||||||||||||||||||||||||||
if (std_vec != std_vec2) throw std::runtime_error("FAIL"); | ||||||||||||||||||||||||||||||||||||||||||
auto const v0 = copy_construct(std_vec); | ||||||||||||||||||||||||||||||||||||||||||
auto const v1 = copy_construct(no_construct_vec); | ||||||||||||||||||||||||||||||||||||||||||
auto const v2 = copy_manual(std_vec); | ||||||||||||||||||||||||||||||||||||||||||
auto const v3 = copy_manual(no_construct_vec); | ||||||||||||||||||||||||||||||||||||||||||
auto const v4 = copy_operator_equal_super(no_construct_vec); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (v0 != std_vec) throw std::runtime_error("FAIL 0"); | ||||||||||||||||||||||||||||||||||||||||||
if (v0 == v1) throw std::runtime_error("FAIL 1"); // :( | ||||||||||||||||||||||||||||||||||||||||||
if (v0 != v2) throw std::runtime_error("FAIL 2"); | ||||||||||||||||||||||||||||||||||||||||||
if (v0 != v3) throw std::runtime_error("FAIL 3"); | ||||||||||||||||||||||||||||||||||||||||||
if (v0 != v4) throw std::runtime_error("FAIL 4"); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
auto wash = [&]<typename V>(auto const& name) { | ||||||||||||||||||||||||||||||||||||||||||
KOUT(NON) << name; | ||||||||||||||||||||||||||||||||||||||||||
auto const copy = make_vector_from<V>(v0); | ||||||||||||||||||||||||||||||||||||||||||
auto const v1 = copy_mem(copy); | ||||||||||||||||||||||||||||||||||||||||||
auto const v2 = copy_loop(copy); | ||||||||||||||||||||||||||||||||||||||||||
auto const v3 = copy_manual(copy); | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
if (v0 != v1) throw std::runtime_error("FAIL 1"); // :( | ||||||||||||||||||||||||||||||||||||||||||
if (v0 != v2) throw std::runtime_error("FAIL 2"); | ||||||||||||||||||||||||||||||||||||||||||
if (v0 != v3) throw std::runtime_error("FAIL 3"); | ||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
using namespace mkn::kul; | ||||||||||||||||||||||||||||||||||||||||||
wash.template operator()<std::vector<T>>("std::vector<T>"); | ||||||||||||||||||||||||||||||||||||||||||
wash.template operator()<NonConstructingVector<T>>("NonConstructingVector<T>"); | ||||||||||||||||||||||||||||||||||||||||||
wash.template operator()<HugePageVector<T>>("HugePageVector<T>"); | ||||||||||||||||||||||||||||||||||||||||||
wash.template operator()<NonConstructingHugePageVector<T>>("NonConstructingHugePageVector<T>"); | ||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
TEST(NoConstructAllocator, copies) { do_compare<S<8>>(); } | ||||||||||||||||||||||||||||||||||||||||||
TEST(NoConstructAllocator, copies) { do_compare<double>(); } | ||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add test coverage for non-trivial types. While testing with TEST(NoConstructAllocator, copies) { do_compare<double>(); }
+TEST(NoConstructAllocator, copies_complex) { do_compare<S<8>>(); } 📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||
// int main(int argc, char* argv[]) { | ||||||||||||||||||||||||||||||||||||||||||
// ::testing::InitGoogleTest(&argc, argv); | ||||||||||||||||||||||||||||||||||||||||||
// return RUN_ALL_TESTS(); | ||||||||||||||||||||||||||||||||||||||||||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ensure proper handling of zero-size allocations and allocation failures
In the
NonConstructingAllocator::allocate
method:if (n == 0) return nullptr;
) is commented out. Allocating zero elements can lead to undefined behavior. It's advisable to reintroduce this check to handle zero-size allocations safely.malloc
instead of::operator new
bypasses exception handling on allocation failure.malloc
returnsnullptr
on failure, which might not throw astd::bad_alloc
exception as expected in standard allocator behavior.Apply this diff to address these concerns:
Consider using
::operator new
to maintain consistency with standard allocation practices, unless there is a specific reason to usemalloc
.