Radix сортировать по 38 бит в uint64? - PullRequest
1 голос
/ 28 января 2020

Вопрос: существует ли сортировка по радикалу Dial_A_Bit, которая может использоваться для сортировки по подмножеству битов в типе данных?

uint64 *Radix_Sort_Dial_A_Bit(uint64_t *a, int num_a, int sort_bits);

В частности, будет ли возможна 38-битная сортировка на 64-битных данных и иметь скорость где-то между 32/64 и 48/64 вариантами, показанными ниже?

uint64 *Radix_Sort_ui64_38MSB(uint64_t *a, int num_a);

Примечание как исследование 48 и 32-битных сортировок подтвердило увеличение скорости и правильности по сравнению с сортировкой по всем 64-битным в uint64_t [].

Кажется, что Radix_Sort (), который сортирует по подмножеству размера пакета данных, будет в целом полезным и эффективным, сортируя только то, что нужно.

В некоторых случаях результаты рассчитываются для каждого пикселя и должны быть отсортированы. Uint64_t [] используется для хранения как результата вычисления, так и местоположения XY. * Всего 1011 *

26 битов (13 для X и 13 для Y, максимальное разрешение 8192) необходимы для хранения координат XY пикселя, что оставляет 38 биты для данных, которые будут отсортированы.

Весь 64-битный пакет может быть отсортирован с помощью Radix_Sort_Uint64 ().

Более быстрый метод заключается в использовании Radix_Sort_Uint48 () (см. Ниже), чтобы последние 16 бит не учитывались при сортировке. Это сортирует все данные правильно, а также сортирует 10 из 13 битов координат X, которые не нужны.

Поскольку производительность почти линейно пропорциональна отсортированным битам, оптимально, только 38 наиболее значимых битов будут рассматриваться в сортировке.

Даже 40-битная сортировка по Radix будет лучше, чем использование 48-битной Я пытался обобщить работающую 48-битную сортировку по Radix для работы на 40 битах, но она не сортируется правильно.

QSort_uint64_38_msb ():

static inline int comp_uint64_38_msb(const void *a, const void *b)  {
register int64_t ca, cb;
    ca = (*((uint64_t *)a)) >> 26;  // Send 26 LSBs to oblivion
    cb = (*((uint64_t *)b)) >> 26;  // Send 26 LSBs to oblivion
    return((ca > cb) - (ca < cb));  // Calcs to [+1, 0 -1]
}

Как видите, 48-битная сортировка значительно быстрее, чем полные 64 бита. 32-битная сортировка почти в два раза быстрее, чем полная 64-битная. И qsort сильно отстает:

  Time=  2.136 sec =  3.390%, RADIX_SORT_FFFF0000  , hits=4, 0.534 sec each
  Time=  2.944 sec =  4.672%, RADIX_SORT_FFFFFF00  , hits=4, 0.736 sec each
  Time=  4.691 sec =  7.444%, RADIX_SORT_64        , hits=5, 0.938 sec each
  Time= 25.209 sec = 40.010%, QSORT_UINT64_ARRAY   , hits=4, 6.302 sec each
  Time= 26.191 sec = 41.569%, QSORT_UINT64_38_ARRAY, hits=4, 6.548 sec each

Линеаризация от 64, 48 и 32-битных результатов до 38 бит:

lsf 64  0.938   48  0.736    32  0.534   38   ->  0.6500

38-битная Radix_Sort может быть на 35% быстрее, чем 64-битная сортировка и 17% быстрее, чем 48-битная сортировка.

Даже 40 битов будет быстрее, 5 байт против 6, обрабатываемых за uint64.

=========

Самый быстрый, 6 из 8-байтовый тип uint64 [], обобщенный из: 32 MSbit, используемых для сортировки uint64

// #############################################################################
// From: http://ideone.com/JHI0d9
// RadixSort---  for 48 MSB of uint64
typedef union {
    struct {
        uint32_t c6[256];
        uint32_t c5[256];
        uint32_t c4[256];
        uint32_t c3[256];
        uint32_t c2[256];
        uint32_t c1[256];
    };
    uint32_t counts[256 * 6];
}  rscounts6_t;


// #############################################################################
// Patterned off of Radix_Sort_64 but looks only at the 48 MostSigBits
// 0XFFFF-FFFF-FFFF-0000  << Ignore the zeros, sort on 3 MostSigBytes
// Made for RGB48 stuffed into uint64 with 2 LeastSig bytes zero
// Get rid of the 7 and 8 level comps
uint64_t *radix_sort_48_msb(uint64_t *arrayA, uint32_t asize) 
{
register uint64_t *array=arrayA;  // Slam arg into Register!
register int ii;  // Loop control

    rscounts6_t counts;
    memset(&counts, 0, 256 * 6 * sizeof(uint32_t));
    uint64_t *cpy = (uint64_t *)malloc(asize * sizeof(uint64_t));
    uint32_t o6=0, o5=0, o4=0, o3=0, o2=0, o1=0;
    uint32_t t6, t5, t4, t3, t2, t1;
    register uint32_t x;
    // calculate counts
    for(x = 0; x < asize; x++) {
        t6 = (array[x] >> 16) & 0xff;
        t5 = (array[x] >> 24) & 0xff;
        t4 = (array[x] >> 32) & 0xff;
        t3 = (array[x] >> 40) & 0xff;
        t2 = (array[x] >> 48) & 0xff;
        t1 = (array[x] >> 56) & 0xff;
        counts.c6[t6]++;
        counts.c5[t5]++;
        counts.c4[t4]++;
        counts.c3[t3]++;
        counts.c2[t2]++;
        counts.c1[t1]++;
    }
    // convert counts to offsets
    for(x = 0; x < 256; x++) {
        t6 = o6 + counts.c6[x];
        t5 = o5 + counts.c5[x];
        t4 = o4 + counts.c4[x];
        t3 = o3 + counts.c3[x];
        t2 = o2 + counts.c2[x];
        t1 = o1 + counts.c1[x];
        counts.c6[x] = o6;
        counts.c5[x] = o5;
        counts.c4[x] = o4;
        counts.c3[x] = o3;
        counts.c2[x] = o2;
        counts.c1[x] = o1;
        o6 = t6; 
        o5 = t5; 
        o4 = t4; 
        o3 = t3; 
        o2 = t2; 
        o1 = t1;
    }
    // radix
    for(x = 0; x < asize; x++) {
        t6 = (array[x] >> 16) & 0xff;
        cpy[counts.c6[t6]] = array[x];
        counts.c6[t6]++;  }
    for(x = 0; x < asize; x++) {
        t5 = (cpy[x] >> 24) & 0xff;
        array[counts.c5[t5]] = cpy[x];
        counts.c5[t5]++;  }
    for(x = 0; x < asize; x++) {
        t4 = (array[x] >> 32) & 0xff;
        cpy[counts.c4[t4]] = array[x];
        counts.c4[t4]++;  }
    for(x = 0; x < asize; x++) {
        t3 = (cpy[x] >> 40) & 0xff;
        array[counts.c3[t3]] = cpy[x];
        counts.c3[t3]++;  }
    for(x = 0; x < asize; x++) {
        t2 = (array[x] >> 48) & 0xff;
        cpy[counts.c2[t2]] = array[x];
        counts.c2[t2]++;  }
    for(x = 0; x < asize; x++) {
        t1 = (cpy[x] >> 56) & 0xff;
        array[counts.c1[t1]] = cpy[x];
        counts.c1[t1]++;  }
    free(cpy);
    return array;
}  // End radix_sort_48_msb().

========================================

Еще раз спасибо Rcgldr для инновационного предложения программирования! Вместо 10, 10, 9, 9 я использовал быструю 32-битную комбинацию с [4] [10]

. Это работает, но это немного медленнее, чем сортировка 48 MSB, 737 mse * 1071. * для 40 MSBt, 588 мсэ c для 48 MSB. : (

Возможно, я плохо его кодировал.

    Time=  6.108 sec = 33.668%, QSORT_UINT64_ARRAY   , hits=1
    Time=  3.060 sec = 16.866%, RADIX_SORT_UINT64_REG, hits=4, 0.765 sec each
    Time=  2.947 sec = 16.241%, RADIX_SORT_UINT64_40R, hits=4, 0.737 sec each < SLOW
    Time=  2.354 sec = 12.973%, RADIX_SORT_UINT64_48R, hits=4, 0.588 sec each
    Time=  1.542 sec =  8.498%, RADIX_SORT_UINT64_32R, hits=4, 0.385 sec each
    Time=  0.769 sec =  4.236%, RADIX_SORT_64        , hits=1

Тест:

  • создать случайный, основной массив uint64_t [36M]
  • отсортируйте его как с помощью qsort, так и с помощью известного исправного метода Radix Sort, radixSort (), чтобы создать стандартный массив
  • Сравнение результатов Qsort и radixSort () на идентичность.
  • Сортируйте копию мастера с 32, 40, 48 и 64 MSB Radix Sorts
  • Сравните каждую тестовую сортировку со стандартом после маскировки игнорируемых LSB

Вот код:

    //=============================================================================
// From code submitted by rcgldr, Feb 8 2020
// Optimized to use Registers and to sort on 40 MSBs, ignoring 24 LSBs
void radix_sort_r64_40(uint64_t *pData, uint64_t *pTemp, size_t count,
    EV_TIME_STR *tsa)
{
    size_t mIndex[4][1024] = { 0 };  /* index matrix */
    size_t * pmIndex;                /* ptr to row of matrix */
    size_t i, j, m, n;
    uint64_t u;
    if(tsa)  time_event(E_RADIX_SORT_UINT64_40R, tsa, E_TIME_EVENT, 1, 0);  

    for (i = 0; i < count; i++) {       /* generate histograms */
        u = pData[i];
        mIndex[3][(u >> 24) & 0x3ff]++;
        mIndex[2][(u >> 34) & 0x3ff]++;
        mIndex[1][(u >> 44) & 0x3ff]++;
        mIndex[0][(u >> 54) & 0x3ff]++;
    }

    for (j = 0; j < 4; j++) {           /* convert to indices */
        pmIndex = mIndex[j];
        n = 0;
        for (i = 0; i < 1024; i++)  {
            m = pmIndex[i];
            pmIndex[i] = n;
            n += m;
        }
    }

    for (i = 0; i < count; i++)  {  /* radix sort */
        u = pData[i];
        pTemp[mIndex[3][(u >> 24) & 0x3ff]++] = u;
    }
    for (i = 0; i < count; i++)  {
        u = pTemp[i];
        pData[mIndex[2][(u >> 34) & 0x3ff]++] = u;
    }
    for (i = 0; i < count; i++)  {
        u = pData[i];
        pTemp[mIndex[1][(u >> 44) & 0x3ff]++] = u;
    }
    for (i = 0; i < count; i++)  {
        u = pTemp[i];
        pData[mIndex[0][(u >> 54) & 0x3ff]++] = u;
    }
}  // End Radix_Sort_R64_40().

Вот уникальное отличие между версией FAST 32 MSB и клонированной медленной версией 40 MSB.

Unique lines from "~/tmp/radix.sort.32.c":
  02) void radix_sort_r64_32(uint64_t *pData, uint64_t *pTemp, size_t count,
  05) size_t mIndex[4][256] = { 0 };      /* index matrix */
  09) if(tsa)  time_event(E_RADIX_SORT_UINT64_32R, tsa, E_TIME_EVENT, 1, 0);
  13) mIndex[3][(u >> 32) & 0xff]++;  // B4
  14) mIndex[2][(u >> 40) & 0xff]++;  // B5
  15) mIndex[1][(u >> 48) & 0xff]++;  // B6
  16) mIndex[0][(u >> 56) & 0xff]++;  // B7
  22) for (i = 0; i < 256; i++) {
  31) pTemp[mIndex[3][(u >> 32) & 0xff]++] = u;
  35) pData[mIndex[2][(u >> 40) & 0xff]++] = u;
  39) pTemp[mIndex[1][(u >> 48) & 0xff]++] = u;
  43) pData[mIndex[0][(u >> 56) & 0xff]++] = u;

Unique lines from "~/tmp/radix.sort.40.c":
  01) void radix_sort_r64_40(uint64_t *pData, uint64_t *pTemp, size_t count,
  04) size_t mIndex[4][1024] = { 0 };  /* index matrix */
  08) if(tsa)  time_event(E_RADIX_SORT_UINT64_40R, tsa, E_TIME_EVENT, 1, 0);
  12) mIndex[3][(u >> 24) & 0x3ff]++;
  13) mIndex[2][(u >> 34) & 0x3ff]++;
  14) mIndex[1][(u >> 44) & 0x3ff]++;
  15) mIndex[0][(u >> 54) & 0x3ff]++;
  21) for (i = 0; i < 1024; i++)  {
  30) pTemp[mIndex[3][(u >> 24) & 0x3ff]++] = u;
  34) pData[mIndex[2][(u >> 34) & 0x3ff]++] = u;
  38) pTemp[mIndex[1][(u >> 44) & 0x3ff]++] = u;
  42) pData[mIndex[0][(u >> 54) & 0x3ff]++] = u;

Ответы [ 4 ]

2 голосов
/ 08 февраля 2020

Радикальная сортировка для 38 мсб может быть выполнена за 4 прохода с использованием {10 бит, 10 бит, 9 бит, 9 бит} отсчетов == смещения. Измените код для использования размеров: c1 [1024], c2 [1024], c3 [512], c4 [512] и инициализация счетчика для использования сдвигов и масок {(... >> 54) & 0x3ff, (.. . >> 44) & 0x3ff, (... >> 35) & 0x1ff, (... >> 26) & 0x1ff} и внесите аналогичные изменения в остальной код.


Попробуйте использовать этот код для сравнения. Либо измените свой код, либо добавьте таймер к этому коду:

void RadixSort(uint64_t *a, uint64_t *b, size_t count)
{
    uint32_t mIndex[4][1024] = { 0 };  /* index matrix */
    uint32_t * pmIndex;                /* ptr to row of matrix */
    uint32_t i, j, m, n;
    uint64_t u;

    for (i = 0; i < count; i++) {       /* generate histograms */
        u = a[i];
        mIndex[3][(u >> 26) & 0x1ff]++;
        mIndex[2][(u >> 35) & 0x1ff]++;
        mIndex[1][(u >> 44) & 0x3ff]++;
        mIndex[0][(u >> 54) & 0x3ff]++;
    }

    for (j = 0; j < 2; j++) {           /* convert to indices */
        pmIndex = mIndex[j];
        n = 0;
        for (i = 0; i < 1024; i++)  {
            m = pmIndex[i];
            pmIndex[i] = n;
            n += m;
        }
    }
    for (j = 2; j < 4; j++) {
        pmIndex = mIndex[j];
        n = 0;
        for (i = 0; i < 512; i++)  {
            m = pmIndex[i];
            pmIndex[i] = n;
            n += m;
        }
    }

    pmIndex = mIndex[3];
    for (i = 0; i < count; i++)  {      /* radix sort */
        u = a[i];
        b[pmIndex[(u >> 26) & 0x1ff]++] = u;
    }
    pmIndex = mIndex[2];
    for (i = 0; i < count; i++)  {
        u = b[i];
        a[pmIndex[(u >> 35) & 0x1ff]++] = u;
    }
    pmIndex = mIndex[1];
    for (i = 0; i < count; i++)  {
        u = a[i];
        b[pmIndex[(u >> 44) & 0x3ff]++] = u;
    }
    pmIndex = mIndex[0];
    for (i = 0; i < count; i++)  {
        u = b[i];
        a[pmIndex[(u >> 54) & 0x3ff]++] = u;
    }
}

Еще быстрее сначала отсортировать по старшим 10 битам, создав 1024 ячейки, затем отсортировать 1024 ячейки, {10, 9,9} битовые поля, сначала младшие биты. Это ускорит сортировку, поскольку каждый из 1024 бинов помещается в кэш, уменьшая накладные расходы для всех операций произвольного доступа. Примечание. AIndex имеет размер 1025, чтобы отслеживать размер последнего бина.

void RadixSort3(uint64_t *, uint64_t *, size_t);

/* split array into 1024 bins according to most significant 10 bits */
void RadixSort(uint64_t *a, uint64_t *b, size_t count)
{
uint32_t aIndex[1025] = {0};            /* index array */
uint32_t i, m, n;
    for(i = 0; i < count; i++)          /* generate histogram */
        aIndex[(a[i] >> 54)]++;
    n = 0;                              /* convert to indices */
    for (i = 0; i < 1025; i++)  {
        m = aIndex[i];
        aIndex[i] = n;
        n += m;
    }
    for(i = 0; i < count; i++)          /* sort by ms 10 bits */
        b[aIndex[a[i]>>54]++] = a[i];
    for(i = 1024; i; i--)               /* restore aIndex */
        aIndex[i] = aIndex[i-1];
    aIndex[0] = 0;
    for(i = 0; i < 1024; i++)           /* radix sort the 1024 bins */
        RadixSort3(&b[aIndex[i]], &a[aIndex[i]], aIndex[i+1]-aIndex[i]);
}

void RadixSort3(uint64_t *a, uint64_t *b, size_t count)
{
    uint32_t mIndex[3][1024] = { 0 };   /* index matrix */
    uint32_t * pmIndex;                 /* ptr to row of matrix */
    uint32_t i, j, m, n;
    uint64_t u;

    for (i = 0; i < count; i++) {       /* generate histograms */
        u = a[i];
        mIndex[2][(u >> 26) & 0x1ff]++;
        mIndex[1][(u >> 35) & 0x1ff]++;
        mIndex[0][(u >> 44) & 0x3ff]++;
    }

    for (j = 0; j < 1; j++) {           /* convert to indices */
        pmIndex = mIndex[j];
        n = 0;
        for (i = 0; i < 1024; i++)  {
            m = pmIndex[i];
            pmIndex[i] = n;
            n += m;
        }
    }
    for (j = 1; j < 3; j++) {
        pmIndex = mIndex[j];
        n = 0;
        for (i = 0; i < 512; i++)  {
            m = pmIndex[i];
            pmIndex[i] = n;
            n += m;
        }
    }

    pmIndex = mIndex[2];
    for (i = 0; i < count; i++)  {      /* radix sort */
        u = a[i];
        b[pmIndex[(u >> 26) & 0x1ff]++] = u;
    }
    pmIndex = mIndex[1];
    for (i = 0; i < count; i++)  {
        u = b[i];
        a[pmIndex[(u >> 35) & 0x1ff]++] = u;
    }
    pmIndex = mIndex[0];
    for (i = 0; i < count; i++)  {
        u = a[i];
        b[pmIndex[(u >> 44) & 0x3ff]++] = u;
    }
}
1 голос
/ 09 февраля 2020

Для полноты работы работает 6-разрядная, 7-разрядная / двоичная 42-разрядная сортировка по шкале MSB, а производительность соответствует 48-разрядной и 36-разрядной версиям.

  Time=  6.334 sec = 25.435%, QSORT_UINT64_ARRAY   , hits=1
  Time=  3.519 sec = 14.131%, RADIX_SORT_UINT64_REG, hits=4, 0.880 sec each
  Time=  3.273 sec = 13.145%, RADIX_SORT_UINT64_40R, hits=4, 0.818 sec each < anomaly
  Time=  2.680 sec = 10.764%, RADIX_SORT_UINT64_48R, hits=4, 0.670 sec each
  Time=  2.302 sec =  9.246%, RADIX_SORT_UINT64_42R, hits=4, 0.576 sec each < NEW
  Time=  2.025 sec =  8.132%, RADIX_SORT_UINT64_36R, hits=4, 0.506 sec each
  Time=  1.767 sec =  7.094%, RADIX_SORT_UINT64_32R, hits=4, 0.442 sec each
  Time=  0.955 sec =  3.835%, RADIX_SORT_64        , hits=1

Кто-нибудь может объяснить, почему 40 MSB сортировка намного медленнее, чем 48 MSB сортировка?

Полный код:

void radix_sort_r64_42(uint64_t *pData, uint64_t *pTemp, size_t count,
    EV_TIME_STR *tsa)
{
    size_t mIndex[6][128] = { 0 };  /* index matrix */
    size_t * pmIndex;              /* ptr to row of matrix */
    size_t i, j, m, n;
    uint64_t u;
    if(tsa)  time_event(E_RADIX_SORT_UINT64_42R, tsa, E_TIME_EVENT, 1, 0);  

    // 64 -- 56 48 40 32 24 16  -- 8 bits each
    // 64 -- 57 50 43 36 29 22  -- 7 bits each
    // 64 -- 58 52 46 40 34 28  -- 6 bits each
    for (i = 0; i < count; i++) {       /* generate histograms */
        u = pData[i];                   // Igonores Nibbles 0, 1 & 2
        mIndex[5][(u >> 22) & 0x7F]++;  // N2
        mIndex[4][(u >> 29) & 0x7F]++;  // N3
        mIndex[3][(u >> 36) & 0x7F]++;  // N4
        mIndex[2][(u >> 43) & 0x7F]++;  // N5
        mIndex[1][(u >> 50) & 0x7F]++;  // N6
        mIndex[0][(u >> 57) & 0x7F]++;  // N7
    }

    for (j = 0; j < 6; j++) {           /* convert to indices */
        pmIndex = mIndex[j];
        n = 0;
        for (i = 0; i < 128; i++) {
            m = pmIndex[i];
            pmIndex[i] = n;
            n += m;
        }
    }

    for (i = 0; i < count; i++) {  /* radix sort */
        u = pData[i];
        pTemp[mIndex[5][(u >> 22) & 0x7F]++] = u;
    }
    for (i = 0; i < count; i++) { 
        u = pTemp[i];
        pData[mIndex[4][(u >> 29) & 0x7F]++] = u;
    }
    for (i = 0; i < count; i++) {
        u = pData[i];
        pTemp[mIndex[3][(u >> 36) & 0x7F]++] = u;
    }
    for (i = 0; i < count; i++) {
        u = pTemp[i];
        pData[mIndex[2][(u >> 43) & 0x7F]++] = u;
    }
    for (i = 0; i < count; i++) {
        u = pData[i];
        pTemp[mIndex[1][(u >> 50) & 0x7F]++] = u;
    }
    for (i = 0; i < count; i++) {
        u = pTemp[i];
        pData[mIndex[0][(u >> 57) & 0x7F]++] = u;
    }
}  // End Radix_Sort_R64_42().

ДОБАВИТЬ версию diff 36 MSB против 42 MSB

Unique lines from "~/tmp/radix.sort.36.c":
  01) void radix_sort_r64_36(uint64_t *pData, uint64_t *pTemp, size_t count,
  04) size_t mIndex[6][64] = { 0 };  /* index matrix */
  11) mIndex[5][(u >> 28) & 0x3F]++;  // N2
  12) mIndex[4][(u >> 34) & 0x3F]++;  // N3
  13) mIndex[3][(u >> 40) & 0x3F]++;  // N4
  14) mIndex[2][(u >> 46) & 0x3F]++;  // N5
  15) mIndex[1][(u >> 52) & 0x3F]++;  // N6
  16) mIndex[0][(u >> 58) & 0x3F]++;  // N7
  22) for (i = 0; i < 64; i++) {
  31) pTemp[mIndex[5][(u >> 28) & 0x3F]++] = u;
  35) pData[mIndex[4][(u >> 34) & 0x3F]++] = u;
  39) pTemp[mIndex[3][(u >> 40) & 0x3F]++] = u;
  43) pData[mIndex[2][(u >> 46) & 0x3F]++] = u;
  47) pTemp[mIndex[1][(u >> 52) & 0x3F]++] = u;
  51) pData[mIndex[0][(u >> 58) & 0x3F]++] = u;

19 Unique lines from "~/tmp/radix.sort.42.c":
  01) void radix_sort_r64_42(uint64_t *pData, uint64_t *pTemp, size_t count,
  04) size_t mIndex[6][128] = { 0 };  /* index matrix */
  10) // 64 -- 56 48 40 32 24 16  -- 8 bits each
  11) // 64 -- 57 50 43 36 29 22  -- 7 bits each
  12) // 64 -- 58 52 46 40 34 28  -- 6 bits each
  15) mIndex[5][(u >> 22) & 0x7F]++;  // N2
  16) mIndex[4][(u >> 29) & 0x7F]++;  // N3
  17) mIndex[3][(u >> 36) & 0x7F]++;  // N4
  18) mIndex[2][(u >> 43) & 0x7F]++;  // N5
  19) mIndex[1][(u >> 50) & 0x7F]++;  // N6
  20) mIndex[0][(u >> 57) & 0x7F]++;  // N7
  26) for (i = 0; i < 128; i++) {
  35) pTemp[mIndex[5][(u >> 22) & 0x7F]++] = u;
  39) pData[mIndex[4][(u >> 29) & 0x7F]++] = u;
  43) pTemp[mIndex[3][(u >> 36) & 0x7F]++] = u;
  47) pData[mIndex[2][(u >> 43) & 0x7F]++] = u;
  51) pTemp[mIndex[1][(u >> 50) & 0x7F]++] = u;
  55) pData[mIndex[0][(u >> 57) & 0x3F]++] = u;
1 голос
/ 08 февраля 2020

Сортировка 40 MSB работала очень плохо, намного медленнее, чем версия 48 MSB.

Так что я попробовал [6] [64], 36-разрядную версию, смоделированную после относительно быстрой 48 MSB.

Я знаю, что хотел 38 битов, а не 36. Оптимизированный метод заключался в том, как упаковать местоположение XY и свойство в этой точке в uint64_t. С координатами 8k X и Y это заняло 13 + 13 битов для XY и оставило не более 64-26 = 38 битов для данных.

Текущий максимум данных - 34 или 35 бит, поэтому 36 должно работать.

Вот производительность:

  Time=  6.104 sec = 30.673%, QSORT_UINT64_ARRAY   , hits=1
  Time=  3.117 sec = 15.663%, RADIX_SORT_UINT64_REG, hits=4, 0.779 sec each
  Time=  2.931 sec = 14.731%, RADIX_SORT_UINT64_40R, hits=4, 0.733 sec each
  Time=  2.269 sec = 11.401%, RADIX_SORT_UINT64_48R, hits=4, 0.567 sec each
  Time=  1.663 sec =  8.359%, RADIX_SORT_UINT64_36R, hits=4, 0.416 sec each < FAST
  Time=  1.516 sec =  7.620%, RADIX_SORT_UINT64_32R, hits=4, 0.379 sec each
  Time=  0.734 sec =  3.689%, RADIX_SORT_64        , hits=1

Это на 27% быстрее, чем 48-битный код.

И, похоже, его можно расширить до [6 ] [7] для сортировки на 42 старших бита, если слишком мало 36 бит!

Вот полный код:

void radix_sort_r64_36(uint64_t *pData, uint64_t *pTemp, size_t count,
    EV_TIME_STR *tsa)
{
    size_t mIndex[6][64] = { 0 };  /* index matrix */
    size_t * pmIndex;              /* ptr to row of matrix */
    size_t i, j, m, n;
    uint64_t u;
    if(tsa)  time_event(E_RADIX_SORT_UINT64_36R, tsa, E_TIME_EVENT, 1, 0);  

    // 64 -- 56 48 40 32 24 16  -- 8 bits each
    // 64 -- 58 52 46 40 34 28  -- 6 bits each
    for (i = 0; i < count; i++) {       /* generate histograms */
        u = pData[i];                   // Igonores Nibbles 0, 1 & 2
        mIndex[5][(u >> 28) & 0x3F]++;  // N2
        mIndex[4][(u >> 34) & 0x3F]++;  // N3
        mIndex[3][(u >> 40) & 0x3F]++;  // N4
        mIndex[2][(u >> 46) & 0x3F]++;  // N5
        mIndex[1][(u >> 52) & 0x3F]++;  // N6
        mIndex[0][(u >> 58) & 0x3F]++;  // N7
    }

    for (j = 0; j < 6; j++) {           /* convert to indices */
        pmIndex = mIndex[j];
        n = 0;
        for (i = 0; i < 64; i++) {
            m = pmIndex[i];
            pmIndex[i] = n;
            n += m;
        }
    }

    for (i = 0; i < count; i++) {  /* radix sort */
        u = pData[i];
        pTemp[mIndex[5][(u >> 28) & 0x3F]++] = u;
    }
    for (i = 0; i < count; i++) { 
        u = pTemp[i];
        pData[mIndex[4][(u >> 34) & 0x3F]++] = u;
    }
    for (i = 0; i < count; i++) {
        u = pData[i];
        pTemp[mIndex[3][(u >> 40) & 0x3F]++] = u;
    }
    for (i = 0; i < count; i++) {
        u = pTemp[i];
        pData[mIndex[2][(u >> 46) & 0x3F]++] = u;
    }
    for (i = 0; i < count; i++) {
        u = pData[i];
        pTemp[mIndex[1][(u >> 52) & 0x3F]++] = u;
    }
    for (i = 0; i < count; i++) {
        u = pTemp[i];
        pData[mIndex[0][(u >> 58) & 0x3F]++] = u;
    }
}  // End Radix_Sort_R64_36().

Уникальное отличие с функцией 48 MSB:

Unique lines from "/home/brianp/tmp/radix.sort.36.c":
  01) void radix_sort_r64_36(uint64_t *pData, uint64_t *pTemp, size_t count,
  04) size_t mIndex[6][64] = { 0 };  /* index matrix */
  08) if(tsa)  time_event(E_RADIX_SORT_UINT64_36R, tsa, E_TIME_EVENT, 1, 0);
  11) mIndex[5][(u >> 28) & 0x3F]++;  // N2
  12) mIndex[4][(u >> 34) & 0x3F]++;  // N3
  13) mIndex[3][(u >> 40) & 0x3F]++;  // N4
  14) mIndex[2][(u >> 46) & 0x3F]++;  // N5
  15) mIndex[1][(u >> 52) & 0x3F]++;  // N6
  16) mIndex[0][(u >> 58) & 0x3F]++;  // N7
  22) for (i = 0; i < 64; i++) {
  31) pTemp[mIndex[5][(u >> 28) & 0x3F]++] = u;
  35) pData[mIndex[4][(u >> 34) & 0x3F]++] = u;
  39) pTemp[mIndex[3][(u >> 40) & 0x3F]++] = u;
  43) pData[mIndex[2][(u >> 46) & 0x3F]++] = u;
  47) pTemp[mIndex[1][(u >> 52) & 0x3F]++] = u;
  51) pData[mIndex[0][(u >> 58) & 0x3F]++] = u;

Unique lines from "/home/brianp/tmp/radix.sort.48.c":
  01) void radix_sort_r64_48(uint64_t *pData, uint64_t *pTemp, size_t count,
  04) size_t mIndex[6][256] = { 0 };      /* index matrix */
  08) if(tsa)  time_event(E_RADIX_SORT_UINT64_48R, tsa, E_TIME_EVENT, 1, 0);
  14) mIndex[5][(u >> 16) & 0xff]++;  // B2
  15) mIndex[4][(u >> 24) & 0xff]++;  // B3
  16) mIndex[3][(u >> 32) & 0xff]++;  // B4
  17) mIndex[2][(u >> 40) & 0xff]++;  // B5
  18) mIndex[1][(u >> 48) & 0xff]++;  // B6
  19) mIndex[0][(u >> 56) & 0xff]++;  // B7
  25) for (i = 0; i < 256; i++) {
  34) pTemp[mIndex[5][(u >> 16) & 0xff]++] = u;
  38) pData[mIndex[4][(u >> 24) & 0xff]++] = u;
  42) pTemp[mIndex[3][(u >> 32) & 0xff]++] = u;
  46) pData[mIndex[2][(u >> 40) & 0xff]++] = u;
  50) pTemp[mIndex[1][(u >> 48) & 0xff]++] = u;
  54) pData[mIndex[0][(u >> 56) & 0xff]++] = u;
0 голосов
/ 14 февраля 2020

2 других результата теста алгоритма сортировки:

  Time=  6.208 sec = 21.838%, QSORT_UINT64_ARRAY   , hits=1
  Time=  3.358 sec = 11.813%, RADIX_SORT_UINT64_REG, hits=4, 0.840 sec each
  Time=  2.525 sec =  8.884%, RADIX_SORT_UI64_AA99 , hits=4, 0.631 sec each <NEW
  Time=  2.509 sec =  8.825%, RADIX_SORT_UINT64_48R, hits=4, 0.627 sec each
  Time=  2.461 sec =  8.658%, RADIX_SORT_UI64_1024 , hits=4, 0.615 sec each <NEW
  Time=  2.223 sec =  7.822%, RADIX_SORT_UINT64_42R, hits=4, 0.556 sec each
  Time=  2.215 sec =  7.791%, RADIX_SORT_UI64_40_85, hits=4, 0.554 sec each
  Time=  1.930 sec =  6.788%, RADIX_SORT_UINT64_36R, hits=4, 0.482 sec each
  Time=  1.710 sec =  6.014%, RADIX_SORT_UINT64_32R, hits=4, 0.427 sec each
  Time=  0.915 sec =  3.220%, COMP_UINT64_ARRAYS   , hits=32, 0.029 sec each

Еще один прогон без Firefox перегрузки системы:

  Time=  6.156 sec = 23.199%, QSORT_UINT64_ARRAY   , hits=1
  Time=  2.993 sec = 11.277%, RADIX_SORT_UINT64_REG, hits=4, 0.748 sec each
  Time=  2.409 sec =  9.077%, RADIX_SORT_UI64_AA99 , hits=4, 0.602 sec each < NEW
  Time=  2.330 sec =  8.778%, RADIX_SORT_UI64_1024 , hits=4, 0.582 sec each < NEW
  Time=  2.241 sec =  8.443%, RADIX_SORT_UINT64_48R, hits=4, 0.560 sec each
  Time=  2.124 sec =  8.002%, RADIX_SORT_UI64_40_85, hits=4, 0.531 sec each
  Time=  1.982 sec =  7.468%, RADIX_SORT_UINT64_42R, hits=4, 0.495 sec each
  Time=  1.725 sec =  6.499%, RADIX_SORT_UINT64_36R, hits=4, 0.431 sec each
  Time=  1.507 sec =  5.677%, RADIX_SORT_UINT64_32R, hits=4, 0.377 sec each
  Time=  0.889 sec =  3.348%, COMP_UINT64_ARRAYS   , hits=32, 0.028 sec each

RADIX_SORT_UI64_AA99 использует биты бина [10, 10, 9, 9] за 4 прохода.

RADIX_SORT_UI64_1024 сортирует сначала по старшим 10 битам.

RADIX_SORT_UINT64_42R использует 6 блоков по 7 битов каждый

Обратите внимание на дуэль 40 MSB, 8 Относительное быстродействие бина 5 бит RADIX_SORT_UI64_40_85 и 42 MSB, 7 бинов 6 бит RADIX_SORT_UINT64_42R. Сортировка 42 MSB часто намного быстрее, хотя 40-битная порция делает это время от времени.

Компилятор G CC, оптимизированный для скорости:

gcc -Ofast -ffast-math -m64 -march=native -funroll-loops -fopenmp -flto -finline-functions -Wuninitialized  ~/bin/pb.c  -lm  -o  ~/bin/pb_a 

Есть ли лучшие варианты компилятора?

g cc версия 7.4.1 20190905 [г cc -7-ветка ревизия 275407] (SUSE Linux)

===============================

Полный код:

// =============================================================================
// Sort with bin bits 10, 10, 9,9
// From code by rcgldr StackOverflow Feb 8, 2020
void RadixSort_aa99(uint64_t *a, uint64_t *b, size_t count, EV_TIME_STR *tsa)
{
uint32_t mIndex[4][1024] = { 0 };  /* index matrix */
uint32_t * pmIndex;                /* ptr to row of matrix */
uint32_t i, j, m, n;
uint64_t u;

if(tsa)  time_event(E_RADIX_SORT_UI64_AA99, tsa, E_TIME_EVENT, 1, 0);  
for (i = 0; i < count; i++) {       /* generate histograms */
    u = a[i];
    mIndex[3][(u >> 26) & 0x1ff]++;
    mIndex[2][(u >> 35) & 0x1ff]++;
    mIndex[1][(u >> 44) & 0x3ff]++;
    mIndex[0][(u >> 54) & 0x3ff]++;
}

for (j = 0; j < 2; j++) {           /* convert to indices */
    pmIndex = mIndex[j];
    n = 0;
    for (i = 0; i < 1024; i++)  {
        m = pmIndex[i];
        pmIndex[i] = n;
        n += m;
    }
}
for (j = 2; j < 4; j++) {
    pmIndex = mIndex[j];
    n = 0;
    for (i = 0; i < 512; i++)  {
        m = pmIndex[i];
        pmIndex[i] = n;
        n += m;
    }
}

pmIndex = mIndex[3];
for (i = 0; i < count; i++)  {      /* radix sort */
    u = a[i];
    b[pmIndex[(u >> 26) & 0x1ff]++] = u;
}
pmIndex = mIndex[2];
for (i = 0; i < count; i++)  {
    u = b[i];
    a[pmIndex[(u >> 35) & 0x1ff]++] = u;
}
pmIndex = mIndex[1];
for (i = 0; i < count; i++)  {
    u = a[i];
    b[pmIndex[(u >> 44) & 0x3ff]++] = u;
}
pmIndex = mIndex[0];
for (i = 0; i < count; i++)  {
    u = b[i];
    a[pmIndex[(u >> 54) & 0x3ff]++] = u;
}
}


// =============================================================================
/* split array into 1024 bins according to most significant 10 bits */
void RadixSort_1024(uint64_t *a, uint64_t *b, size_t count, EV_TIME_STR *tsa)
{
uint32_t aIndex[1025] = {0};            /* index array */
uint32_t i, m, n;
if(tsa)  time_event(E_RADIX_SORT_UI64_1024, tsa, E_TIME_EVENT, 1, 0);  
for(i = 0; i < count; i++)          /* generate histogram */
    aIndex[(a[i] >> 54)]++;
n = 0;                              /* convert to indices */
for (i = 0; i < 1025; i++)  {
    m = aIndex[i];
    aIndex[i] = n;
    n += m;
}
for(i = 0; i < count; i++)          /* sort by ms 10 bits */
    b[aIndex[a[i]>>54]++] = a[i];
for(i = 1024; i; i--)               /* restore aIndex */
    aIndex[i] = aIndex[i-1];
aIndex[0] = 0;
for(i = 0; i < 1024; i++)           /* radix sort the 1024 bins */
    RadixSort3(&b[aIndex[i]], &a[aIndex[i]], aIndex[i+1]-aIndex[i]);
}

void RadixSort3(uint64_t *a, uint64_t *b, size_t count)
{
uint32_t mIndex[3][1024] = { 0 };   /* index matrix */
uint32_t * pmIndex;                 /* ptr to row of matrix */
uint32_t i, j, m, n;
uint64_t u;

for (i = 0; i < count; i++) {       /* generate histograms */
    u = a[i];
    mIndex[2][(u >> 26) & 0x1ff]++;
    mIndex[1][(u >> 35) & 0x1ff]++;
    mIndex[0][(u >> 44) & 0x3ff]++;
}

for (j = 0; j < 1; j++) {           /* convert to indices */
    pmIndex = mIndex[j];
    n = 0;
    for (i = 0; i < 1024; i++)  {
        m = pmIndex[i];
        pmIndex[i] = n;
        n += m;
    }
}
for (j = 1; j < 3; j++) {
    pmIndex = mIndex[j];
    n = 0;
    for (i = 0; i < 512; i++)  {
        m = pmIndex[i];
        pmIndex[i] = n;
        n += m;
    }
}

pmIndex = mIndex[2];
for (i = 0; i < count; i++)  {      /* radix sort */
    u = a[i];
    b[pmIndex[(u >> 26) & 0x1ff]++] = u;
}
pmIndex = mIndex[1];
for (i = 0; i < count; i++)  {
    u = b[i];
    a[pmIndex[(u >> 35) & 0x1ff]++] = u;
}
pmIndex = mIndex[0];
for (i = 0; i < count; i++)  {
    u = a[i];
    b[pmIndex[(u >> 44) & 0x3ff]++] = u;
}
}
...