This is the mail archive of the libstdc++@gcc.gnu.org mailing list for the libstdc++ project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]