This is the mail archive of the
libstdc++@gcc.gnu.org
mailing list for the libstdc++ project.
[Patch] Convert pool_allocator
- From: Paolo Carlini <pcarlini at suse dot de>
- To: libstdc++ <libstdc++ at gcc dot gnu dot org>
- Cc: Benjamin Kosnik <bkoz at redhat dot com>
- Date: Sun, 21 Mar 2004 18:24:52 +0100
- Subject: [Patch] Convert pool_allocator
Hi all, hi Benjamin,
eventually I went ahead and converted __pool_alloc according to the
original plan:
http://gcc.gnu.org/ml/libstdc++/2003-12/msg00270.html
Basically, enabling it at configure time leads to a behavior which is
(supposed to be) identical to that of 3.3 default allocator.
Of course, we can consider tweaking it further for MT/not, but I think
its real virtue is stability in providing a baseline form of "pooling"
against which more sophisticated ideas will be compared.
Regtested x86/x86_64/ia64 linux, also checked that on ia64 no runtime
alignement warnings are issued.
I will commit to mainline tomorrow morning, if nobody objects.
Paolo.
////////////
2004-03-22 Paolo Carlini <pcarlini@suse.de>
* acinclude.m4 (GLIBCXX_ENABLE_ALLOCATOR): Add pool_allocator.
* configure: Regenerate.
* config/allocator/pool_allocator_base.h: New.
* include/ext/pool_allocator.h: Convert to a standard-conforming
allocator.
* src/allocator.cc: Tweak instantiations.
diff -prN libstdc++-v3-orig/acinclude.m4 libstdc++-v3/acinclude.m4
*** libstdc++-v3-orig/acinclude.m4 Fri Mar 19 12:34:20 2004
--- libstdc++-v3/acinclude.m4 Sun Mar 21 12:18:31 2004
*************** AC_DEFUN([GLIBCXX_ENABLE_ALLOCATOR], [
*** 1183,1189 ****
AC_MSG_CHECKING([for std::allocator base class to use])
GLIBCXX_ENABLE(libstdcxx-allocator,auto,[=KIND],
[use KIND for target std::allocator base],
! [permit new|malloc|mt|bitmap|yes|no|auto])
# If they didn't use this option switch, or if they specified --enable
# with no specific model, we'll have to look for one. If they
# specified --disable (???), do likewise.
--- 1183,1189 ----
AC_MSG_CHECKING([for std::allocator base class to use])
GLIBCXX_ENABLE(libstdcxx-allocator,auto,[=KIND],
[use KIND for target std::allocator base],
! [permit new|malloc|mt|bitmap|pool|yes|no|auto])
# If they didn't use this option switch, or if they specified --enable
# with no specific model, we'll have to look for one. If they
# specified --disable (???), do likewise.
*************** AC_DEFUN([GLIBCXX_ENABLE_ALLOCATOR], [
*** 1224,1229 ****
--- 1224,1233 ----
ALLOCATOR_H=config/allocator/new_allocator_base.h
ALLOCATOR_NAME=__gnu_cxx::new_allocator
;;
+ pool)
+ ALLOCATOR_H=config/allocator/pool_allocator_base.h
+ ALLOCATOR_NAME=__gnu_cxx::__pool_alloc
+ ;;
esac
AC_SUBST(ALLOCATOR_H)
diff -prN libstdc++-v3-orig/config/allocator/pool_allocator_base.h libstdc++-v3/config/allocator/pool_allocator_base.h
*** libstdc++-v3-orig/config/allocator/pool_allocator_base.h Thu Jan 1 01:00:00 1970
--- libstdc++-v3/config/allocator/pool_allocator_base.h Sun Mar 21 12:15:55 2004
***************
*** 0 ****
--- 1,37 ----
+ // Base to std::allocator -*- C++ -*-
+
+ // Copyright (C) 2004 Free Software Foundation, Inc.
+ //
+ // This file is part of the GNU ISO C++ Library. This library is free
+ // software; you can redistribute it and/or modify it under the
+ // terms of the GNU General Public License as published by the
+ // Free Software Foundation; either version 2, or (at your option)
+ // any later version.
+
+ // This library is distributed in the hope that it will be useful,
+ // but WITHOUT ANY WARRANTY; without even the implied warranty of
+ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ // GNU General Public License for more details.
+
+ // You should have received a copy of the GNU General Public License along
+ // with this library; see the file COPYING. If not, write to the Free
+ // Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ // USA.
+
+ // As a special exception, you may use this file as part of a free software
+ // library without restriction. Specifically, if other files instantiate
+ // templates or use macros or inline functions from this file, or you compile
+ // this file and link it with other files to produce an executable, this
+ // file does not by itself cause the resulting executable to be covered by
+ // the GNU General Public License. This exception does not however
+ // invalidate any other reasons why the executable file might be covered by
+ // the GNU General Public License.
+
+ #ifndef _CXX_ALLOCATOR_H
+ #define _CXX_ALLOCATOR_H 1
+
+ // Define new_allocator as the base class to std::allocator.
+ #include <ext/pool_allocator.h>
+ #define ___glibcxx_base_allocator __gnu_cxx::__pool_alloc
+
+ #endif
diff -prN libstdc++-v3-orig/include/ext/pool_allocator.h libstdc++-v3/include/ext/pool_allocator.h
*** libstdc++-v3-orig/include/ext/pool_allocator.h Tue Feb 10 01:43:04 2004
--- libstdc++-v3/include/ext/pool_allocator.h Sun Mar 21 17:49:15 2004
*************** namespace __gnu_cxx
*** 59,67 ****
/**
* @if maint
! * Default node allocator. "SGI" style. Uses various allocators to
! * fulfill underlying requests (and makes as few requests as possible
! * when in default high-speed pool mode).
*
* Important implementation properties:
* 0. If globally mandated, then allocate objects from new
--- 59,66 ----
/**
* @if maint
! * Uses various allocators to fulfill underlying requests (and makes as
! * few requests as possible when in default high-speed pool mode).
*
* Important implementation properties:
* 0. If globally mandated, then allocate objects from new
*************** namespace __gnu_cxx
*** 72,96 ****
* information that we can return the object to the proper free list
* without permanently losing part of the object.
*
- * The first template parameter specifies whether more than one thread may
- * use this allocator. It is safe to allocate an object from one instance
- * of a default_alloc and deallocate it with another one. This effectively
- * transfers its ownership to the second one. This may have undesirable
- * effects on reference locality.
- *
- * The second parameter is unused and serves only to allow the
- * creation of multiple default_alloc instances. Note that
- * containers built on different allocator instances have different
- * types, limiting the utility of this approach. If you do not
- * wish to share the free lists with the main default_alloc
- * instance, instantiate this with a non-zero __inst.
- *
* @endif
* (See @link Allocators allocators info @endlink for more.)
*/
! template<bool __threads, int __inst>
class __pool_alloc
{
private:
enum {_S_align = 8};
enum {_S_max_bytes = 128};
--- 71,129 ----
* information that we can return the object to the proper free list
* without permanently losing part of the object.
*
* @endif
* (See @link Allocators allocators info @endlink for more.)
*/
! template<typename _Tp>
class __pool_alloc
{
+ public:
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef _Tp* pointer;
+ typedef const _Tp* const_pointer;
+ typedef _Tp& reference;
+ typedef const _Tp& const_reference;
+ typedef _Tp value_type;
+
+ template<typename _Tp1>
+ struct rebind
+ { typedef __pool_alloc<_Tp1> other; };
+
+ __pool_alloc() throw() { }
+
+ __pool_alloc(const __pool_alloc&) throw() { }
+
+ template<typename _Tp1>
+ __pool_alloc(const __pool_alloc<_Tp1>&) throw() { }
+
+ ~__pool_alloc() throw() { }
+
+ pointer
+ address(reference __x) const { return &__x; }
+
+ const_pointer
+ address(const_reference __x) const { return &__x; }
+
+ size_type
+ max_size() const throw()
+ { return size_t(-1) / sizeof(_Tp); }
+
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // 402. wrong new expression in [some_] allocator::construct
+ void
+ construct(pointer __p, const _Tp& __val)
+ { ::new(__p) _Tp(__val); }
+
+ void
+ destroy(pointer __p) { __p->~_Tp(); }
+
+ pointer
+ allocate(size_type __n, const void* = 0);
+
+ void
+ deallocate(pointer __p, size_type __n);
+
private:
enum {_S_align = 8};
enum {_S_max_bytes = 128};
*************** namespace __gnu_cxx
*** 134,173 ****
// test whether threads are in use.
struct _Lock
{
! _Lock() { if (__threads) _S_lock._M_acquire_lock(); }
! ~_Lock() { if (__threads) _S_lock._M_release_lock(); }
} __attribute__ ((__unused__));
friend struct _Lock;
-
- public:
- // __n must be > 0
- static void*
- allocate(size_t __n);
-
- // __p may not be 0
- static void
- deallocate(void* __p, size_t __n);
};
! template<bool __threads, int __inst>
inline bool
! operator==(const __pool_alloc<__threads,__inst>&,
! const __pool_alloc<__threads,__inst>&)
{ return true; }
! template<bool __threads, int __inst>
inline bool
! operator!=(const __pool_alloc<__threads,__inst>&,
! const __pool_alloc<__threads,__inst>&)
{ return false; }
-
// Allocate memory in large chunks in order to avoid fragmenting the
// heap too much. Assume that __n is properly aligned. We hold
// the allocation lock.
! template<bool __threads, int __inst>
char*
! __pool_alloc<__threads, __inst>::_S_chunk_alloc(size_t __n, int& __nobjs)
{
char* __result;
size_t __total_bytes = __n * __nobjs;
--- 167,194 ----
// test whether threads are in use.
struct _Lock
{
! _Lock() { _S_lock._M_acquire_lock(); }
! ~_Lock() { _S_lock._M_release_lock(); }
} __attribute__ ((__unused__));
friend struct _Lock;
};
! template<typename _Tp>
inline bool
! operator==(const __pool_alloc<_Tp>&, const __pool_alloc<_Tp>&)
{ return true; }
! template<typename _Tp>
inline bool
! operator!=(const __pool_alloc<_Tp>&, const __pool_alloc<_Tp>&)
{ return false; }
// Allocate memory in large chunks in order to avoid fragmenting the
// heap too much. Assume that __n is properly aligned. We hold
// the allocation lock.
! template<typename _Tp>
char*
! __pool_alloc<_Tp>::_S_chunk_alloc(size_t __n, int& __nobjs)
{
char* __result;
size_t __total_bytes = __n * __nobjs;
*************** namespace __gnu_cxx
*** 238,246 ****
// Returns an object of size __n, and optionally adds to "size
// __n"'s free list. We assume that __n is properly aligned. We
// hold the allocation lock.
! template<bool __threads, int __inst>
void*
! __pool_alloc<__threads, __inst>::_S_refill(size_t __n)
{
int __nobjs = 20;
char* __chunk = _S_chunk_alloc(__n, __nobjs);
--- 259,267 ----
// Returns an object of size __n, and optionally adds to "size
// __n"'s free list. We assume that __n is properly aligned. We
// hold the allocation lock.
! template<typename _Tp>
void*
! __pool_alloc<_Tp>::_S_refill(size_t __n)
{
int __nobjs = 20;
char* __chunk = _S_chunk_alloc(__n, __nobjs);
*************** namespace __gnu_cxx
*** 272,363 ****
return __result;
}
! template<bool __threads, int __inst>
! void*
! __pool_alloc<__threads, __inst>::allocate(size_t __n)
{
! void* __ret = 0;
!
! // If there is a race through here, assume answer from getenv
! // will resolve in same direction. Inspired by techniques
! // to efficiently support threading found in basic_string.h.
! if (_S_force_new == 0)
! {
! if (getenv("GLIBCXX_FORCE_NEW"))
! __atomic_add(&_S_force_new, 1);
! else
! __atomic_add(&_S_force_new, -1);
! }
!
! if ((__n > (size_t) _S_max_bytes) || (_S_force_new > 0))
! __ret = ::operator new(__n);
! else
{
! _Obj* volatile* __free_list = _S_free_list + _S_freelist_index(__n);
! // Acquire the lock here with a constructor call. This
! // ensures that it is released in exit or during stack
! // unwinding.
! _Lock __lock_instance;
! _Obj* __restrict__ __result = *__free_list;
! if (__builtin_expect(__result == 0, 0))
! __ret = _S_refill(_S_round_up(__n));
! else
{
! *__free_list = __result -> _M_free_list_link;
! __ret = __result;
}
! if (__builtin_expect(__ret == 0, 0))
__throw_bad_alloc();
}
return __ret;
}
! template<bool __threads, int __inst>
void
! __pool_alloc<__threads, __inst>::deallocate(void* __p, size_t __n)
{
! if ((__n > (size_t) _S_max_bytes) || (_S_force_new > 0))
! ::operator delete(__p);
! else
{
! _Obj* volatile* __free_list = _S_free_list + _S_freelist_index(__n);
! _Obj* __q = (_Obj*)__p;
!
! // Acquire the lock here with a constructor call. This
! // ensures that it is released in exit or during stack
! // unwinding.
! _Lock __lock_instance;
! __q -> _M_free_list_link = *__free_list;
! *__free_list = __q;
}
}
! template<bool __threads, int __inst>
! typename __pool_alloc<__threads, __inst>::_Obj* volatile
! __pool_alloc<__threads, __inst>::_S_free_list[_S_freelists];
! template<bool __threads, int __inst>
! char* __pool_alloc<__threads, __inst>::_S_start_free = 0;
! template<bool __threads, int __inst>
! char* __pool_alloc<__threads, __inst>::_S_end_free = 0;
! template<bool __threads, int __inst>
! size_t __pool_alloc<__threads, __inst>::_S_heap_size = 0;
! template<bool __threads, int __inst>
_STL_mutex_lock
! __pool_alloc<__threads, __inst>::_S_lock __STL_MUTEX_INITIALIZER;
! template<bool __threads, int __inst> _Atomic_word
! __pool_alloc<__threads, __inst>::_S_force_new = 0;
!
! // Inhibit implicit instantiations for required instantiations,
! // which are defined via explicit instantiations elsewhere.
! // NB: This syntax is a GNU extension.
! #if _GLIBCXX_EXTERN_TEMPLATE
! extern template class __pool_alloc<true, 0>;
! #endif
} // namespace __gnu_cxx
#endif
--- 293,391 ----
return __result;
}
! template<typename _Tp>
! _Tp*
! __pool_alloc<_Tp>::allocate(size_type __n, const void*)
{
! pointer __ret = 0;
! if (__n)
{
! if (__n <= max_size())
{
! const size_t __bytes = __n * sizeof(_Tp);
! // If there is a race through here, assume answer from getenv
! // will resolve in same direction. Inspired by techniques
! // to efficiently support threading found in basic_string.h.
! if (_S_force_new == 0)
! {
! if (getenv("GLIBCXX_FORCE_NEW"))
! __atomic_add(&_S_force_new, 1);
! else
! __atomic_add(&_S_force_new, -1);
! }
!
! if ((__bytes > (size_t) _S_max_bytes) || (_S_force_new > 0))
! __ret = static_cast<_Tp*>(::operator new(__bytes));
! else
! {
! _Obj* volatile* __free_list = (_S_free_list
! + _S_freelist_index(__bytes));
! // Acquire the lock here with a constructor call. This
! // ensures that it is released in exit or during stack
! // unwinding.
! _Lock __lock_instance;
! _Obj* __restrict__ __result = *__free_list;
! if (__builtin_expect(__result == 0, 0))
! __ret = static_cast<_Tp*>(_S_refill(_S_round_up(__bytes)));
! else
! {
! *__free_list = __result -> _M_free_list_link;
! __ret = reinterpret_cast<_Tp*>(__result);
! }
! if (__builtin_expect(__ret == 0, 0))
! __throw_bad_alloc();
! }
}
! else
__throw_bad_alloc();
}
return __ret;
}
! template<typename _Tp>
void
! __pool_alloc<_Tp>::deallocate(pointer __p, size_type __n)
{
! if (__n)
{
! const size_t __bytes = __n * sizeof(_Tp);
! if ((__bytes > (size_t) _S_max_bytes) || (_S_force_new > 0))
! ::operator delete(__p);
! else
! {
! _Obj* volatile* __free_list = (_S_free_list
! + _S_freelist_index(__bytes));
! _Obj* __q = (_Obj*)__p;
!
! // Acquire the lock here with a constructor call. This
! // ensures that it is released in exit or during stack
! // unwinding.
! _Lock __lock_instance;
! __q -> _M_free_list_link = *__free_list;
! *__free_list = __q;
! }
}
}
! template<typename _Tp>
! typename __pool_alloc<_Tp>::_Obj* volatile
! __pool_alloc<_Tp>::_S_free_list[_S_freelists];
! template<typename _Tp>
! char* __pool_alloc<_Tp>::_S_start_free = 0;
! template<typename _Tp>
! char* __pool_alloc<_Tp>::_S_end_free = 0;
! template<typename _Tp>
! size_t __pool_alloc<_Tp>::_S_heap_size = 0;
! template<typename _Tp>
_STL_mutex_lock
! __pool_alloc<_Tp>::_S_lock __STL_MUTEX_INITIALIZER;
! template<typename _Tp> _Atomic_word
! __pool_alloc<_Tp>::_S_force_new = 0;
} // namespace __gnu_cxx
#endif
diff -prN libstdc++-v3-orig/src/allocator.cc libstdc++-v3/src/allocator.cc
*** libstdc++-v3-orig/src/allocator.cc Thu Mar 4 19:10:58 2004
--- libstdc++-v3/src/allocator.cc Sun Mar 21 13:09:43 2004
*************** namespace __gnu_cxx
*** 44,48 ****
template class __mt_alloc<wchar_t>;
// Static members of __pool_alloc.
! template class __pool_alloc<true, 0>;
} // namespace __gnu_cxx
--- 44,49 ----
template class __mt_alloc<wchar_t>;
// Static members of __pool_alloc.
! template class __pool_alloc<char>;
! template class __pool_alloc<wchar_t>;
} // namespace __gnu_cxx