Combsort comparison

bearophile bearophileHUGS at lycos.com
Sun Jun 20 15:30:36 PDT 2010


Lutger:
> I get about 30% improvement if the inner loop is rewritten to:
> 
> foreach (i; 0 .. total - gap) // total was already computed by walkLength
> {
>     left.popFront;
>     right.popFront;
>     if (binaryFun!less(right.front, left.front)) {
> 	swap(left.front, right.front);
> 	swapped = true;
>     }
> }

This is a version that works (note the popFront at the end of the loop):


// D #4
import std.array: empty, popFront, popFrontN, front;
import std.range: walkLength, isForwardRange, hasSwappableElements;
import std.algorithm: swap, binaryFun;
import std.c.stdlib: malloc;
import std.c.stdio: printf;
debug {
    import std.contracts: enforce;
    import std.algorithm: sort, equal;
    import std.array: array;
}

void combsort(alias less="a < b", Range)(Range data)
  if (isForwardRange!Range && hasSwappableElements!Range) {

    static void combsort_impl(Range data) {
        enum double SHRINK_FACTOR = 1.247330950103979;
        size_t total = walkLength(data);
        size_t gap = total;
        bool swapped = true;

        while (gap > 1 || swapped) {
            if (gap > 1)
                gap /= SHRINK_FACTOR;

            swapped = false;
            auto left = data;
            auto right = data;
            popFrontN(right, gap);

            foreach (_; 0 .. total - gap) {
                if (binaryFun!less(right.front, left.front)) {
                    swap(left.front, right.front);
                    swapped = true;
                }
                left.popFront;
                right.popFront;
            }
        }
    }

    // postcondition verified in debug mode only.
    // This is a workaround, D postconditions don't
    // support the "old" (original input data view) yet.
    debug {
        auto data_copy = array(data);
        sort!(less)(data_copy);
        combsort_impl(data);
        enforce(equal(data, data_copy));
    } else {
        combsort_impl(data);
    }
}

int myrandom() {
    enum uint IM = 139968;
    enum uint IA = 3877;
    enum uint IC = 29573;
    static uint last = 42;
    last = (last * IA + IC) % IM;
    return last;
}

debug {
    bool issorted(int[] items) {
        if (items.length < 2)
            return true;

        foreach (i, el; items[0 .. $-1])
            if (el > items[i+1])
                return false;
        return true;
    }
}

void main() {
    enum int N = 10_000_000;
    int* v = cast(int*)malloc(int.sizeof * N);
    int i;
    for (i = 0; i < N; i++)
        v[i] = myrandom();

    debug {
        for (i = 0; i < N; i++)
            printf("%d ", v[i]);
        printf("\n");
    }

    combsort(v[0 .. N]);

    debug {
        for (i = 0; i < N; i++)
            printf("%d ", v[i]);
        printf("\n");
    }

    printf("%d\n", v[N-1]);

    debug {
        if (!issorted(v[0 .. N]))
            printf("NOT sorted\n");
    }
}

In all the programs I now use a size_t to keep the result of walkLenth and gap.
My timings don't show a 30% improvement:

Timings, seconds, best of 3:
  D3:  5.94
  D4:  5.86
  D2:  3.13
  C++: 3.05
  D1:  2.90
  C:   2.72

Bye,
bearophile


More information about the Digitalmars-d mailing list