473,396 Members | 1,998 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,396 software developers and data experts.

questions about the STL sort()

STL sort() as an implementation of introsort, below is the code
snippet from the gnu stl.

template<typename _RandomAccessIterator, typename _Size>
void
__introsort_loop(_RandomAccessIterator __first,
_RandomAccessIterator __last,
_Size __depth_limit)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type
_ValueType;

while (__last - __first _S_threshold)
{
if (__depth_limit == 0)
{
std::partial_sort(__first, __last, __last);
return;
}
--__depth_limit;
_RandomAccessIterator __cut =
std::__unguarded_partition(__first, __last,
_ValueType(std::__median(*__first,
*(__first
+ (__last
- __first)
/ 2),
*(__last
- 1))));
std::__introsort_loop(__cut, __last, __depth_limit);
__last = __cut;
}
}

my question is : why not use two recursive calls to __introsort_loop,
it will have a clear view to present a divide-and-conquer algorithm.
Yes, it is a optimization,but tail recursion elimination is now a
common technique of today's cpp compiler.
another question is about the __final_insertion_sort() placed after
the call to __introsort_loop()
below is the code snippet.

template<typename _RandomAccessIterator>
void
__final_insertion_sort(_RandomAccessIterator __first,
_RandomAccessIterator __last)
{
if (__last - __first _S_threshold)
{
std::__insertion_sort(__first, __first + _S_threshold);
std::__unguarded_insertion_sort(__first + _S_threshold, __last);
}
else
std::__insertion_sort(__first, __last);
}

why not just call one __insertion_sort in the if-condition?
Jul 8 '08 #1
5 4882
Hello,

feverzsj wrote:
STL sort() as an implementation of introsort, below is the code
snippet from the gnu stl.
my question is : why not use two recursive calls to __introsort_loop,
it will have a clear view to present a divide-and-conquer algorithm.
Yes, it is a optimization,but tail recursion elimination is now a
common technique of today's cpp compiler.
I'm not sure tail recursion does apply here. Since the code is there,
you could try out what the compilers do. I'm pretty sure that saving
one recursive call saves enough, e.g. initializing the iterators for
every recursive instance, to justify doing it that way. Automatically
transforming non-tail recursion away, even partially, is a pretty
complicated operation with too many problematic cases. I would not
expect any compiler sacrificing lots of time in fruitless analysis in
that area.
>

another question is about the __final_insertion_sort() placed after
the call to __introsort_loop()
below is the code snippet.

template<typename _RandomAccessIterator>
void
__final_insertion_sort(_RandomAccessIterator __first,
_RandomAccessIterator __last)
{
if (__last - __first _S_threshold)
{
std::__insertion_sort(__first, __first + _S_threshold);
std::__unguarded_insertion_sort(__first + _S_threshold, __last);
}
else
std::__insertion_sort(__first, __last);
}

why not just call one __insertion_sort in the if-condition?
__unguarded_insertion_sort saves bounds-checking, therefore it needs
some elements less or equal in the area before first + _S_threshold. It
will always read at least at position __first + _S_threshold - 1, and
it will sort arbitrarily far before __first + _S_threshold. The big
question is, why there must be an element less then all in range
[__first + _S_threshold, __last) in the range [__first, __first +
_S_threshold). An that can only be found when looking at the places
calling this function.

I think this splitting should make an easily measurable difference when
sorting containers of small elements like integer.

Bernd Strieder

Jul 8 '08 #2
feverzsj wrote:
my question is : why not use two recursive calls to __introsort_loop,
it will have a clear view to present a divide-and-conquer algorithm.
Yes, it is a optimization,but tail recursion elimination is now a
common technique of today's cpp compiler.
What would be the advantage of not removing the second recursive call?
The only effect is that it will start relying on the tail recursion
optimization to exist and succeed, for no good reason.

The code itself is not even intended to be looked at.
Jul 8 '08 #3
On Jul 8, 10:32 am, feverzsj <fever...@hotmail.comwrote:
STL sort() as an implementation of introsort, below is the
code snippet from the gnu stl.
template<typename _RandomAccessIterator, typename _Size>
void
__introsort_loop(_RandomAccessIterator __first,
_RandomAccessIterator __last,
_Size __depth_limit)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type
_ValueType;
while (__last - __first _S_threshold)
{
if (__depth_limit == 0)
{
std::partial_sort(__first, __last, __last);
return;
}
--__depth_limit;
_RandomAccessIterator __cut =
std::__unguarded_partition(__first, __last,
_ValueType(std::__median(*__first,
*(__first
+ (__last
- __first)
/ 2),
*(__last
- 1))));
std::__introsort_loop(__cut, __last, __depth_limit);
__last = __cut;
}
}
my question is : why not use two recursive calls to __introsort_loop,
Probably because the code was originally a quick sort (without
the __depth_limit), and they didn't want it to core dump due to
stack overflow.
it will have a clear view to present a divide-and-conquer
algorithm. Yes, it is a optimization, but tail recursion
elimination is now a common technique of today's cpp compiler.
First, I'm not sure that it's that common. Compilers optimize
for real code, and tail recursion isn't that frequent in C++.
And second, I'm not sure that the compiler can do the
optimization; if _RandomAccessIterator has a non-trivial
destructor, it certainly can't.
another question is about the __final_insertion_sort() placed after
the call to __introsort_loop()
below is the code snippet.
template<typename _RandomAccessIterator>
void
__final_insertion_sort(_RandomAccessIterator __first,
_RandomAccessIterator __last)
{
if (__last - __first _S_threshold)
{
std::__insertion_sort(__first, __first + _S_threshold);
std::__unguarded_insertion_sort(__first + _S_threshold, __last);
}
else
std::__insertion_sort(__first, __last);
}
why not just call one __insertion_sort in the if-condition?
Probably optimization. Without seeing where this function is
called, and what the differences are between the two insertion
sorts, it's hard to say, but just guessing... this function is
called when the quick sort has partitionned the total range into
blocks of _S_threshold size, and all of the values in one block
are greater than the values in all previous blocks, and less
then the values in all following blocks. The standard insertion
sort will have to check for the bottom of the array in the inner
loop, something like (i and j are iterators):

for ( i = first + 1 ; i != last ; ++ i ) {
T tmp = *i ;
j = i ;
while ( j != first && *(j-1) tmp ) {
*j = *(j-1) ;
-- j ;
}
*j = tmp ;
}

If you're sorting anything but the first block after the
partitionning, you don't need the j != first test in the inner
loop.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jul 9 '08 #4
Hello,
Bernd Strieder wrote:
I'm not sure tail recursion does apply here. Since the code is there,
you could try out what the compilers do. I'm pretty sure that saving
one recursive call saves enough, e.g. initializing the iterators for
every recursive instance, to justify doing it that way. Automatically
transforming non-tail recursion away, even partially, is a pretty
complicated operation with too many problematic cases. I would not
expect any compiler sacrificing lots of time in fruitless analysis in
that area.
i see what u mean. STL is designed for both good portability and high
performance.
We can study a lot from it by comparing and rewriting.
If the __introsort_loop() was written like this:

__introsort_loop(/*...*/)
{
//...

if(__last - __first _S_threshold)
{
//....
}
std::__introsort_loop(__cut, __last, __depth_limit);
std::__introsort_loop(__first, __cut, __depth_limit);
}
Then, a tail recursion elimination could just simply replace the
params by the new one ,and goto the beginning of procedure.
The compiler may also identify only __last has changed , and just
replace the _last by __cut.
Finally ,we get the same effect as the GNU STL __introsort_loop does.

__introsort_loop(/*...*/)
{
//...

recu: if(__last - __first _S_threshold)
{
//....
}

std::__introsort_loop(__cut, __last, __depth_limit);
// tail recursion elimination could be just simply like this
__first=__first;
__last=__cut;
__depth_limit=__depth_limit;
goto recu;
}

__unguarded_insertion_sort saves bounds-checking, therefore it needs
some elements less or equal in the area before first + _S_threshold. It
will always read at least at position __first + _S_threshold - 1, and
it will sort arbitrarily far before __first + _S_threshold. The big
question is, why there must be an element less then all in range
[__first + _S_threshold, __last) in the range [__first, __first +
_S_threshold). An that can only be found when looking at the places
calling this function.

I think this splitting should make an easily measurable difference when
sorting containers of small elements like integer.
yeah ,that's a speed-up trick, but I have the same question as you
mentioned.
The place calling this function makes me doubt ,how could there must
be an element less then all in range [__first + _S_threshold, __last)
in the range [__first, __first + _S_threshold]

sort(_RandomAccessIterator __first, _RandomAccessIterator __last)
{

if (__first != __last)
{
std::__introsort_loop(__first, __last, __lg(__last - __first) * 2);
std::__final_insertion_sort(__first, __last); // the only palce it
calls
}
}

or the __introsort_loop() has assured such thing?

Furthermore ,the __unguarded_partition() uses a simple 2-way
partitioning with no bound check, becuz the median-of-three method has
assured so. I think a 3-way partitioning could be a better partition
routine for common use with a slight overhead
Jul 9 '08 #5
hello:

James Kanze wrote:
Probably because the code was originally a quick sort (without
the __depth_limit), and they didn't want it to core dump due to
stack overflow.
First, I'm not sure that it's that common. *Compilers optimize
for real code, and tail recursion isn't that frequent in C++.
And second, I'm not sure that the compiler can do the
optimization; if _RandomAccessIterator has a non-trivial
destructor, it certainly can't.
thx,you remind me of cpp's RAII feature, and i'm still on the road
from "c+" to "c++", a long way to pass through.

Probably optimization. *Without seeing where this function is
called, and what the differences are between the two insertion
sorts, it's hard to say, but just guessing... this function is
called when the quick sort has partitionned the total range into
blocks of _S_threshold size, and all of the values in one block
are greater than the values in all previous blocks, and less
then the values in all following blocks. *The standard insertion
sort will have to check for the bottom of the array in the inner
loop, something like (i and j are iterators):

* * for ( i = first + 1 ; i != last ; ++ i ) {
* * * * T tmp = *i ;
* * * * j = i ;
* * * * while ( j != first && *(j-1) tmp ) {
* * * * * * *j = *(j-1) ;
* * * * * * -- j ;
* * * * }
* * * * *j = tmp ;
* * }

If you're sorting anything but the first block after the
partitionning, you don't need the j != first test in the inner
loop.
below r the routines of __insertion_sort() and the
__unguarded_insertion_sort().
looks like __insertion_sort() is just a common implementation of
insertion sort,
and __unguarded_insertion_sort() is some kind insertion sort without
bound check
they'll be called right after the __introsort_loop(/*...*/).
__insertion_sort(_RandomAccessIterator __first,
_RandomAccessIterator __last)
{
if (__first == __last)
return;

for (_RandomAccessIterator __i = __first + 1; __i != __last; ++__i)
{
typename iterator_traits<_RandomAccessIterator>::value_type
__val = *__i;
if (__val < *__first)
{
std::copy_backward(__first, __i, __i + 1);
*__first = __val;
}
else
std::__unguarded_linear_insert(__i, __val);
}
}

__unguarded_insertion_sort(_RandomAccessIterator __first,
_RandomAccessIterator __last)
{
typedef typename iterator_traits<_RandomAccessIterator>::value_type
_ValueType;

for (_RandomAccessIterator __i = __first; __i != __last; ++__i)
std::__unguarded_linear_insert(__i, _ValueType(*__i));
}

as you mentioned:
>...the total range into blocks of _S_threshold size, and all of the valuesin one block are greater than the values in all previous blocks, and less then the values in all following blocks
it could be possibile, but i think the __unguarded_partition() may
fail to partition the array with reduplicate keys.

__unguarded_partition(_RandomAccessIterator __first,
_RandomAccessIterator __last, _Tp __pivot)
{
while (true)
{
while (*__first < __pivot)
++__first;
--__last;
while (__pivot < *__last)
--__last;
if (!(__first < __last))
return __first;
std::iter_swap(__first, __last);
++__first;
}
}
Jul 10 '08 #6

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: tridion | last post by:
Could someone answer the following questions for me : 1. Discuss this C++ code fragment. Do you see any potential problems ? BSTR bsValue = _bstr_t("a string");...
3
by: Alan | last post by:
I have some questions about the STL template "vector": - Do I need to do anything (e.g., "delete") to free memory when I`m done with a vector? I just declare it, and I know it allocates memory...
8
by: Mike | last post by:
Hello, I have a few rather urgent questions that I hope someone can help with (I need to figure this out prior to a meeting tomorrow.) First, a bit of background: The company I work for is...
4
by: Megan | last post by:
Okay, I have a few questions regarding an Access database our company has. I should first mention that I'm not that knowledgable in Access and don't really know how to use it, but I am learning. We...
162
by: techievasant | last post by:
hello everyone, Iam vasant from India.. I have a test+interview on C /C++ in the coming month so plz help me by giving some resources of FAQS, interview questions, tracky questions, multiple...
4
by: PCHOME | last post by:
Hi! I have questions about qsort( ). Is anyone be willing to help? I use the following struct: struct Struct_A{ double value; ... } *AA, **pAA;
2
by: jy836 | last post by:
Firstly, is there a way to find out which column header a user has clicked on? And secondly, is there then an easy way (or any way, for that matter) to sort the items by that column (either...
0
by: Jobs | last post by:
All answers to the below interview questions are at http://www.geocities.com/dotnetinterviews/ or you can download the complete answer zip file from...
4
by: Schüle Daniel | last post by:
Hello, first question In : cmp("ABC",) Out: 1 against what part of the list is the string "ABC" compared? second question
3
by: JJ | last post by:
I've done a little multi-threading on winform apps some time ago now, so I'm not a complete beginner, but best assume I am for any explanations..: This is an asp.net 2.0 website, using c#: I...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
0
BarryA
by: BarryA | last post by:
What are the essential steps and strategies outlined in the Data Structures and Algorithms (DSA) roadmap for aspiring data scientists? How can individuals effectively utilize this roadmap to progress...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.