O retorno triunfante de Lomuto

EUA, Texas, Austin, Continental Club

Domingo, 5 de janeiro de 1987



“Obrigado pelo convite, Sr. Lomuto. Estou voltando para a Inglaterra em breve, então foi na hora certa.



“Obrigado por concordar em me conhecer, senhor ... senhor ... Charles ... Anthony Richard ... Hoare. É uma grande honra para mim. Eu nem sei como entrar em contato com você. Você tem um título de cavaleiro?



“Me chame de Tony e, por favor, vou chamá-lo de Niko.



À primeira vista, esta é uma visão comum: duas pessoas tomando uísque. No entanto, detalhes intrigantes são revelados ao observador cuidadoso. Em primeiro lugar, a tensão que pode ser cortada com uma faca.



Vestido com um terno de três peças de corte impecável com a casualidade deliberada de um inglês, Tony Hoare era da Grã-Bretanha tanto quanto uma xícara de chá. A expressão de humildade no rosto com que bebeu do copo, sem palavras, exprimiu a sua opinião na disputa entre o bourbon e o scotch. Sentado em frente a Niko Lomuto representava o completo oposto dele: um programador vestido de jeans, misturando uísque e cola (o que foi tão ultrajante para Tony que ele imediatamente decidiu ignorar enfaticamente - como um cheiro pungente de suor ou uma tatuagem ofensiva), em um estado de espanto relaxado diante de um gigante ciência da computação, que ele acabou de conhecer pessoalmente.



“Escute, Tony,” Niko disse depois que ele ficou sem tópicos para sua conversa leve de costume. - Sobre aquele algoritmo de particionamento. Eu nem ia publicar ...



— ? , , — , , . , , , , , .



— , , , — . — . Ada, . , — , , — . , . nn! , . . , . . , - , - ( ?) , : . .



. . . , , . . . — .



, , , :



— , . . , . , , ...



— , : , . , — .



— . . . , . . .



— . . .



— , — .



, , -



. : — . , , , . , 2002 . ( fit pivot? ). , std.sort D, , , ( , , ). ( ), , . CppCon 2019 . — , .



. , « »? , : « » (Branch Mispredictions Don’t Affect Mergesort). . : , ? , , . , , , , , . . ( ), - : . !



, .



, ,



- . , , , , , , — . . ( , , , , ).



, «» «», 0. : , ( ). , . , . — ( Mastremo, CC-BY-SA 3.0).





, . , . , , , .



, «» «». , ( — , — ) ́ . , . , : , , .



, , , , . — STL — : , . , : , , , — , .



— — , : , . , , (, C++ D), , .



.





. , long . C++, D. , std::sort C++.



/**
Partition using the minimum of the first and last element as pivot.
Returns: a pointer to the final position of the pivot.
*/
long* hoare_partition(long* first, long* last) {
    assert(first <= last);
    if (last - first < 2)
        return first; // nothing interesting to do
    --last;
    if (*first > *last)
        swap(*first, *last);
    auto pivot_pos = first;
    auto pivot = *pivot_pos;
    for (;;) {
        ++first;
        auto f = *first;
        while (f < pivot)
            f = *++first;
        auto l = *last;
        while (pivot < l)
            l = *--last;
        if (first >= last)
            break;
        *first = l;
        *last = f;
        --last;
    }
    --first;
    swap(*first, *pivot_pos);
    return first;
}


, , : (, ), , . .



( , , , C++ D). , , . , , . . . C++ D. : LLVM (clang ldc) gcc (g++ gdc).



, , :



/**
Choose the pivot as the first element, then partition.
Returns: a pointer to the final position of the pivot. 
*/
long* lomuto_partition_naive(long* first, long* last) {
    assert(first <= last);
    if (last - first < 2)
        return first; // nothing interesting to do
    auto pivot_pos = first;
    auto pivot = *first;
    ++first;
    for (auto read = first; read < last; ++read) {
        if (*read < pivot) {
            swap(*read, *first);
            ++first;
        }
    }
    --first;
    swap(*first, *pivot_pos);
    return first;
}


, ( ), . first write, *first *write . , , :



/**
Partition using the minimum of the first and last element as pivot. 
Returns: a pointer to the final position of the pivot.
*/
long* lomuto_partition(long* first, long* last) {
    assert(first <= last);
    if (last - first < 2)
        return first; // nothing interesting to do
    --last;
    if (*first > *last)
        swap(*first, *last);
    auto pivot_pos = first;
    auto pivot = *first;
    // Prelude: position first (the write head) on the first element
    // larger than the pivot.
    do {
        ++first;
    } while (*first < pivot);
    assert(first <= last);
    // Main course.
    for (auto read = first + 1; read < last; ++read) {
        auto x = *read;
        if (x < pivot) {
            *read = *first;
            *first = x;
            ++first;
        }
    }
    // Put the pivot where it belongs.
    assert(*first >= pivot);
    --first;
    *pivot_pos = *first;
    *first = pivot;
    return first;
}


, hoare_partition. : swap . , . . :



for (auto read = first + 1; read < last; ++read) {
    auto x = *read;
    if (x < pivot) {
        *read = *first;
        *first = x;
        ++first;
    }
}


. : read < last x < pivot. ? , : , , . , , . ( —  Intel: « »). , , . . .



x < pivot — . . , , . ? ( ) , , , , ( ). , . , 30% .



? : , , , , . : . , , , , . , ( « »?). , :



for (auto read = first + 1; read < last; ++read) {
    auto x = *read;
    if (x < pivot) {
        *read = *first;
        *first = x;
        first += 1; 
    } else {
        *read = x;
        *first = *first;
        first += 0; 
    }
}


, . ( ), else *read *first . ? , . first : first += x < pivot. . , , . . , :



for (; read < last; ++read) {
    auto x = *read;
    auto smaller = -int(x < pivot);
    auto delta = smaller & (read - first);
    first[delta] = *first;
    read[-delta] = x;
    first -= smaller;
}


, explanatio longa, codex brevis est. , . smaller -int(x < pivot) , : smaller (0 −1) , 0x0 0xFFFFFFFF ( 0, 1) . , delta. x < pivot, smaller — , delta read - first. delta first[delta] read[-delta]*(first + delta) *(read - delta). delta , *(first + (read - first)) *(read - (read - first)).



first -= smaller — : x < pivot, first −1, , first 1. first 0, .



x < pivot 1, :



auto x = *read;
int smaller = -1;
auto delta = -1 & (read - first);
*(first + (read - first)) = *first;
*(read - (read - first)) = x;
first -= -1;


*read *first, ( , x *read). , «if» !



x < pivot — , delta , :



auto x = *read;
int smaller = 0;
auto delta = 0 & (read - first);
*(first + 0) = *first;
*(read - 0) = x;
first -= 0;


: *first *read , first . , , .



:



long* lomuto_partition_branchfree(long* first, long* last) {
    assert(first <= last);
    if (last - first < 2)
        return first; // nothing interesting to do
    --last;
    if (*first > *last)
        swap(*first, *last);
    auto pivot_pos = first;
    auto pivot = *first;
    do {
        ++first;
        assert(first <= last);
    } while (*first < pivot);
    for (auto read = first + 1; read < last; ++read) {
        auto x = *read;
        auto smaller = -int(x < pivot);
        auto delta = smaller & (read - first);
        first[delta] = *first;
        read[-delta] = x;
        first -= smaller;
    }
    assert(*first >= pivot);
    --first;
    *pivot_pos = *first;
    *first = pivot;
    return first;
}


, ? : clang/ldc g++/gdc. , .





: https://github.com/andralex/lomuto.



, , . , , ( , , ). , 3—9 , . , .



, . : . — , .



, . : Intel i7-4790 3600  256  / 1  / 8 , Ubuntu 20.04. (-O3, assert D). — long . .



D, std.sort .







C++. , std::sort .







— CPU, Intel VTune -. VTune , - , . ́ (), .



, ( ) . 30% - . , .





- . - .





- . , .





- . - , ́ .





( ) - . , .



, , , . , , , . , .



, ( ) , . — . , , . , , .



, , , .





Amr Elmasry, Jyrki Katajainen Max Stenmark . ( ), , . ( … ). , — . ( : «pretend not to notice» «pretend to not notice»? ). , , , — .




All Articles