Lomutos triumphale Rückkehr

USA, Texas, Austin, Continental Club

Sonntag, 5. Januar 1987



„Danke für die Einladung, Mr. Lomuto. Ich kehre bald nach England zurück, also war es gerade noch rechtzeitig.



„Danke, dass Sie zugestimmt haben, sich mit mir zu treffen, Mr.… Sir… Charles… Anthony Richard… Hoare. Das ist eine große Ehre für mich. Ich weiß nicht einmal, wie ich Sie kontaktieren soll. Hast du einen ritterlichen Titel?



„Nenn mich Tony, und wenn du willst, nenne ich dich Niko.



Auf den ersten Blick ist dies ein häufiger Anblick: Zwei Menschen genießen Whisky. Dem aufmerksamen Beobachter werden jedoch interessante Details offenbart. Zuallererst die Spannung, die mit einem Messer geschnitten werden könnte.



Tony Hoare trug einen tadellos geschnittenen dreiteiligen Anzug mit der bewussten Lässigkeit eines Engländers und war so britisch wie eine Tasse Tee. Der bescheidene Ausdruck auf seinem Gesicht, mit dem er wortlos aus seinem Glas nippte, drückte seine Meinung im Streit zwischen Bourbon und Scotch aus. Das Sitzen gegenüber von Niko Lomuto war das genaue Gegenteil von ihm: Ein Programmierer in Jeans, der Whisky und Cola mischte (was für Tony so empörend war, dass er sich sofort entschied, es nachdrücklich zu ignorieren - wie ein stechender Schweißgeruch oder ein beleidigendes Tattoo), in einem Zustand entspannter Ehrfurcht vor einem Riesen Informatik, die er gerade persönlich kennengelernt hat.



„Hör zu, Tony“, sagte Nico, nachdem ihm die Themen für sein übliches leichtes Gespräch ausgegangen waren. - Über diesen Partitionierungsalgorithmus. Ich wollte es nicht einmal veröffentlichen ...



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



— , , , — . — . 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