Kembalinya Lomuto dengan penuh kemenangan

USA, Texas, Austin, Continental Club,

Minggu 5 Januari 1987



“Terima kasih atas undangannya, Tuan Lomuto. Saya akan segera kembali ke Inggris, jadi tepat waktu.



“Terima kasih telah setuju untuk bertemu dengan saya, mister… Sir… Charles… Anthony Richard… Hoare. Ini adalah kehormatan besar bagiku. Saya bahkan tidak tahu bagaimana menghubungi Anda. Apakah Anda memiliki gelar ksatria?



“Panggil aku Tony, dan jika kamu mau, aku akan memanggilmu Niko.



Sekilas, ini pemandangan yang umum: dua orang sedang menikmati wiski. Namun, detail yang menarik diungkapkan kepada pengamat yang cermat. Pertama-tama, ketegangan itu bisa dipotong dengan pisau.



Mengenakan setelan tiga potong yang disesuaikan tanpa cela dengan kelalaian orang Inggris yang disengaja, Tony Hoare sama seperti orang Inggris seperti secangkir teh. Ekspresi rendah hati di wajahnya yang ia teguk dari gelasnya, tanpa kata-kata, mengungkapkan pendapatnya dalam perdebatan antara bourbon dan scotch. Duduk berseberangan dengan Niko Lomuto mewakili kebalikan dari dirinya: seorang programmer mengenakan jeans, mencampurkan wiski dan cola (yang sangat keterlaluan bagi Tony sehingga dia segera memutuskan untuk mengabaikannya dengan tegas - seperti bau keringat yang menyengat atau tato yang menghina), dalam keadaan kagum santai di depan raksasa ilmu komputer, yang baru saja dia temui secara langsung.



"Dengar, Tony," kata Niko setelah dia kehabisan topik untuk percakapan ringannya yang biasa. - Tentang algoritma partisi itu. Saya bahkan tidak akan menerbitkannya ...



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



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