Por que swap é chamado por std :: sort apenas se meu contêiner tiver mais de 32 elementos?

13

Olá, tenho uma pergunta simples:

class A 
{
public:
    A(int);
    A(const A&);
    A& operator=(const A&);
    ~A();
private:
    int* ptr_;

    friend bool operator<(const A&, const A&);
    friend void swap(A&, A&);
};

A::A(int x) : 
    ptr_(new int(x))
{}

A::A(const A& rhs) :
    ptr_(rhs.ptr_ ? new int(*rhs.ptr_) : nullptr)
{}

A& A::operator = (const A & rhs)
{
    int* tmp = rhs.ptr_ ? new int(*rhs.ptr_) : nullptr;
    delete ptr_;
    ptr_ = tmp;

    return *this;
}

A::~A()
{
    delete ptr_;
}

bool operator<(const A& lhs, const A& rhs)
{
    cout << "operator<(const A&, const A&)" << endl;
    return *lhs.ptr_ < *rhs.ptr_;
}

void swap(A& lhs, A& rhs)
{
    cout << "swap(A&, A&)" << endl;
    using std::swap;
    swap(lhs.ptr_, rhs.ptr_);
}

int main()
{

    std::vector<A> v{ 33,32,31,30,29,28,27,26,25,24,23,22, 21,20,19,18,17,16,15,14,13,12,11,10,9,8,7,6,5, 4,3,2,1 };
    std::sort(v.begin(), v.end());

}

Com mais de 32 elementos, a classificação é chamada swap. Com 32 elementos ou menos, os elementos ainda são classificados, mas swapnão são chamados.

  • Estou usando o MSVC ++ 2019 em x64.
  • Quando é swapchamado e quando não é e por quê? Obrigado!
  • Não usei swapna atribuição de cópias apenas para distinguir entre a chamada e a classificação do operador de atribuição de cópia.
Maestro
fonte
6
std::sortrecorre à classificação por inserção se o número de elementos for 32 ou menos e usa a classificação rápida caso contrário.
Evg
@ Evg Isso é um requisito ou uma explicação para esse contexto específico?
François Andrieux
2
@ FrançoisAndrieux, este é um detalhe de implementação da biblioteca padrão da Microsoft. Meu palpite é que esse é o motivo do comportamento observado pelo OP. Atualmente, estou pesquisando o código-fonte para obter mais detalhes.
Evg
11
Parte relevante da fonte é: while (_ISORT_MAX < (_Count = _Last - _First) && 0 < _Ideal)where _ISORT_MAXé dado o valor de 32. Linha 3447 do <algorithm>uso do VS 16.5.0
ChrisMM
Nenhum quicksort real é usado em qualquer biblioteca padrão moderna em qualquer idioma. Todos usam versões mistas modificadas, que são apenas uma classificação rápida quando o número de elementos é grande o suficiente. Por exemplo, Java e Python usam o Timsort, enquanto o .NET framework e a biblioteca C ++ do GCC usam o Introsort . libstdc ++ e libc ++ também usam classificação de inserção para sequências curtas. Consulte Quais algoritmos são usados ​​no C ++ 11 std :: sort em diferentes implementações STL?
phuclv

Respostas:

14

A std::sortimplementação da Microsoft se parece com isso:

const int ISORT_MAX = 32;  // maximum size for insertion sort

template<class RanIt, class Diff, class Pr>
void Sort(RanIt First, RanIt Last, Diff Ideal, Pr Pred)
{
    Diff Count;
    for (; ISORT_MAX < (Count = Last - First) && 0 < Ideal; )
    {   // divide and conquer by quicksort
        pair<RanIt, RanIt> Mid = Unguarded_partition(First, Last, Pred);

        // ...
    }

    if (ISORT_MAX < Count)
    {   // heap sort if too many divisions
        Make_heap(First, Last, Pred);
        Sort_heap(First, Last, Pred);
    }
    else if (1 < Count)
        Insertion_sort(First, Last, Pred);  // small
}

Quando o intervalo a ser classificado possui 32 elementos ou menos, Sortusa a classificação por inserção. A classificação por inserção não é usada swapem sua implementação . Caso contrário, a classificação rápida de dividir e conquistar é usada. Na implementação , chama iter_swap(dentro Unguarded_partition), que por sua vez chama swap:

template<class FwdIt1, class FwdIt2>
void iter_swap(FwdIt1 Left, FwdIt2 Right)
{   // swap *Left and *Right
    swap(*Left, *Right);
}

Todos esses são detalhes de implementação. Eles variam de uma implementação de biblioteca padrão para outra.

Evg
fonte
11
A libcxx usa a ordenação por inserção para seqüências de tamanho menor que 6 ou 30, dependendo do tipo. O libstd ++ faz isso para seqüências de 16 elementos ou menos. Quais algoritmos são usados ​​no C ++ 11 std :: sort em diferentes implementações STL?
Phuclv