Avoid multi-processor bus contention on increment/decrement-and- test of the reference count in the empty-string object...

2003-06-13  Nathan C. Myers <ncm-nospam@cantrip.org>

	Avoid multi-processor bus contention on increment/decrement-and-
	test of the reference count in the empty-string object, by comparing
        addresses first, and never touching the reference count of the empty-
        string object.
	* include/bits/basic_string.h:
	(_S_empty_rep_storage): Move into basic_string<>::_Rep for use by its
	members.
	(_Rep::_S_empty_rep()): New accessor.
	(_Rep::_M_length, _Rep::_M_capacity, _Rep::_M_references): Move to
	a base class _Rep_base.
	(_Rep::_M_dispose, _Rep::_M_refcopy): Check for the empty string.
	(basic_string()): Change to use _M_refdata() in place of _M_refcopy(),
	since no longer must increment its refcount.
	* include/bits/basic_string.tcc:
	(_Rep::_M_destroy, _M_leak_hard): Check for the empty string and
        return immediately.  The former might be unnecessary.  The latter
        prevents begin() and end() from cloning it unnecessarily.
	(_S_construct(_InIterator, _InIterator, const _Alloc&,
	input_iterator_tag), _S_construct(_InIterator, _InIterator,
	const _Alloc&, forward_iterator_tag), _S_construct(size_type, _CharT,
	const _Alloc&)): Change to use _M_refdata() in place of _M_refcopy().
	(_M_mutate): Check for the empty string and treat it as shared.
        This is necessary here because _M_mutate is sometimes called with
        all-zero arguments; in all other uses of _M_is_shared, the test comes
        out right anyhow.

From-SVN: r67912
This commit is contained in:
Nathan C. Myers 2003-06-13 20:59:42 +00:00 committed by Paolo Carlini
parent 9f629a2191
commit ca566e4c07
3 changed files with 91 additions and 41 deletions

View File

@ -1,3 +1,31 @@
2003-06-13 Nathan C. Myers <ncm-nospam@cantrip.org>
Avoid multi-processor bus contention on increment/decrement-and-
test of the reference count in the empty-string object, by comparing
addresses first, and never touching the reference count of the empty-
string object.
* include/bits/basic_string.h:
(_S_empty_rep_storage): Move into basic_string<>::_Rep for use by its
members.
(_Rep::_S_empty_rep()): New accessor.
(_Rep::_M_length, _Rep::_M_capacity, _Rep::_M_references): Move to
a base class _Rep_base.
(_Rep::_M_dispose, _Rep::_M_refcopy): Check for the empty string.
(basic_string()): Change to use _M_refdata() in place of _M_refcopy(),
since no longer must increment its refcount.
* include/bits/basic_string.tcc:
(_Rep::_M_destroy, _M_leak_hard): Check for the empty string and
return immediately. The former might be unnecessary. The latter
prevents begin() and end() from cloning it unnecessarily.
(_S_construct(_InIterator, _InIterator, const _Alloc&,
input_iterator_tag), _S_construct(_InIterator, _InIterator,
const _Alloc&, forward_iterator_tag), _S_construct(size_type, _CharT,
const _Alloc&)): Change to use _M_refdata() in place of _M_refcopy().
(_M_mutate): Check for the empty string and treat it as shared.
This is necessary here because _M_mutate is sometimes called with
all-zero arguments; in all other uses of _M_is_shared, the test comes
out right anyhow.
2003-06-12 Benjamin Kosnik <bkoz@redhat.com> 2003-06-12 Benjamin Kosnik <bkoz@redhat.com>
* src/allocator-inst.cc: Explicitly instantiate. * src/allocator-inst.cc: Explicitly instantiate.

View File

@ -1,6 +1,6 @@
// Components for manipulating sequences of characters -*- C++ -*- // Components for manipulating sequences of characters -*- C++ -*-
// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 // Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003
// Free Software Foundation, Inc. // Free Software Foundation, Inc.
// //
// This file is part of the GNU ISO C++ Library. This library is free // This file is part of the GNU ISO C++ Library. This library is free
@ -140,7 +140,15 @@ namespace std
// 4. All fields==0 is an empty string, given the extra storage // 4. All fields==0 is an empty string, given the extra storage
// beyond-the-end for a null terminator; thus, the shared // beyond-the-end for a null terminator; thus, the shared
// empty string representation needs no constructor. // empty string representation needs no constructor.
struct _Rep
struct _Rep_base
{
size_type _M_length;
size_type _M_capacity;
_Atomic_word _M_references;
};
struct _Rep : _Rep_base
{ {
// Types: // Types:
typedef typename _Alloc::template rebind<char>::other _Raw_bytes_alloc; typedef typename _Alloc::template rebind<char>::other _Raw_bytes_alloc;
@ -157,29 +165,33 @@ namespace std
// npos = sizeof(_Rep) + (m * sizeof(_CharT)) + sizeof(_CharT) // npos = sizeof(_Rep) + (m * sizeof(_CharT)) + sizeof(_CharT)
// Solving for m: // Solving for m:
// m = ((npos - sizeof(_Rep))/sizeof(CharT)) - 1 // m = ((npos - sizeof(_Rep))/sizeof(CharT)) - 1
// In addition, this implementation quarters this ammount. // In addition, this implementation quarters this amount.
static const size_type _S_max_size; static const size_type _S_max_size;
static const _CharT _S_terminal; static const _CharT _S_terminal;
size_type _M_length; // The following storage is init'd to 0 by the linker, resulting
size_type _M_capacity; // (carefully) in an empty string with one reference.
_Atomic_word _M_references; static size_type _S_empty_rep_storage[];
static _Rep&
_S_empty_rep()
{ return *reinterpret_cast<_Rep*>(&_S_empty_rep_storage); }
bool bool
_M_is_leaked() const _M_is_leaked() const
{ return _M_references < 0; } { return this->_M_references < 0; }
bool bool
_M_is_shared() const _M_is_shared() const
{ return _M_references > 0; } { return this->_M_references > 0; }
void void
_M_set_leaked() _M_set_leaked()
{ _M_references = -1; } { this->_M_references = -1; }
void void
_M_set_sharable() _M_set_sharable()
{ _M_references = 0; } { this->_M_references = 0; }
_CharT* _CharT*
_M_refdata() throw() _M_refdata() throw()
@ -203,8 +215,9 @@ namespace std
void void
_M_dispose(const _Alloc& __a) _M_dispose(const _Alloc& __a)
{ {
if (__exchange_and_add(&_M_references, -1) <= 0) if (__builtin_expect(this != &_S_empty_rep(), false))
_M_destroy(__a); if (__exchange_and_add(&this->_M_references, -1) <= 0)
_M_destroy(__a);
} // XXX MT } // XXX MT
void void
@ -213,7 +226,8 @@ namespace std
_CharT* _CharT*
_M_refcopy() throw() _M_refcopy() throw()
{ {
__atomic_add(&_M_references, 1); if (__builtin_expect(this != &_S_empty_rep(), false))
__atomic_add(&this->_M_references, 1);
return _M_refdata(); return _M_refdata();
} // XXX MT } // XXX MT
@ -240,10 +254,6 @@ namespace std
// Data Members (private): // Data Members (private):
mutable _Alloc_hider _M_dataplus; mutable _Alloc_hider _M_dataplus;
// The following storage is init'd to 0 by the linker, resulting
// (carefully) in an empty string with one reference.
static size_type _S_empty_rep_storage[(sizeof(_Rep) + sizeof(_CharT) + sizeof(size_type) - 1)/sizeof(size_type)];
_CharT* _CharT*
_M_data() const _M_data() const
{ return _M_dataplus._M_p; } { return _M_dataplus._M_p; }
@ -322,7 +332,7 @@ namespace std
static _Rep& static _Rep&
_S_empty_rep() _S_empty_rep()
{ return *reinterpret_cast<_Rep*>(&_S_empty_rep_storage); } { return _Rep::_S_empty_rep(); }
public: public:
// Construct/copy/destroy: // Construct/copy/destroy:
@ -859,7 +869,7 @@ namespace std
template<typename _CharT, typename _Traits, typename _Alloc> template<typename _CharT, typename _Traits, typename _Alloc>
inline basic_string<_CharT, _Traits, _Alloc>:: inline basic_string<_CharT, _Traits, _Alloc>::
basic_string() basic_string()
: _M_dataplus(_S_empty_rep()._M_refcopy(), _Alloc()) { } : _M_dataplus(_S_empty_rep()._M_refdata(), _Alloc()) { }
// operator+ // operator+
template<typename _CharT, typename _Traits, typename _Alloc> template<typename _CharT, typename _Traits, typename _Alloc>

View File

@ -48,7 +48,7 @@ namespace std
template<typename _CharT, typename _Traits, typename _Alloc> template<typename _CharT, typename _Traits, typename _Alloc>
const typename basic_string<_CharT, _Traits, _Alloc>::size_type const typename basic_string<_CharT, _Traits, _Alloc>::size_type
basic_string<_CharT, _Traits, _Alloc>:: basic_string<_CharT, _Traits, _Alloc>::
_Rep::_S_max_size = (((npos - sizeof(_Rep))/sizeof(_CharT)) - 1) / 4; _Rep::_S_max_size = (((npos - sizeof(_Rep_base))/sizeof(_CharT)) - 1) / 4;
template<typename _CharT, typename _Traits, typename _Alloc> template<typename _CharT, typename _Traits, typename _Alloc>
const _CharT const _CharT
@ -63,8 +63,9 @@ namespace std
// at static init time (before static ctors are run). // at static init time (before static ctors are run).
template<typename _CharT, typename _Traits, typename _Alloc> template<typename _CharT, typename _Traits, typename _Alloc>
typename basic_string<_CharT, _Traits, _Alloc>::size_type typename basic_string<_CharT, _Traits, _Alloc>::size_type
basic_string<_CharT, _Traits, _Alloc>::_S_empty_rep_storage[ basic_string<_CharT, _Traits, _Alloc>::_Rep::_S_empty_rep_storage[
(sizeof(_Rep) + sizeof(_CharT) + sizeof(size_type) - 1)/sizeof(size_type)]; (sizeof(_Rep_base) + sizeof(_CharT) + sizeof(size_type) - 1) /
sizeof(size_type)];
// NB: This is the special case for Input Iterators, used in // NB: This is the special case for Input Iterators, used in
// istreambuf_iterators, etc. // istreambuf_iterators, etc.
@ -78,7 +79,7 @@ namespace std
input_iterator_tag) input_iterator_tag)
{ {
if (__beg == __end && __a == _Alloc()) if (__beg == __end && __a == _Alloc())
return _S_empty_rep()._M_refcopy(); return _S_empty_rep()._M_refdata();
// Avoid reallocation for common case. // Avoid reallocation for common case.
_CharT __buf[100]; _CharT __buf[100];
size_type __i = 0; size_type __i = 0;
@ -138,7 +139,7 @@ namespace std
forward_iterator_tag) forward_iterator_tag)
{ {
if (__beg == __end && __a == _Alloc()) if (__beg == __end && __a == _Alloc())
return _S_empty_rep()._M_refcopy(); return _S_empty_rep()._M_refdata();
// NB: Not required, but considered best practice. // NB: Not required, but considered best practice.
if (__builtin_expect(__beg == _InIterator(), 0)) if (__builtin_expect(__beg == _InIterator(), 0))
@ -167,7 +168,7 @@ namespace std
_S_construct(size_type __n, _CharT __c, const _Alloc& __a) _S_construct(size_type __n, _CharT __c, const _Alloc& __a)
{ {
if (__n == 0 && __a == _Alloc()) if (__n == 0 && __a == _Alloc())
return _S_empty_rep()._M_refcopy(); return _S_empty_rep()._M_refdata();
// Check for out_of_range and length_error exceptions. // Check for out_of_range and length_error exceptions.
_Rep* __r = _Rep::_S_create(__n, __a); _Rep* __r = _Rep::_S_create(__n, __a);
@ -242,7 +243,8 @@ namespace std
template<typename _CharT, typename _Traits, typename _Alloc> template<typename _CharT, typename _Traits, typename _Alloc>
basic_string<_CharT, _Traits, _Alloc>& basic_string<_CharT, _Traits, _Alloc>&
basic_string<_CharT, _Traits, _Alloc>::assign(const basic_string& __str) basic_string<_CharT, _Traits, _Alloc>::
assign(const basic_string& __str)
{ {
if (_M_rep() != __str._M_rep()) if (_M_rep() != __str._M_rep())
{ {
@ -371,7 +373,10 @@ namespace std
basic_string<_CharT, _Traits, _Alloc>::_Rep:: basic_string<_CharT, _Traits, _Alloc>::_Rep::
_M_destroy(const _Alloc& __a) throw () _M_destroy(const _Alloc& __a) throw ()
{ {
const size_type __size = sizeof(_Rep) + (_M_capacity + 1) * sizeof(_CharT); if (this == &_S_empty_rep())
return;
const size_type __size = sizeof(_Rep_base) +
(this->_M_capacity + 1) * sizeof(_CharT);
_Raw_bytes_alloc(__a).deallocate(reinterpret_cast<char*>(this), __size); _Raw_bytes_alloc(__a).deallocate(reinterpret_cast<char*>(this), __size);
} }
@ -379,6 +384,8 @@ namespace std
void void
basic_string<_CharT, _Traits, _Alloc>::_M_leak_hard() basic_string<_CharT, _Traits, _Alloc>::_M_leak_hard()
{ {
if (_M_rep() == &_S_empty_rep())
return;
if (_M_rep()->_M_is_shared()) if (_M_rep()->_M_is_shared())
_M_mutate(0, 0, 0); _M_mutate(0, 0, 0);
_M_rep()->_M_set_leaked(); _M_rep()->_M_set_leaked();
@ -400,7 +407,8 @@ namespace std
const _CharT* __src = _M_data() + __pos + __len1; const _CharT* __src = _M_data() + __pos + __len1;
const size_type __how_much = __old_size - __pos - __len1; const size_type __how_much = __old_size - __pos - __len1;
if (_M_rep()->_M_is_shared() || __new_size > capacity()) if (_M_rep() == &_S_empty_rep()
|| _M_rep()->_M_is_shared() || __new_size > capacity())
{ {
// Must reallocate. // Must reallocate.
allocator_type __a = get_allocator(); allocator_type __a = get_allocator();
@ -434,7 +442,7 @@ namespace std
} }
_M_rep()->_M_dispose(__a); _M_rep()->_M_dispose(__a);
_M_data(__r->_M_refdata()); _M_data(__r->_M_refdata());
} }
else if (__how_much && __len1 != __len2) else if (__how_much && __len1 != __len2)
{ {
// Work in-place // Work in-place
@ -443,7 +451,7 @@ namespace std
_M_rep()->_M_set_sharable(); _M_rep()->_M_set_sharable();
_M_rep()->_M_length = __new_size; _M_rep()->_M_length = __new_size;
_M_data()[__new_size] = _Rep::_S_terminal; // grrr. (per 21.3.4) _M_data()[__new_size] = _Rep::_S_terminal; // grrr. (per 21.3.4)
// You cannot leave those LWG people alone for a second. // You cannot leave those LWG people alone for a second.
} }
template<typename _CharT, typename _Traits, typename _Alloc> template<typename _CharT, typename _Traits, typename _Alloc>
@ -505,7 +513,7 @@ namespace std
// NB: Need an array of char_type[__capacity], plus a // NB: Need an array of char_type[__capacity], plus a
// terminating null char_type() element, plus enough for the // terminating null char_type() element, plus enough for the
// _Rep data structure. Whew. Seemingly so needy, yet so elemental. // _Rep data structure. Whew. Seemingly so needy, yet so elemental.
size_t __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep); size_t __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep_base);
// The standard places no restriction on allocating more memory // The standard places no restriction on allocating more memory
// than is strictly needed within this layer at the moment or as // than is strictly needed within this layer at the moment or as
@ -538,7 +546,7 @@ namespace std
(__pagesize - ((__size + __malloc_header_size) % __pagesize)) (__pagesize - ((__size + __malloc_header_size) % __pagesize))
% __pagesize; % __pagesize;
__capacity += __extra / sizeof(_CharT); __capacity += __extra / sizeof(_CharT);
__size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep); __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep_base);
} }
else if (__size > __subpagesize) else if (__size > __subpagesize)
{ {
@ -546,7 +554,7 @@ namespace std
(__subpagesize - ((__size + __malloc_header_size) % __subpagesize)) (__subpagesize - ((__size + __malloc_header_size) % __subpagesize))
% __subpagesize; % __subpagesize;
__capacity += __extra / sizeof(_CharT); __capacity += __extra / sizeof(_CharT);
__size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep); __size = (__capacity + 1) * sizeof(_CharT) + sizeof(_Rep_base);
} }
// NB: Might throw, but no worries about a leak, mate: _Rep() // NB: Might throw, but no worries about a leak, mate: _Rep()
@ -565,33 +573,37 @@ namespace std
_M_clone(const _Alloc& __alloc, size_type __res) _M_clone(const _Alloc& __alloc, size_type __res)
{ {
// Requested capacity of the clone. // Requested capacity of the clone.
const size_type __requested_cap = _M_length + __res; const size_type __requested_cap = this->_M_length + __res;
// See above (_S_create) for the meaning and value of these constants. // See above (_S_create) for the meaning and value of these constants.
const size_type __pagesize = 4096; const size_type __pagesize = 4096;
const size_type __malloc_header_size = 4 * sizeof (void*); const size_type __malloc_header_size = 4 * sizeof (void*);
// The biggest string which fits in a memory page. // The biggest string which fits in a memory page.
const size_type __page_capacity = const size_type __page_capacity =
(__pagesize - __malloc_header_size - sizeof(_Rep) - sizeof(_CharT)) (__pagesize - __malloc_header_size - sizeof(_Rep_base) - sizeof(_CharT))
/ sizeof(_CharT); / sizeof(_CharT);
_Rep* __r; _Rep* __r;
if (__requested_cap > _M_capacity && __requested_cap > __page_capacity) if (__requested_cap > this->_M_capacity
&& __requested_cap > __page_capacity)
// Growing exponentially. // Growing exponentially.
__r = _Rep::_S_create(__requested_cap > 2*_M_capacity ? __r = _Rep::_S_create(__requested_cap > 2*this->_M_capacity ?
__requested_cap : 2*_M_capacity, __alloc); __requested_cap : 2*this->_M_capacity, __alloc);
else else
__r = _Rep::_S_create(__requested_cap, __alloc); __r = _Rep::_S_create(__requested_cap, __alloc);
if (_M_length) if (this->_M_length)
{ {
try try
{ traits_type::copy(__r->_M_refdata(), _M_refdata(), _M_length); } {
traits_type::copy(__r->_M_refdata(), _M_refdata(),
this->_M_length);
}
catch(...) catch(...)
{ {
__r->_M_destroy(__alloc); __r->_M_destroy(__alloc);
__throw_exception_again; __throw_exception_again;
} }
} }
__r->_M_length = _M_length; __r->_M_length = this->_M_length;
return __r->_M_refdata(); return __r->_M_refdata();
} }