Compare commits

..

4 Commits

Author SHA1 Message Date
Peter Dimov
82e80c7175 Implement quick_allocator in terms of std::allocator and deprecate it 2025-10-24 19:32:03 +03:00
Peter Dimov
81318213a6 quick_allocator supports dealloc(nullptr) 2025-10-24 19:11:54 +03:00
Peter Dimov
85c2a6ea74 The hint parameter of allocate has been removed in C++20 2025-10-24 18:04:26 +03:00
Peter Dimov
9723621128 Add tests for quick_allocator 2025-10-24 17:21:29 +03:00
4 changed files with 130 additions and 163 deletions

View File

@@ -1,198 +1,63 @@
#ifndef BOOST_SMART_PTR_DETAIL_QUICK_ALLOCATOR_HPP_INCLUDED
#define BOOST_SMART_PTR_DETAIL_QUICK_ALLOCATOR_HPP_INCLUDED
// MS compatible compilers support #pragma once
// Copyright 2003 David Abrahams
// Copyright 2003, 2025 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
# pragma once
#endif
#include <boost/config/header_deprecated.hpp>
#include <memory>
#include <cstddef>
//
// detail/quick_allocator.hpp
//
// Copyright (c) 2003 David Abrahams
// Copyright (c) 2003 Peter Dimov
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
#include <boost/config.hpp>
#include <boost/smart_ptr/detail/lightweight_mutex.hpp>
#include <boost/smart_ptr/detail/sp_type_traits.hpp>
#include <type_traits>
#include <new> // ::operator new, ::operator delete
#include <cstddef> // std::size_t
BOOST_HEADER_DEPRECATED("std::allocator or std::pmr::synchronized_pool_resource")
namespace boost
{
namespace detail
{
template<unsigned size, unsigned align_> union freeblock
template<class T> struct quick_allocator
{
typedef typename sp_type_with_alignment<align_>::type aligner_type;
aligner_type aligner;
char bytes[size];
freeblock * next;
};
template<unsigned size, unsigned align_> struct allocator_impl
{
typedef freeblock<size, align_> block;
// It may seem odd to use such small pages.
//
// However, on a typical Windows implementation that uses
// the OS allocator, "normal size" pages interact with the
// "ordinary" operator new, slowing it down dramatically.
//
// 512 byte pages are handled by the small object allocator,
// and don't interfere with ::new.
//
// The other alternative is to use much bigger pages (1M.)
//
// It is surprisingly easy to hit pathological behavior by
// varying the page size. g++ 2.96 on Red Hat Linux 7.2,
// for example, passionately dislikes 496. 512 seems OK.
#if defined(BOOST_QA_PAGE_SIZE)
enum { items_per_page = BOOST_QA_PAGE_SIZE / size };
#else
enum { items_per_page = 512 / size }; // 1048560 / size
#endif
#ifdef BOOST_HAS_THREADS
static lightweight_mutex & mutex()
static void* alloc()
{
static freeblock< sizeof( lightweight_mutex ), std::alignment_of< lightweight_mutex >::value > fbm;
static lightweight_mutex * pm = new( &fbm ) lightweight_mutex;
return *pm;
return std::allocator<T>().allocate( 1 );
}
static lightweight_mutex * mutex_init;
#endif
static block * free;
static block * page;
static unsigned last;
static inline void * alloc()
static void* alloc( std::size_t n )
{
#ifdef BOOST_HAS_THREADS
lightweight_mutex::scoped_lock lock( mutex() );
#endif
if(block * x = free)
if( n != sizeof(T) ) // class-specific delete called for a derived object
{
free = x->next;
return x;
return ::operator new( n );
}
else
{
if(last == items_per_page)
{
// "Listen to me carefully: there is no memory leak"
// -- Scott Meyers, Eff C++ 2nd Ed Item 10
page = ::new block[items_per_page];
last = 0;
}
return &page[last++];
return alloc();
}
}
static inline void * alloc(std::size_t n)
static void dealloc( void* p )
{
if(n != size) // class-specific new called for a derived object
if( p != 0 ) // 18.4.1.1/13
{
return ::operator new(n);
std::allocator<T>().deallocate( static_cast<T*>( p ), 1 );
}
}
static void dealloc( void* p, std::size_t n )
{
if( n != sizeof(T) ) // class-specific delete called for a derived object
{
::operator delete( p );
}
else
{
#ifdef BOOST_HAS_THREADS
lightweight_mutex::scoped_lock lock( mutex() );
#endif
if(block * x = free)
{
free = x->next;
return x;
}
else
{
if(last == items_per_page)
{
page = ::new block[items_per_page];
last = 0;
}
return &page[last++];
}
dealloc( p );
}
}
static inline void dealloc(void * pv)
{
if(pv != 0) // 18.4.1.1/13
{
#ifdef BOOST_HAS_THREADS
lightweight_mutex::scoped_lock lock( mutex() );
#endif
block * pb = static_cast<block *>(pv);
pb->next = free;
free = pb;
}
}
static inline void dealloc(void * pv, std::size_t n)
{
if(n != size) // class-specific delete called for a derived object
{
::operator delete(pv);
}
else if(pv != 0) // 18.4.1.1/13
{
#ifdef BOOST_HAS_THREADS
lightweight_mutex::scoped_lock lock( mutex() );
#endif
block * pb = static_cast<block *>(pv);
pb->next = free;
free = pb;
}
}
};
#ifdef BOOST_HAS_THREADS
template<unsigned size, unsigned align_>
lightweight_mutex * allocator_impl<size, align_>::mutex_init = &allocator_impl<size, align_>::mutex();
#endif
template<unsigned size, unsigned align_>
freeblock<size, align_> * allocator_impl<size, align_>::free = 0;
template<unsigned size, unsigned align_>
freeblock<size, align_> * allocator_impl<size, align_>::page = 0;
template<unsigned size, unsigned align_>
unsigned allocator_impl<size, align_>::last = allocator_impl<size, align_>::items_per_page;
template<class T>
struct quick_allocator: public allocator_impl< sizeof(T), std::alignment_of<T>::value >
{
};
} // namespace detail
} // namespace boost
#endif // #ifndef BOOST_SMART_PTR_DETAIL_QUICK_ALLOCATOR_HPP_INCLUDED

View File

@@ -429,3 +429,6 @@ run sp_type_with_alignment_test.cpp ;
run sp_ostream_test.cpp ;
run ip_ostream_test.cpp ;
run lsp_ostream_test.cpp ;
run shared_ptr_alloc_test.cpp ;
run quick_allocator_test.cpp ;

View File

@@ -0,0 +1,99 @@
// Copyright 2025 Peter Dimov
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt
#include <boost/smart_ptr/detail/quick_allocator.hpp>
#include <boost/core/lightweight_test.hpp>
#include <cstring>
// The interface of quick_allocator has never been documented,
// but in can be inferred from the source code to be
//
// template<class T> struct quick_allocator
// {
// // allocate memory for an object of type T
// static void* alloc();
//
// // deallocate the memory returned from alloc()
// // if p == 0, no effect
// static void dealloc( void* p );
//
// // if n == sizeof(T), returns alloc()
// // otherwise, allocates a memory block of size n
// static void* alloc( std::size_t n );
//
// // deallocate the memory returned from alloc( n )
// // if p == 0, no effect
// static void dealloc( void* p, std::size_t n );
// };
struct X
{
int data;
};
struct Y: public X
{
int data2;
};
int main()
{
using boost::detail::quick_allocator;
void* p = quick_allocator<Y>::alloc();
std::memset( p, 0xAA, sizeof(Y) );
{
void* p1 = quick_allocator<X>::alloc();
std::memset( p1, 0xCC, sizeof(X) );
void* p2 = quick_allocator<X>::alloc();
std::memset( p2, 0xDD, sizeof(X) );
BOOST_TEST_NE( p1, p2 );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p1 ), 0xCC );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p2 ), 0xDD );
quick_allocator<X>::dealloc( 0 );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p1 ), 0xCC );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p2 ), 0xDD );
quick_allocator<X>::dealloc( p1 );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p2 ), 0xDD );
quick_allocator<X>::dealloc( p2 );
}
{
void* p1 = quick_allocator<X>::alloc( sizeof(X) );
std::memset( p1, 0xCC, sizeof(X) );
void* p2 = quick_allocator<X>::alloc( sizeof(Y) );
std::memset( p2, 0xDD, sizeof(Y) );
BOOST_TEST_NE( p1, p2 );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p1 ), 0xCC );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p2 ), 0xDD );
quick_allocator<X>::dealloc( 0, sizeof(X) );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p1 ), 0xCC );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p2 ), 0xDD );
quick_allocator<X>::dealloc( p1, sizeof(X) );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p2 ), 0xDD );
quick_allocator<X>::dealloc( 0, sizeof(Y) );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p2 ), 0xDD );
quick_allocator<X>::dealloc( p2, sizeof(Y) );
}
BOOST_TEST_EQ( *static_cast<unsigned char*>( p ), 0xAA );
quick_allocator<Y>::dealloc( 0 );
BOOST_TEST_EQ( *static_cast<unsigned char*>( p ), 0xAA );
quick_allocator<Y>::dealloc( p );
return boost::report_errors();
}

View File

@@ -52,7 +52,7 @@ public:
void * operator new(std::size_t)
{
return std::allocator<X>().allocate(1, static_cast<X*>(0));
return std::allocator<X>().allocate(1);
}
void operator delete(void * p)