Подтвердить что ты не робот

Самый быстрый способ сортировки 32-разрядных целочисленных массивов в JavaScript?

_radixSort_0 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
            0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
/*
RADIX SORT
Use 256 bins
Use shadow array
- Get counts
- Transform counts to pointers
- Sort from LSB - MSB
*/
function radixSort(intArr) {
    var cpy = new Int32Array(intArr.length);
    var c4 = [].concat(_radixSort_0); 
    var c3 = [].concat(_radixSort_0); 
    var c2 = [].concat(_radixSort_0);
    var c1 = [].concat(_radixSort_0); 
    var o4 = 0; var t4;
    var o3 = 0; var t3;
    var o2 = 0; var t2;
    var o1 = 0; var t1;
    var x;
    for(x=0; x<intArr.length; x++) {
        t4 = intArr[x] & 0xFF;
        t3 = (intArr[x] >> 8) & 0xFF;
        t2 = (intArr[x] >> 16) & 0xFF;
        t1 = (intArr[x] >> 24) & 0xFF ^ 0x80;
        c4[t4]++;
        c3[t3]++;
        c2[t2]++;
        c1[t1]++;
    }
    for (x=0; x<256; x++) {
        t4 = o4 + c4[x];
        t3 = o3 + c3[x];
        t2 = o2 + c2[x];
        t1 = o1 + c1[x];
        c4[x] = o4;
        c3[x] = o3;
        c2[x] = o2;
        c1[x] = o1;
        o4 = t4;
        o3 = t3;
        o2 = t2;
        o1 = t1;
    }
    for(x=0; x<intArr.length; x++) {
        t4 = intArr[x] & 0xFF;
        cpy[c4[t4]] = intArr[x];
        c4[t4]++;
    }
    for(x=0; x<intArr.length; x++) {
        t3 = (cpy[x] >> 8) & 0xFF;
        intArr[c3[t3]] = cpy[x];
        c3[t3]++;
    }
    for(x=0; x<intArr.length; x++) {
        t2 = (intArr[x] >> 16) & 0xFF;
        cpy[c2[t2]] = intArr[x];
        c2[t2]++;
    }
    for(x=0; x<intArr.length; x++) {
        t1 = (cpy[x] >> 24) & 0xFF ^ 0x80;
        intArr[c1[t1]] = cpy[x];
        c1[t1]++;
    }
    return intArr;
}

EDIT:

Пока что лучшая/единственная крупная оптимизация выявляется на основе массивов JS. Использование типизированного массива для обычного теневого массива сортировки с радиусом дает наилучшие результаты. Я также смог выжать немного лишнего из быстрого выбора, используя JS, встроенный в стек push/pop.


последний тест jsfiddle

Intel i7 870, 4GB, FireFox 8.0
2mil
radixSort(intArr): 172 ms
radixSortIP(intArr): 1738 ms
quickSortIP(arr): 661 ms
200k
radixSort(intArr): 18 ms
radixSortIP(intArr): 26 ms
quickSortIP(arr): 58 ms

Кажется, что стандартная сортировка radix действительно является королем для этого потока работ. Если у кого-то есть время, чтобы поэкспериментировать с циклом или другими модификациями, я был бы признателен.

У меня есть конкретный прецедент, где мне нужна быстрая реализация сортировки в JavaScript. Будут большие (50 000 - 2 мил), несортированные (по существу случайные), целые (32-разрядные) массивы, которые клиент script будет иметь доступ, тогда ему необходимо отсортировать и представить эти данные.

Я реализовал довольно быстро на месте сортировки radix и на месте быстро сортировал jsfiddle benchmark, но для моей длины массива верхней границы они все еще довольно медленный. Быстрая сортировка лучше работает на моем размере верхней границы массива, в то время как сортировка radix лучше работает на моей нижней границе.

defaultSort is the built-in JavaScript array.sort with an integer compare function

Intel C2Q 9650, 4GB, FireFox 3.6
2mil
radixSortIP(intArr): 5554 ms
quickSortIP(arr): 1796 ms
200k
radixSortIP(intArr): 139 ms
quickSortIP(arr): 190 ms
defaultSort(intArr): 354 ms

Intel i7 870, 4GB, FireFox 8.0
2mil
radixSortIP(intArr): 990 ms
quickSortIP(arr): 882 ms
defaultSort(intArr): 3632 ms
200k
radixSortIP(intArr): 28 ms
quickSortIP(arr): 68 ms
defaultSort(intArr): 306 ms

Вопросы

  • Есть ли более эффективная реализация любого алгоритма сортировки, который бы соответствовал моим потребностям/потребностям?
  • Есть ли какие-либо оптимизации, которые могут быть сделаны для меня на месте внедрения radix/quick sort для повышения производительности?
    • Есть ли эффективный способ преобразования моего места на основе сортировки radix с рекурсивной итеративной функции? Скорость памяти и скорость.

Цель

  • Я надеюсь, что эти ответы помогут мне улучшить производительность на 20-30% в моем тестовом тесте.

Разъяснения /Notes

  • "DEFINE FAST". Я бы предпочел общий случай, когда он хорошо работает во всех современных браузерах, но если есть определенная оптимизация браузера, которая делает значительное улучшение приемлемым.
  • Сортировка COULD будет выполняться на стороне сервера, но я бы предпочел избежать этого, потому что приложение JS может стать автономным (в паре с некоторыми проприетарными приложениями, которые будут передавать данные датчика в файл).
  • JavaScript, возможно, не лучший язык для этого, но это требование.
  • Я уже задал этот вопрос https://stackoverflow.com/info/7111525/fastest-way-to-sort-integer-arrays-in-javascript неправильный ответ был проголосован, и вопрос был закрыт.
  • Я попытался использовать несколько экземпляров окна браузера как временную многопоточность; это не закончилось. Мне будет интересна полезная информация о создании нескольких окон для concurrency.
4b9b3361

Ответ 1

Я тестировал типизированные массивы, версия QSIP, по-видимому, хороша в современных браузерах:

2 000 000 элементов

          QSIP_TYPED | RDXIP_TYPED |  QSIP_STD | RDXIP_STD
----------------------------------------------------------
Chrome  |    300          1000          600        1300
Firefox |    550          1500          800        1600    

http://jsfiddle.net/u8t2a/35/

Поддержка (источник: http://caniuse.com/typedarrays):

 IE 10+   |   FF 4+  |  Chrome 7+  |  Safari 5.1+  |  Opera 11.6+   

Ответ 2

Рассматривали ли вы комбинацию алгоритмов для максимального использования кеша? Я видел в тесте, что вы переключаетесь на сортировку вставки, когда субмарины становятся маленькими. Интересный подход заключается в том, чтобы вместо этого переключиться на heapsort. Используемый вместе с quicksort, он может свести наихудший случай к O (nlog (n)) вместо O (n ^ 2). Посмотрите Introsort.

Ответ 3

Я поиграл с вашим тестом и добавил свою собственную функцию сортировки. Он выполняет то же самое, что и radixsort, но идея (и реализация) проще, она похожа на radixsort, но в двоичном формате, поэтому у вас есть только два ведра и можете сделать это на месте. Посмотрите http://jsfiddle.net/KaRV9/7/.

Я помещаю свой код вместо "Quicksort in place" (поскольку он очень похож на quicksort, только ось вращения выбирается другим способом). Запустите их несколько раз, в моем Chrome 15 они работают так близко, что не могут их отличить.

Ответ 4

Я не буду комментировать алгоритмы сортировки. вы знаете больше об этом, чем мне.

Но хорошая идея - использовать веб-работников. Это позволяет вашему типу работать в фоновом режиме в собственном потоке и, таким образом, не блокировать интерфейс. Это было бы хорошей практикой, несмотря ни на что. Он хорошо поддерживается для Chrome и Firefox. Opera имеет не-поточную версию. Не уверен в поддержке IE, но было бы легко обойти это. Разумеется, накладные расходы связаны с использованием нескольких потоков.

Сортировка слияния может быть легко преобразована в многопоточную версию, что даст вам некоторое повышение производительности. Разумеется, сообщения поступают со временем, так что это будет действительно зависеть от вашей конкретной ситуации, если она будет работать быстрее. Помните, однако, что неблокирующий характер может заставить его чувствовать, что приложение работает быстрее для конечного пользователя.

Ответ 5

EDIT: Я вижу, вы уже используете сортировку вставки для меньших подмассивов. Я пропустил это.

Хороший подход в реальном мире с быстрой сортировкой - это проверить размер подмассива, а если он достаточно короткий, используйте быстрый низкоуровневый сорт, который слишком медленный для больших массивов, таких как сортировка вставки.

Псевдокод - это что-то вроде строк:

quicksort(array, start, end):
  if ( (end-start) < THRESHOLD ) {
    insertion_sort(array, start, end);
    return;
  }
  // regular quicksort here
  // ...

Чтобы определить THRESHOLD, вам нужно время его на интересующие вас платформы, в вашем случае - возможно, в разных браузерах. Измерьте время для случайных массивов с разными пороговыми значениями, чтобы найти близкую к оптимальной. Вы также можете выбрать разные пороговые значения для разных браузеров, если обнаружите существенные различия.

Теперь, если ваши входы на самом деле не случайны (что довольно часто), вы можете увидеть, улучшает ли оптимальный выбор поворота. Общим методом является средний из трех вариантов.